如何在 Android 中通过 FFmpeg 解码音频
Posted
技术标签:
【中文标题】如何在 Android 中通过 FFmpeg 解码音频【英文标题】:How to decode audio via FFmpeg in Android 【发布时间】:2011-07-19 01:32:22 【问题描述】:我正在使用为 android NDK 编译的 FFmpeg 编写一个 Android 播放器。
我可以通过 FFmpeg 打开文件并写下:
av_register_all();
char* str = (*env) -> GetStringUTFChars(env, argv, 0);
__android_log_print(ANDROID_LOG_INFO, "HelloNDK!", str, str);
if (av_open_input_file (&pFormatCtx, str, NULL, 0, NULL) != 0)
return -2; // Couldn't open file
// Retrieve stream information
if (av_find_stream_info(pFormatCtx) < 0)
return -3; // Couldn't find stream information
// Dump information about file onto standard error
dump_format(pFormatCtx, 0, argv, 0);
// Find the first video stream
videoStream =- 1;
audiostream =- 1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO && audioStream <0)
audioStream = i;
if (audioStream == -1)
return -5;
aCodecCtx = pFormatCtx->streams[audioStream]->codec;
// Set audio settings from codec info
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if (!aCodec)
fprintf (stderr, "Unsupported codec! \n");
return -1;
avcodec_open (aCodecCtx, aCodec);
我现在如何解码音频并将其返回给 Java 程序?也许有人可以给我一个例子
编辑: 使用 avcodec_decode_audio3 时 获取以下内容: 03-22 07:54:00.988: 信息/调试 (31): * ** * ** * ** * * * ** * ** * 03-22 07:54:00.988: INFO / DEBUG (31): 构建指纹: 'generic / sdk / generic /: 2.2/FRF91/43546: eng / test-keys' 03-22 07:54:00.988: 信息/调试 (31): pid: 435, tid: 435>>> org.libsdl.app
我可以使用 avcodec_decode_audio2 吗? 我最近几天达到了以下代码:
AVFormatContext * pFormatCtx;
int i, videoStream, audioStream;
AVCodecContext * pCodecCtx;
AVCodec * pCodec;
AVFrame * pFrame;
AVPacket packet;
int frameFinished;
float aspect_ratio;
AVCodecContext * aCodecCtx;
AVCodec * aCodec;
AVCodecContext * c = NULL;
int out_size, len;
int16_t * audio_buf;
uint8_t * outbuf;
uint8_t inbuf [AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
av_register_all ();
char * str = (* env) -> GetStringUTFChars (env, argv, 0);
if (av_open_input_file (& pFormatCtx, str, NULL, 0, NULL)! = 0)
return -150;
if (av_find_stream_info (pFormatCtx) nb_streams; i + +)
if (pFormatCtx-> streams [i] -> codec-> codec_type == CODEC_TYPE_VIDEO & & videoStream streams [i] -> codec-> codec_type == CODEC_TYPE_AUDIO & & audioStream streams [audioStream] -> codec;
aCodec = avcodec_find_decoder (aCodecCtx-> codec_id);
if (! aCodec)
fprintf (stderr, "Unsupported codec! \ n");
return -45;
avcodec_open (aCodecCtx, aCodec);
c = avcodec_alloc_context ();
packet_queue_init (& audioq);
while (av_read_frame (pFormatCtx, & packet)> = 0)
if (packet.stream_index == videoStream)
Else if (packet.stream_index == audioStream)
packet_queue_put (& audioq, & packet);
int len1, data_size;
data_size = 417;
len1 = avcodec_decode_audio2 (aCodecCtx, (int16_t *) audio_buf, & data_size,
packet.data, packet.size);
return packet.size;
Else
av_free_packet (& packet);
return 0;
在这种情况下,当 avcodec_decode_audio2 我得到-1。我做错了什么?
注意: 当我开车时 int data_size = 417;然后DEBUG没有出现并且函数返回-1,但是当我驱动a时:int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2;然后 vyskakievaet 在你的函数中调试,所以在我自己的函数中!怎么解决?
编辑2: 我的新代码:
JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_main( JNIEnv* env, jobject obj, int argc, jstring argv, jbyteArray array)
AVFormatContext *pFormatCtx;
int i, videoStream, audioStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVPacket packet;
int frameFinished;
float aspect_ratio;
AVCodecContext *aCodecCtx;
AVCodec *aCodec;
SDL_Overlay *bmp;
SDL_Surface *screen;
SDL_Rect rect;
SDL_Event event;
SDL_AudioSpec wanted_spec, spec;
AVCodecContext *c= NULL;
int out_size, len;
int16_t *audio_buf;
uint8_t *outbuf;
uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
char *pAudioBuffer = (char *) av_malloc (AVCODEC_MAX_AUDIO_FRAME_SIZE * 2);
av_register_all();
char *str = (*env)->GetStringUTFChars(env, argv, 0);
if(av_open_input_file(&pFormatCtx, str, NULL, 0, NULL)!=0)
return -150; // Couldn't open file
if(av_find_stream_info(pFormatCtx)nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO &&
videoStream streams[i]->codec->codec_type==CODEC_TYPE_AUDIO &&
audioStream streams[audioStream]->codec;
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if(!aCodec)
fprintf(stderr, "Unsupported codec!\n");
return -45;
avcodec_open(aCodecCtx, aCodec);
c=avcodec_alloc_context();
packet_queue_init(&audioq);
while (av_read_frame(pFormatCtx, &packet)>= 0)
if (aCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO)
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2;
int size=packet.size;
while(size > 0)
int len = avcodec_decode_audio3(aCodecCtx, (int16_t *) pAudioBuffer, &data_size, &packet);
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
memcpy(bytes, (int16_t *) pAudioBuffer, size);
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
size = packet.size-len;
return 5;
【问题讨论】:
您能解释一下为什么在 memcpy 中使用“size”而不是 len 吗?据我了解,我们从“pAudioBuffer”复制到长度为“size”的“bytes”。但是 size 是 avpacket 的大小,而 pAudioBuffer 已经解码数据,其长度为“len”。那是对的吗? (我不是 C 专业的 - 正在努力学习) 【参考方案1】:使用 audiotrack 类为您完成工作。你可以这样做。
JAVA 方面。
AudioTrack track;
int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM);
track.play();
while(true)
readBufferFromNative(); //update buffer from native code
....................
....................
在本机端:您需要先读取帧并将其转换为原始 pcm 格式,然后开始连续填充音频缓冲区。缓冲区满时会自动播放。
JNIEXPORT int JNICALL Java_com_ffmpeg_Main_jniMainEntry(JNIEnv* env, jobject obj, jstring input)
const char * pszFileName = (*env)->GetStringUTFChars(env, input, 0);
AVFormatContext * m_fc;
int err;
AVPacket pkt;
char * pAudioBuffer = (char *) av_malloc (AVCODEC_MAX_AUDIO_FRAME_SIZE * 2);
int i;
avcodec_register_all ();
avdevice_register_all ();
av_register_all ();
err = av_open_input_file(&m_fc, pszFileName, 0, 0, 0);
err = av_find_stream_info(m_fc);
for(i = 0; i<m_fc->nb_streams; i++)
if((m_fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) || (m_fc->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO))
AVCodec *codec = avcodec_find_decoder(m_fc->streams[i]->codec->codec_id);
if (codec == 0)
continue;
err = avcodec_open(m_fc->streams[i]->codec, codec);
if (err <0)
continue;
while (av_read_frame(m_fc, &pkt)>= 0)
if (m_fc-> streams[pkt.stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2;
int size=pkt->size;
while(size > 0)
int len = avcodec_decode_audio3(m_fc->streams[pkt.stream_index]->codec, (int16_t *) pAudioBuffer, &data_size, &pkt);
LOGD("data_size %d len %d", data_size, len);
size = pkt->size-len;
【讨论】:
我更新了问题。请再看一遍。有问题。 我已经编辑了上面的代码,并且我已经在我的机器人上尝试过,avcodec_decode_audio3 总是返回它使用的字节并且总是一个正值。我对上面的代码没有任何问题。尝试将 malloc 更改为 av_malloc。有时会这样做。 你的代码我没有编译(没有找到函数avdevice_register_all),但是我升级了我的代码,现在返回了一些必要的东西。问题是:如何处理我收到的缓冲区?我明白了——应该是转Java程序。但是怎么做?我希望这不难:)我可以通过电子邮件与您联系吗?因此,如果不是太难,它可能会在其他地方有所帮助。 顺便说一句:while (true) make me: 03-22 17:54:24.916: WARN / ActivityManager (59): Launch timeout has expired,放弃唤醒锁! 03-22 17:54:26.387: WARN / ActivityManager (1959): HistoryRecord 44f04510 org.libsdl.app / .SDLActivity 的活动空闲超时怎么办? 这是一个非常简单的 JNI 问题。通过一些 JNI 基础知识,更新缓冲区非常容易。只需将字节数组传递给 C 代码并使用 setbytearrayregion 函数对其进行更新或将字节数组返回给 JAVA。【参考方案2】:我无法发表评论,所以将其发布为答案。
我也在尝试使用 ffmpeg 播放 aac 音频,但对于如何让它工作有很多问题。
我尝试使用http://code.google.com/p/aacplayer-android/作为参考,但是代码的作者没有使用avcodec_decode_audio3进行解码。通过 AudioTrack 的播放非常卡顿,我认为解码器填充缓冲区的速度不够快,无法播放音轨。
有没有其他方法可以做到这一点?
再次抱歉,将其发布为答案。
【讨论】:
您在什么设备上测试它?在模拟器上速度太慢了。我在机器人上也试过了,很好。如果您不尝试使用模拟器,请告诉我。 我已经在 Droid 和 HTC Hero 上测试过了。两者的播放是相同的。以上是关于如何在 Android 中通过 FFmpeg 解码音频的主要内容,如果未能解决你的问题,请参考以下文章
使用 Audiotrack 在 Android 中播放 javacv-ffmpeg 解码的音频
如何在Android用FFmpeg+SDL2.0解码显示图像
一文读懂 Android FFmpeg 视频解码过程与实战分析