Android多媒体开发(4)————移植Libmad到android平台
/********************************************************************************************
* author:conowen@大钟
* E-mail:conowen@hotmail.com
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
众所周知,Android的audiotrack只能播放原始的音频,也就是PCM数据,若是播放mp3编码格式的音频的话,就是出现沙沙的噪音。所以,可以使用第三方库Libmad来对mp3文件解码称为PCM数据,再送给audiotrack播放即可。
1、Libmad简介
Libmad是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。
2、实现过程
2.1、下载Android平台下的Libmad工程
http://gitorious.org/rowboat/external-libmad
或者直接执行
- mkdir libmad
- cd libmad
- git clone git://gitorious.org/rowboat/external-libmad.git
mkdir libmad
cd libmad
git clone git://gitorious.org/rowboat/external-libmad.git
下载的project可以直接用NDK编译通过的,但是要使用还是要写jni层供java层调用,关于NDK开发,可以参看我之前的博文。
但是Android.mk要改成如下形式。
- #ifeq ($(strip $(BUILD_WITH_GST)),true)
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_SRC_FILES:= \
- version.c \
- fixed.c \
- bit.c \
- timer.c \
- stream.c \
- frame.c \
- synth.c \
- decoder.c \
- layer12.c \
- layer3.c \
- huffman.c
- LOCAL_ARM_MODE := arm
- LOCAL_MODULE:= libmad
- LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/android
- LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3
- include $(BUILD_SHARED_LIBRARY)
- #endif
#ifeq ($(strip $(BUILD_WITH_GST)),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
version.c \
fixed.c \
bit.c \
timer.c \
stream.c \
frame.c \
synth.c \
decoder.c \
layer12.c \
layer3.c \
huffman.c
LOCAL_ARM_MODE := arm
LOCAL_MODULE:= libmad
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/android
LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3
include $(BUILD_SHARED_LIBRARY)
#endif
2.2、C代码编写API
需要注意一点的是,得到音频的Samplerate(采样率)要先进行一次readSamples操作才能发采样率读出。具体看代码即可。
- #define TAG "NativeMP3Decoder"
- #include "FileSystem.h"
- #include "Mad.h"
- #include "NativeMP3Decoder.h"
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <android/log.h>
- #define SHRT_MAX (32767)
- #define INPUT_BUFFER_SIZE (8192/4)
- #define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */
- //int g_size;
- extern int file_open(const char *filename, int flags);
- extern int file_read(T_pFILE fd, unsigned char *buf, int size);
- extern int file_write(T_pFILE fd, unsigned char *buf, int size);
- extern int64_t file_seek(T_pFILE fd, int64_t pos, int whence);
- extern int file_close(T_pFILE fd);
- /**
- * Struct holding the pointer to a wave file.
- */
- typedef struct
- {
- int size;
- int64_t fileStartPos;
- T_pFILE file;
- struct mad_stream stream;
- struct mad_frame frame;
- struct mad_synth synth;
- mad_timer_t timer;
- int leftSamples;
- int offset;
- unsigned char inputBuffer[INPUT_BUFFER_SIZE];
- } MP3FileHandle;
- /** static WaveFileHandle array **/
- static inline int readNextFrame( MP3FileHandle* mp3 );
- static MP3FileHandle* Handle;
- unsigned int g_Samplerate;
- /**
- * Seeks a free handle in the handles array and returns its index or -1 if no handle could be found
- */
- static inline void closeHandle()
- {
- file_close( Handle->file);
- mad_synth_finish(&Handle->synth);
- mad_frame_finish(&Handle->frame);
- mad_stream_finish(&Handle->stream);
- free(Handle);
- Handle = NULL;
- }
- static inline signed short fixedToShort(mad_fixed_t Fixed)
- {
- if(Fixed>=MAD_F_ONE)
- return(SHRT_MAX);
- if(Fixed<=-MAD_F_ONE)
- return(-SHRT_MAX);
- Fixed=Fixed>>(MAD_F_FRACBITS-15);
- return((signed short)Fixed);
- }
- int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/)
- {
- __android_log_print(ANDROID_LOG_INFO, TAG, "start = %d*******\n",start);
- // __android_log_print(ANDROID_LOG_INFO, TAG, "size = %d*******\n",size);
- //g_size=size;
- T_pFILE fileHandle = file_open( filepath, _FMODE_READ);
- if( fileHandle == 0 )
- return -1;
- MP3FileHandle* mp3Handle = (MP3FileHandle*)malloc(sizeof(MP3FileHandle));
- memset(mp3Handle, 0, sizeof(MP3FileHandle));
- mp3Handle->file = fileHandle;
- // mp3Handle->size = size;
- mp3Handle->fileStartPos= start;
- file_seek( mp3Handle->file, start, SEEK_SET);
- mad_stream_init(&mp3Handle->stream);
- mad_frame_init(&mp3Handle->frame);
- mad_synth_init(&mp3Handle->synth);
- mad_timer_reset(&mp3Handle->timer);
- Handle = mp3Handle;
- readNextFrame( Handle );
- g_Samplerate = Handle->frame.header.samplerate;
- return 1;
- }
- static inline int readNextFrame( MP3FileHandle* mp3 )
- {
- do
- {
- if( mp3->stream.buffer == 0 || mp3->stream.error == MAD_ERROR_BUFLEN )
- {
- int inputBufferSize = 0;
- if( mp3->stream.next_frame != 0 )
- {
- int leftOver = mp3->stream.bufend - mp3->stream.next_frame;
- int i;
- for( i= 0; i < leftOver; i++ )
- mp3->inputBuffer[i] = mp3->stream.next_frame[i];
- int readBytes = file_read( mp3->file, mp3->inputBuffer + leftOver, INPUT_BUFFER_SIZE - leftOver);
- if( readBytes == 0 )
- return 0;
- inputBufferSize = leftOver + readBytes;
- }
- else
- {
- int readBytes = file_read( mp3->file, mp3->inputBuffer, INPUT_BUFFER_SIZE);
- if( readBytes == 0 )
- return 0;
- inputBufferSize = readBytes;
- }
- mad_stream_buffer( &mp3->stream, mp3->inputBuffer, inputBufferSize );
- mp3->stream.error = MAD_ERROR_NONE;
- }
- if( mad_frame_decode( &mp3->frame, &mp3->stream ) )
- {
- if( mp3->stream.error == MAD_ERROR_BUFLEN ||(MAD_RECOVERABLE(mp3->stream.error)))
- continue;
- else
- return 0;
- }
- else
- break;
- }
- while( 1 );
- mad_timer_add( &mp3->timer, mp3->frame.header.duration );
- mad_synth_frame( &mp3->synth, &mp3->frame );
- mp3->leftSamples = mp3->synth.pcm.length;
- mp3->offset = 0;
- return -1;
- }
- int NativeMP3Decoder_readSamples(short *target, int size)
- {
- MP3FileHandle* mp3 = Handle;
- //short* target = (short*)env->GetDirectBufferAddress(buffer);
- int pos=0;
- int idx = 0;
- while( idx != size )
- {
- if( mp3->leftSamples > 0 )
- {
- for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ )
- {
- int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]);
- if( MAD_NCHANNELS(&mp3->frame.header) == 2 )
- {
- value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]);
- value /= 2;
- }
- target[idx++] = value;
- }
- }
- else
- {
- pos = file_seek( mp3->file, 0, SEEK_CUR);
- int result = readNextFrame( mp3);
- if( result == 0 )
- return 0;
- }
- }
- /* if(pos > mp3->fileStartPos + mp3->size)
- {
- __android_log_print(ANDROID_LOG_INFO, TAG, "*****return -1**\n");
- return -1;
- }
- */
- if( idx > size )
- return 0;
- //return size;
- return pos;
- }
- int NativeMP3Decoder_getAduioSamplerate()
- {
- return g_Samplerate;
- }
- void NativeMP3Decoder_closeAduioFile()
- {
- if( Handle != 0 )
- {
- closeHandle();
- Handle = 0;
- }
- }
#define TAG "NativeMP3Decoder"
#include "FileSystem.h"
#include "Mad.h"
#include "NativeMP3Decoder.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <android/log.h>
#define SHRT_MAX (32767)
#define INPUT_BUFFER_SIZE (8192/4)
#define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */
//int g_size;
extern int file_open(const char *filename, int flags);
extern int file_read(T_pFILE fd, unsigned char *buf, int size);
extern int file_write(T_pFILE fd, unsigned char *buf, int size);
extern int64_t file_seek(T_pFILE fd, int64_t pos, int whence);
extern int file_close(T_pFILE fd);
/**
* Struct holding the pointer to a wave file.
*/
typedef struct
{
int size;
int64_t fileStartPos;
T_pFILE file;
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
mad_timer_t timer;
int leftSamples;
int offset;
unsigned char inputBuffer[INPUT_BUFFER_SIZE];
} MP3FileHandle;
/** static WaveFileHandle array **/
static inline int readNextFrame( MP3FileHandle* mp3 );
static MP3FileHandle* Handle;
unsigned int g_Samplerate;
/**
* Seeks a free handle in the handles array and returns its index or -1 if no handle could be found
*/
static inline void closeHandle()
{
file_close( Handle->file);
mad_synth_finish(&Handle->synth);
mad_frame_finish(&Handle->frame);
mad_stream_finish(&Handle->stream);
free(Handle);
Handle = NULL;
}
static inline signed short fixedToShort(mad_fixed_t Fixed)
{
if(Fixed>=MAD_F_ONE)
return(SHRT_MAX);
if(Fixed<=-MAD_F_ONE)
return(-SHRT_MAX);
Fixed=Fixed>>(MAD_F_FRACBITS-15);
return((signed short)Fixed);
}
int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/)
{
__android_log_print(ANDROID_LOG_INFO, TAG, "start = %d*******\n",start);
// __android_log_print(ANDROID_LOG_INFO, TAG, "size = %d*******\n",size);
//g_size=size;
T_pFILE fileHandle = file_open( filepath, _FMODE_READ);
if( fileHandle == 0 )
return -1;
MP3FileHandle* mp3Handle = (MP3FileHandle*)malloc(sizeof(MP3FileHandle));
memset(mp3Handle, 0, sizeof(MP3FileHandle));
mp3Handle->file = fileHandle;
// mp3Handle->size = size;
mp3Handle->fileStartPos= start;
file_seek( mp3Handle->file, start, SEEK_SET);
mad_stream_init(&mp3Handle->stream);
mad_frame_init(&mp3Handle->frame);
mad_synth_init(&mp3Handle->synth);
mad_timer_reset(&mp3Handle->timer);
Handle = mp3Handle;
readNextFrame( Handle );
g_Samplerate = Handle->frame.header.samplerate;
return 1;
}
static inline int readNextFrame( MP3FileHandle* mp3 )
{
do
{
if( mp3->stream.buffer == 0 || mp3->stream.error == MAD_ERROR_BUFLEN )
{
int inputBufferSize = 0;
if( mp3->stream.next_frame != 0 )
{
int leftOver = mp3->stream.bufend - mp3->stream.next_frame;
int i;
for( i= 0; i < leftOver; i++ )
mp3->inputBuffer[i] = mp3->stream.next_frame[i];
int readBytes = file_read( mp3->file, mp3->inputBuffer + leftOver, INPUT_BUFFER_SIZE - leftOver);
if( readBytes == 0 )
return 0;
inputBufferSize = leftOver + readBytes;
}
else
{
int readBytes = file_read( mp3->file, mp3->inputBuffer, INPUT_BUFFER_SIZE);
if( readBytes == 0 )
return 0;
inputBufferSize = readBytes;
}
mad_stream_buffer( &mp3->stream, mp3->inputBuffer, inputBufferSize );
mp3->stream.error = MAD_ERROR_NONE;
}
if( mad_frame_decode( &mp3->frame, &mp3->stream ) )
{
if( mp3->stream.error == MAD_ERROR_BUFLEN ||(MAD_RECOVERABLE(mp3->stream.error)))
continue;
else
return 0;
}
else
break;
}
while( 1 );
mad_timer_add( &mp3->timer, mp3->frame.header.duration );
mad_synth_frame( &mp3->synth, &mp3->frame );
mp3->leftSamples = mp3->synth.pcm.length;
mp3->offset = 0;
return -1;
}
int NativeMP3Decoder_readSamples(short *target, int size)
{
MP3FileHandle* mp3 = Handle;
//short* target = (short*)env->GetDirectBufferAddress(buffer);
int pos=0;
int idx = 0;
while( idx != size )
{
if( mp3->leftSamples > 0 )
{
for( ; idx < size && mp3->offset < mp3->synth.pcm.length; mp3->leftSamples--, mp3->offset++ )
{
int value = fixedToShort(mp3->synth.pcm.samples[0][mp3->offset]);
if( MAD_NCHANNELS(&mp3->frame.header) == 2 )
{
value += fixedToShort(mp3->synth.pcm.samples[1][mp3->offset]);
value /= 2;
}
target[idx++] = value;
}
}
else
{
pos = file_seek( mp3->file, 0, SEEK_CUR);
int result = readNextFrame( mp3);
if( result == 0 )
return 0;
}
}
/* if(pos > mp3->fileStartPos + mp3->size)
{
__android_log_print(ANDROID_LOG_INFO, TAG, "*****return -1**\n");
return -1;
}
*/
if( idx > size )
return 0;
//return size;
return pos;
}
int NativeMP3Decoder_getAduioSamplerate()
{
return g_Samplerate;
}
void NativeMP3Decoder_closeAduioFile()
{
if( Handle != 0 )
{
closeHandle();
Handle = 0;
}
}
头文件声明
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- #include <stdlib.h>
- int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/);
- int NativeMP3Decoder_readSamples(short *target, int size);
- void NativeMP3Decoder_closeAduioFile();
- int NativeMP3Decoder_getAduioSamplerate();
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdlib.h>
int NativeMP3Decoder_init(char * filepath,unsigned long start/*,unsigned long size*/);
int NativeMP3Decoder_readSamples(short *target, int size);
void NativeMP3Decoder_closeAduioFile();
int NativeMP3Decoder_getAduioSamplerate();
2.2、编写JNI调用代码,往上提供接口
- #define TAG "native_libmad"
- #include "FileSystem.h"
- #include <stdlib.h>
- #include <jni.h>
- #include <android/log.h>
- extern int NativeMP3Decoder_readSamples( short *target, int size);
- extern void NativeMP3Decoder_closeAduioFile();
- extern int NativeMP3Decoder_getAduioSamplerate();
- extern int NativeMP3Decoder_init(char * filepath,unsigned long start);
- jint Java_com_conowen_libmad_NativeMP3Decoder_initAudioPlayer(JNIEnv *env, jobject obj, jstring file,jint startAddr)
- {
- char* fileString = (*env)->GetStringUTFChars(env,file, NULL);
- return NativeMP3Decoder_init(fileString,startAddr);
- }
- jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioBuf(JNIEnv *env, jobject obj ,jshortArray audioBuf,jint len)
- {
- int bufsize = 0;
- int ret = 0;
- if (audioBuf != NULL) {
- bufsize = (*env)->GetArrayLength(env, audioBuf);
- jshort *_buf = (*env)->GetShortArrayElements(env, audioBuf, 0);
- memset(_buf, 0, bufsize*2);
- ret = NativeMP3Decoder_readSamples(_buf, len);
- (*env)->ReleaseShortArrayElements(env, audioBuf, _buf, 0);
- }
- else{
- __android_log_print(ANDROID_LOG_DEBUG, TAG, "getAudio failed");
- }
- return ret;
- }
- jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioSamplerate()
- {
- return NativeMP3Decoder_getAduioSamplerate();
- }
- void Java_com_conowen_libmad_NativeMP3Decoder_closeAduioFile( )
- {
- NativeMP3Decoder_closeAduioFile();
- }
#define TAG "native_libmad"
#include "FileSystem.h"
#include <stdlib.h>
#include <jni.h>
#include <android/log.h>
extern int NativeMP3Decoder_readSamples( short *target, int size);
extern void NativeMP3Decoder_closeAduioFile();
extern int NativeMP3Decoder_getAduioSamplerate();
extern int NativeMP3Decoder_init(char * filepath,unsigned long start);
jint Java_com_conowen_libmad_NativeMP3Decoder_initAudioPlayer(JNIEnv *env, jobject obj, jstring file,jint startAddr)
{
char* fileString = (*env)->GetStringUTFChars(env,file, NULL);
return NativeMP3Decoder_init(fileString,startAddr);
}
jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioBuf(JNIEnv *env, jobject obj ,jshortArray audioBuf,jint len)
{
int bufsize = 0;
int ret = 0;
if (audioBuf != NULL) {
bufsize = (*env)->GetArrayLength(env, audioBuf);
jshort *_buf = (*env)->GetShortArrayElements(env, audioBuf, 0);
memset(_buf, 0, bufsize*2);
ret = NativeMP3Decoder_readSamples(_buf, len);
(*env)->ReleaseShortArrayElements(env, audioBuf, _buf, 0);
}
else{
__android_log_print(ANDROID_LOG_DEBUG, TAG, "getAudio failed");
}
return ret;
}
jint Java_com_conowen_libmad_NativeMP3Decoder_getAudioSamplerate()
{
return NativeMP3Decoder_getAduioSamplerate();
}
void Java_com_conowen_libmad_NativeMP3Decoder_closeAduioFile( )
{
NativeMP3Decoder_closeAduioFile();
}
2.3、文件读写操作
- #include <unistd.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include"FileSystem.h"
- int file_open(const char *filename, int flags)
- {
- int access;
- T_pFILE fd = 0;
- if (flags == _CREATE) {
- access = O_CREAT | O_TRUNC | O_RDWR;
- } else if (flags == _WRONLY) {
- access = O_CREAT | O_TRUNC | O_WRONLY;
- } else if (flags == _RDONLY){
- access = O_RDONLY;
- } else if (flags == _RDWR){
- access = O_RDWR;
- } else{
- return -1;
- }
- #ifdef O_BINARY
- access |= O_BINARY;
- #endif
- fd = open(filename, access, 0666);
- if (fd == -1)
- return -1;
- return fd;
- }
- int file_read(T_pFILE fd, unsigned char *buf, int size)
- {
- return read(fd, buf, size);
- }
- int file_write(T_pFILE fd, unsigned char *buf, int size)
- {
- return write(fd, buf, size);
- }
- int64_t file_seek(T_pFILE fd, int64_t pos, int whence)
- {
- if (whence == 0x10000) {
- struct stat st;
- int ret = fstat(fd, &st);
- return ret < 0 ? -1 : st.st_size;
- }
- return lseek(fd, pos, whence);
- }
- int file_close(T_pFILE fd)
- {
- return close(fd);
- }
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include"FileSystem.h"
int file_open(const char *filename, int flags)
{
int access;
T_pFILE fd = 0;
if (flags == _CREATE) {
access = O_CREAT | O_TRUNC | O_RDWR;
} else if (flags == _WRONLY) {
access = O_CREAT | O_TRUNC | O_WRONLY;
} else if (flags == _RDONLY){
access = O_RDONLY;
} else if (flags == _RDWR){
access = O_RDWR;
} else{
return -1;
}
#ifdef O_BINARY
access |= O_BINARY;
#endif
fd = open(filename, access, 0666);
if (fd == -1)
return -1;
return fd;
}
int file_read(T_pFILE fd, unsigned char *buf, int size)
{
return read(fd, buf, size);
}
int file_write(T_pFILE fd, unsigned char *buf, int size)
{
return write(fd, buf, size);
}
int64_t file_seek(T_pFILE fd, int64_t pos, int whence)
{
if (whence == 0x10000) {
struct stat st;
int ret = fstat(fd, &st);
return ret < 0 ? -1 : st.st_size;
}
return lseek(fd, pos, whence);
}
int file_close(T_pFILE fd)
{
return close(fd);
}
头文件
- #include <stdlib.h>
- typedef signed long T_S32; /* signed 32 bit integer */
- #define T_pFILE T_S32
- #define T_hFILE T_S32
- #define _CREATE 0//"wb+"//0
- #define _RDONLY 1//"rb" // 1
- #define _WRONLY 2//"wb"// 2
- #define _RDWR 3//"rb+"// 3
- #define _FMODE_READ _RDONLY
- #define _FMODE_WRITE _WRONLY
- #define _FMODE_CREATE _CREATE
- #define _FMODE_OVERLAY _RDWR
- #define _FSEEK_CURRENT 1
- #define _FSEEK_END 2
- #define _FSEEK_SET 0
- #define _FOPEN_FAIL -1
- #define SEEK_CURRENT 1
- #define SEEK_CUR 1
- #define SEEK_END 2
- #define SEEK_SET 0
- #define FS_SEEK_SET 0
#include <stdlib.h>
typedef signed long T_S32; /* signed 32 bit integer */
#define T_pFILE T_S32
#define T_hFILE T_S32
#define _CREATE 0//"wb+"//0
#define _RDONLY 1//"rb" // 1
#define _WRONLY 2//"wb"// 2
#define _RDWR 3//"rb+"// 3
#define _FMODE_READ _RDONLY
#define _FMODE_WRITE _WRONLY
#define _FMODE_CREATE _CREATE
#define _FMODE_OVERLAY _RDWR
#define _FSEEK_CURRENT 1
#define _FSEEK_END 2
#define _FSEEK_SET 0
#define _FOPEN_FAIL -1
#define SEEK_CURRENT 1
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0
#define FS_SEEK_SET 0
把上述两个c文件,加入Android文件编译即可生成。90K左右