Android多媒体开发(4)————移植Libmad到android平台
Android多媒体开发(4)————移植Libmad到android平台
http://blog.csdn.net/conowen/article/details/7727145 分类: 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
或者直接执行
[java] view plain copy print ?- mkdirlibmad
- cdlibmad
- gitclonegit://gitorious.org/rowboat/external-libmad.git
mkdir libmadcd libmadgit clone git://gitorious.org/rowboat/external-libmad.git
下载的project可以直接用NDK编译通过的,但是要使用还是要写jni层供java层调用,关于NDK开发,可以参看我之前的博文。
但是Android.mk要改成如下形式。
[java] view plain copy print ?- #ifeq($(strip$(BUILD_WITH_GST)),true)
- LOCAL_PATH:=$(callmy-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.cLOCAL_ARM_MODE := armLOCAL_MODULE:= libmadLOCAL_C_INCLUDES := \ $(LOCAL_PATH)/android LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3include $(BUILD_SHARED_LIBRARY)#endif
2.2、C代码编写API
需要注意一点的是,得到音频的Samplerate(采样率)要先进行一次readSamples操作才能发采样率读出。具体看代码即可。
- #defineTAG"NativeMP3Decoder"
- #include"FileSystem.h"
- #include"Mad.h"
- #include"NativeMP3Decoder.h"
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #include<android/log.h>
- #defineSHRT_MAX(32767)
- #defineINPUT_BUFFER_SIZE(8192/4)
- #defineOUTPUT_BUFFER_SIZE8192/*Mustbeanintegermultipleof4.*/
- //intg_size;
- externintfile_open(constchar*filename,intflags);
- externintfile_read(T_pFILEfd,unsignedchar*buf,intsize);
- externintfile_write(T_pFILEfd,unsignedchar*buf,intsize);
- externint64_tfile_seek(T_pFILEfd,int64_tpos,intwhence);
- externintfile_close(T_pFILEfd);
- /**
- *Structholdingthepointertoawavefile.
- */
- typedefstruct
- {
- intsize;
- int64_tfileStartPos;
- T_pFILEfile;
- structmad_streamstream;
- structmad_frameframe;
- structmad_synthsynth;
- mad_timer_ttimer;
- intleftSamples;
- intoffset;
- unsignedcharinputBuffer[INPUT_BUFFER_SIZE];
- }MP3FileHandle;
- /**staticWaveFileHandlearray**/
- staticinlineintreadNextFrame(MP3FileHandle*mp3);
- staticMP3FileHandle*Handle;
- unsignedintg_Samplerate;
- /**
- *Seeksafreehandleinthehandlesarrayandreturnsitsindexor-1ifnohandlecouldbefound
- */
- staticinlinevoidcloseHandle()
- {
- file_close(Handle->file);
- mad_synth_finish(&Handle->synth);
- mad_frame_finish(&Handle->frame);
- mad_stream_finish(&Handle->stream);
- free(Handle);
- Handle=NULL;
- }
- staticinlinesignedshortfixedToShort(mad_fixed_tFixed)
- {
- if(Fixed>=MAD_F_ONE)
- return(SHRT_MAX);
- if(Fixed<=-MAD_F_ONE)
- return(-SHRT_MAX);
- Fixed=Fixed>>(MAD_F_FRACBITS-15);
- return((signedshort)Fixed);
- }
- intNativeMP3Decoder_init(char*filepath,unsignedlongstart/*,unsignedlongsize*/)
- {
- __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_pFILEfileHandle=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;
- return1;
- }
- staticinlineintreadNextFrame(MP3FileHandle*mp3)
- {
- do
- {
- if(mp3->stream.buffer==0||mp3->stream.error==MAD_ERROR_BUFLEN)
- {
- intinputBufferSize=0;
- if(mp3->stream.next_frame!=0)
- {
- intleftOver=mp3->stream.bufend-mp3->stream.next_frame;
- inti;
- for(i=0;i<leftOver;i++)
- mp3->inputBuffer[i]=mp3->stream.next_frame[i];
- intreadBytes=file_read(mp3->file,mp3->inputBuffer+leftOver,INPUT_BUFFER_SIZE-leftOver);
- if(readBytes==0)
- return0;
- inputBufferSize=leftOver+readBytes;
- }
- else
- {
- intreadBytes=file_read(mp3->file,mp3->inputBuffer,INPUT_BUFFER_SIZE);
- if(readBytes==0)
- return0;
- 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
- return0;
- }
- 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;
- }
- intNativeMP3Decoder_readSamples(short*target,intsize)
- {
- MP3FileHandle*mp3=Handle;
- //short*target=(short*)env->GetDirectBufferAddress(buffer);
- intpos=0;
- intidx=0;
- while(idx!=size)
- {
- if(mp3->leftSamples>0)
- {
- for(;idx<size&&mp3->offset<mp3->synth.pcm.length;mp3->leftSamples--,mp3->offset++)
- {
- intvalue=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);
- intresult=readNextFrame(mp3);
- if(result==0)
- return0;
- }
- }
- /*if(pos>mp3->fileStartPos+mp3->size)
- {
- __android_log_print(ANDROID_LOG_INFO,TAG,"*****return-1**\n");
- return-1;
- }
- */
- if(idx>size)
- return0;
- //returnsize;
- returnpos;
- }
- intNativeMP3Decoder_getAduioSamplerate()
- {
- returng_Samplerate;
- }
- voidNativeMP3Decoder_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; }}
头文件声明 [cpp] view plain copy print ?
- /*DONOTEDITTHISFILE-itismachinegenerated*/
- #include<jni.h>
- #include<stdlib.h>
- intNativeMP3Decoder_init(char*filepath,unsignedlongstart/*,unsignedlongsize*/);
- intNativeMP3Decoder_readSamples(short*target,intsize);
- voidNativeMP3Decoder_closeAduioFile();
- intNativeMP3Decoder_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调用代码,往上提供接口 [cpp] view plain copy print ?
- #defineTAG"native_libmad"
- #include"FileSystem.h"
- #include<stdlib.h>
- #include<jni.h>
- #include<android/log.h>
- externintNativeMP3Decoder_readSamples(short*target,intsize);
- externvoidNativeMP3Decoder_closeAduioFile();
- externintNativeMP3Decoder_getAduioSamplerate();
- externintNativeMP3Decoder_init(char*filepath,unsignedlongstart);
- jintJava_com_conowen_libmad_NativeMP3Decoder_initAudioPlayer(JNIEnv*env,jobjectobj,jstringfile,jintstartAddr)
- {
- char*fileString=(*env)->GetStringUTFChars(env,file,NULL);
- returnNativeMP3Decoder_init(fileString,startAddr);
- }
- jintJava_com_conowen_libmad_NativeMP3Decoder_getAudioBuf(JNIEnv*env,jobjectobj,jshortArrayaudioBuf,jintlen)
- {
- intbufsize=0;
- intret=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,"getAudiofailed");
- }
- returnret;
- }
- jintJava_com_conowen_libmad_NativeMP3Decoder_getAudioSamplerate()
- {
- returnNativeMP3Decoder_getAduioSamplerate();
- }
- voidJava_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"
- intfile_open(constchar*filename,intflags)
- {
- intaccess;
- T_pFILEfd=0;
- if(flags==_CREATE){
- access=O_CREAT|O_TRUNC|O_RDWR;
- }elseif(flags==_WRONLY){
- access=O_CREAT|O_TRUNC|O_WRONLY;
- }elseif(flags==_RDONLY){
- access=O_RDONLY;
- }elseif(flags==_RDWR){
- access=O_RDWR;
- }else{
- return-1;
- }
- #ifdefO_BINARY
- access|=O_BINARY;
- #endif
- fd=open(filename,access,0666);
- if(fd==-1)
- return-1;
- returnfd;
- }
- intfile_read(T_pFILEfd,unsignedchar*buf,intsize)
- {
- returnread(fd,buf,size);
- }
- intfile_write(T_pFILEfd,unsignedchar*buf,intsize)
- {
- returnwrite(fd,buf,size);
- }
- int64_tfile_seek(T_pFILEfd,int64_tpos,intwhence)
- {
- if(whence==0x10000){
- structstatst;
- intret=fstat(fd,&st);
- returnret<0?-1:st.st_size;
- }
- returnlseek(fd,pos,whence);
- }
- intfile_close(T_pFILEfd)
- {
- returnclose(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);}
头文件
[java] view plain copy print ?- #include<stdlib.h>
- typedefsignedlongT_S32;/*signed32bitinteger*/
- #defineT_pFILET_S32
- #defineT_hFILET_S32
- #define_CREATE0//"wb+"//0
- #define_RDONLY1//"rb"//1
- #define_WRONLY2//"wb"//2
- #define_RDWR3//"rb+"//3
- #define_FMODE_READ_RDONLY
- #define_FMODE_WRITE_WRONLY
- #define_FMODE_CREATE_CREATE
- #define_FMODE_OVERLAY_RDWR
- #define_FSEEK_CURRENT1
- #define_FSEEK_END2
- #define_FSEEK_SET0
- #define_FOPEN_FAIL-1
- #defineSEEK_CURRENT1
- #defineSEEK_CUR1
- #defineSEEK_END2
- #defineSEEK_SET0
- #defineFS_SEEK_SET0
#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左右
更多相关文章
- Android(安卓)studio3.0 com.android.tools.aapt2.Aapt2Exceptio
- Android之访问下载文件
- 【android开发】android操作文件
- ubuntu 不是 识别 android 设备 解决方法
- 短视频带货源码模仿QQ 测滑菜单
- Android文件图片上传的详细讲解(三)---模式回调类
- Android中设置全屏的方法
- react-native apk打包 android
- NPM 和webpack 的基础使用