我可以使用以下代码播放Wav文件而不会出现问题。当试图以Mp3格式播放完全相同的媒体时,我只会得到乱码垃圾。我相信我从根本上误解了avcodec_decode_audio3函数的工作原理。
由于Wav文件在解码时包含PCM数据,因此可以直接进入AudioTrack.write函数。必须有一些额外的步骤才能使Mp3像这样工作。我不知道我错过了什么,但我已经把头发拉了一个星期了。
Java代码
package com.rohaupt.RRD2;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.SystemClock;
public class player extends Activity
{
private AudioTrack track;
private FileOutputStream os;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
createEngine();
MediaPlayer mp = new MediaPlayer();
mp.start();
int bufSize = AudioTrack.getMinBufferSize(32000,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.STREAM_MUSIC,
32000,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
bufSize,
AudioTrack.MODE_STREAM);
byte[] bytes = new byte[bufSize];
try {
os = new FileOutputStream("/sdcard/a.out",false);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String result = loadFile("/sdcard/a.mp3",bytes);
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void playSound(byte[] buf, int size) {
//android.util.Log.v("ROHAUPT", "RAH Playing");
if(track.getPlayState()!=AudioTrack.PLAYSTATE_PLAYING)
track.play();
track.write(buf, 0, size);
try {
os.write(buf,0,size);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private native void createEngine();
private native String loadFile(String file, byte[] array);
/** Load jni .so on initialization*/
static {
System.loadLibrary("avutil");
System.loadLibrary("avcore");
System.loadLibrary("avcodec");
System.loadLibrary("avformat");
System.loadLibrary("avdevice");
System.loadLibrary("swscale");
System.loadLibrary("avfilter");
System.loadLibrary("ffmpeg");
}
}
C代码
#include <assert.h>
#include <jni.h>
#include <string.h>
#include <android/log.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#define DEBUG_TAG "ROHAUPT"
void Java_com_rohaupt_RRD2_player_createEngine(JNIEnv* env, jclass clazz)
{
avcodec_init();
av_register_all();
}
jstring Java_com_rohaupt_RRD2_player_loadFile(JNIEnv* env, jobject obj,jstring file,jbyteArray array)
{
jboolean isCopy;
int i;
int audioStream=-1;
int res;
int decoded = 0;
int out_size;
AVFormatContext *pFormatCtx;
AVCodecContext *aCodecCtx;
AVCodecContext *c= NULL;
AVCodec *aCodec;
AVPacket packet;
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID play = (*env)->GetMethodID(env, cls, "playSound", "([BI)V");//At the begining of your main function
const char * szfile = (*env)->GetStringUTFChars(env, file, &isCopy);
int16_t * pAudioBuffer = (int16_t *) av_malloc (AVCODEC_MAX_AUDIO_FRAME_SIZE*2+FF_INPUT_BUFFER_PADDING_SIZE);
int16_t * outBuffer = (int16_t *) av_malloc (AVCODEC_MAX_AUDIO_FRAME_SIZE*2+FF_INPUT_BUFFER_PADDING_SIZE);
__android_log_print(ANDROID_LOG_INFO, DEBUG_TAG, "RAH28 Starting");
res = av_open_input_file(&pFormatCtx, szfile, NULL, 0, NULL);
if(res!=0)
{
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH opening input failed with result: [%d]", res);
return file;
}
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH getting stream info");
res = av_find_stream_info(pFormatCtx);
if(res<0)
{
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH getting stream info failed with result: [%d]", res);
return file;
}
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH getting audio stream");
for(i=0; i < pFormatCtx->nb_streams; i++) {
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
audioStream < 0) {
audioStream=i;
}
}
if(audioStream==-1)
{
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH couldn't find audio stream");
return file;
}
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio stream found with result: [%d]", res);
aCodecCtx=pFormatCtx->streams[audioStream]->codec;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio codec info loaded");
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio codec info [%d]", aCodecCtx->codec_id);
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if(!aCodec) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio codec unsupported");
return file;
}
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio codec info found");
res = avcodec_open(aCodecCtx, aCodec);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio codec loaded [%d] [%d]",aCodecCtx->sample_fmt,res);
//c=avcodec_alloc_context();
av_init_packet(&packet);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH channels [%d] sample rate [%d] sample format [%d]",aCodecCtx->channels,aCodecCtx->sample_rate,aCodecCtx->sample_fmt);
int x,y;
x=0;y=0;
while (av_read_frame(pFormatCtx, &packet)>= 0) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH frame read: [%d] [%d]",x++,y);
if (aCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH audio ready");
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*2+FF_INPUT_BUFFER_PADDING_SIZE;
int size=packet.size;
y=0;
decoded = 0;
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH packet size: [%d]", size);
while(size > 0) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH decoding: [%d] [%d]",x,y++);
int len = avcodec_decode_audio3(aCodecCtx, pAudioBuffer, &data_size, &packet);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH 1 size [%d] len [%d] data_size [%d] out_size [%d]",size,len,data_size,out_size);
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
memcpy(bytes + decoded, pAudioBuffer, len); //
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH 2");
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH 3");
(*env)->CallVoidMethod(env, obj, play, array, len);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH 4");
size -= len;
decoded += len;
}
av_free_packet(&packet);
}
}
// Close the video file
av_close_input_file(pFormatCtx);
//__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "RAH Finished Running result: [%d]", res);
(*env)->ReleaseStringUTFChars(env, file, szfile);
return file;
}
添加一些细节。使用Wav文件调用此函数时,我得到以下日志数据
I/ROHAUPT ( 227): RAH28 Starting
D/ROHAUPT ( 227): RAH getting stream info
D/ROHAUPT ( 227): RAH getting audio stream
D/ROHAUPT ( 227): RAH audio stream found with result: [0]
D/ROHAUPT ( 227): RAH audio codec info loaded
D/ROHAUPT ( 227): RAH audio codec info [65536]
D/ROHAUPT ( 227): RAH audio codec info found
D/ROHAUPT ( 227): RAH audio codec loaded [1] [0]
D/ROHAUPT ( 227): RAH channels [2] sample rate [32000] sample format [1]
D/ROHAUPT ( 227): RAH frame read: [0] [0]
D/ROHAUPT ( 227): RAH audio ready
D/ROHAUPT ( 227): RAH packet size: [4096]
D/ROHAUPT ( 227): RAH decoding: [1] [0]
D/ROHAUPT ( 227): RAH 1 size [4096] len [4096] data_size [4096] out_size [0]
D/ROHAUPT ( 227): RAH 2
D/ROHAUPT ( 227): RAH 3
D/ROHAUPT ( 227): RAH 4
D/ROHAUPT ( 227): RAH frame read: [1] [1]
D/ROHAUPT ( 227): RAH audio ready
...
D/ROHAUPT ( 227): RAH frame read: [924] [1]
D/ROHAUPT ( 227): RAH audio ready
D/ROHAUPT ( 227): RAH packet size: [4096]
D/ROHAUPT ( 227): RAH decoding: [925] [0]
D/ROHAUPT ( 227): RAH 1 size [4096] len [4096] data_size [4096] out_size [0]
D/ROHAUPT ( 227): RAH 2
D/ROHAUPT ( 227): RAH 3
D/ROHAUPT ( 227): RAH 4
D/ROHAUPT ( 227): RAH frame read: [925] [1]
D/ROHAUPT ( 227): RAH audio ready
D/ROHAUPT ( 227): RAH packet size: [3584]
D/ROHAUPT ( 227): RAH decoding: [926] [0]
D/ROHAUPT ( 227): RAH 1 size [3584] len [3584] data_size [3584] out_size [0]
D/ROHAUPT ( 227): RAH 2
D/ROHAUPT ( 227): RAH 3
D/ROHAUPT ( 227): RAH 4
使用Mp3文件调用时,我得到以下内容
I/ROHAUPT ( 280): RAH28 Starting
D/ROHAUPT ( 280): RAH getting stream info
D/ROHAUPT ( 280): RAH getting audio stream
D/ROHAUPT ( 280): RAH audio stream found with result: [0]
D/ROHAUPT ( 280): RAH audio codec info loaded
D/ROHAUPT ( 280): RAH audio codec info [86017]
D/ROHAUPT ( 280): RAH audio codec info found
D/ROHAUPT ( 280): RAH audio codec loaded [1] [0]
D/ROHAUPT ( 280): RAH channels [2] sample rate [32000] sample format [1]
D/ROHAUPT ( 280): RAH frame read: [0] [0]
D/ROHAUPT ( 280): RAH audio ready
D/ROHAUPT ( 280): RAH packet size: [432]
D/ROHAUPT ( 280): RAH decoding: [1] [0]
D/ROHAUPT ( 280): RAH 1 size [432] len [432] data_size [4608] out_size [0]
D/ROHAUPT ( 280): RAH 2
...
D/ROHAUPT ( 280): RAH frame read: [822] [1]
D/ROHAUPT ( 280): RAH audio ready
D/ROHAUPT ( 280): RAH packet size: [432]
D/ROHAUPT ( 280): RAH decoding: [823] [0]
D/ROHAUPT ( 280): RAH 1 size [432] len [432] data_size [4608] out_size [0]
D/ROHAUPT ( 280): RAH 2
D/ROHAUPT ( 280): RAH 3
D/ROHAUPT ( 280): RAH 4
D/ROHAUPT ( 280): RAH frame read: [823] [1]
D/ROHAUPT ( 280): RAH audio ready
D/ROHAUPT ( 280): RAH packet size: [432]
D/ROHAUPT ( 280): RAH decoding: [824] [0]
D/ROHAUPT ( 280): RAH 1 size [432] len [432] data_size [4608] out_size [0]
D/ROHAUPT ( 280): RAH 2
D/ROHAUPT ( 280): RAH 3
D/ROHAUPT ( 280): RAH 4
答案 0 :(得分:8)
我能够通过获取ffmpeg源的libavcodec中的api_example.c文件中的audio_decode_example代码来解决我的问题,并根据我的需要进行修改。下面是代码。需要注意的是,它没有动态选择正确的编解码器进行解码,这是我接下来要解决的问题以及一些其他项目。 java代码保持不变。
void Java_com_rohaupt_RRD2_player_Example(JNIEnv* env, jobject obj,jstring file,jbyteArray array)
{
jboolean isfilenameCopy;
const char * filename = (*env)->GetStringUTFChars(env, file, &isfilenameCopy);
AVCodec *codec;
AVCodecContext *c= NULL;
int out_size, len;
FILE *f, *outfile;
uint8_t *outbuf;
uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
AVPacket avpkt;
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID play = (*env)->GetMethodID(env, cls, "playSound", "([BI)V");//At the begining of your main function
av_init_packet(&avpkt);
printf("Audio decoding\n");
/* find the mpeg audio decoder */
codec = avcodec_find_decoder(CODEC_ID_MP3);
if (!codec) {
fprintf(stderr, "codec not found\n");
exit(1);
}
c= avcodec_alloc_context();
/* open it */
if (avcodec_open(c, codec) < 0) {
fprintf(stderr, "could not open codec\n");
exit(1);
}
outbuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "could not open %s\n", filename);
exit(1);
}
/* decode until eof */
avpkt.data = inbuf;
avpkt.size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
while (avpkt.size > 0) {
out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
len = avcodec_decode_audio3(c, (short *)outbuf, &out_size, &avpkt);
if (len < 0) {
fprintf(stderr, "Error while decoding\n");
exit(1);
}
if (out_size > 0) {
/* if a frame has been decoded, output it */
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
memcpy(bytes, outbuf, out_size); //
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
(*env)->CallVoidMethod(env, obj, play, array, out_size);
}
avpkt.size -= len;
avpkt.data += len;
if (avpkt.size < AUDIO_REFILL_THRESH) {
/* Refill the input buffer, to avoid trying to decode
* incomplete frames. Instead of this, one could also use
* a parser, or use a proper container format through
* libavformat. */
memmove(inbuf, avpkt.data, avpkt.size);
avpkt.data = inbuf;
len = fread(avpkt.data + avpkt.size, 1,
AUDIO_INBUF_SIZE - avpkt.size, f);
if (len > 0)
avpkt.size += len;
}
}
fclose(f);
free(outbuf);
avcodec_close(c);
av_free(c);
}
答案 1 :(得分:0)
啊,您可能需要在调用data_size
后检查avcodec_decode_audio3
值:现在您总是将返回值提供给play
方法,但是data_size
}小于0然后它不是有效的帧解码。