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


下载的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

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;
 }
}






头文件声明

/* 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();

}


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 <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左右

下一篇博文介绍如何使用这个生成的so库。

见此

http://blog.csdn.net/conowen/article/details/7799155

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值