如何在Android中通过FFmpeg解码音频

如何在Android中通过FFmpeg解码音频,android,audio,ffmpeg,decode,Android,Audio,Ffmpeg,Decode,我正在为Android编写一个播放器,其中FFmpeg是为Android NDK编译的 我可以通过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,

我正在为Android编写一个播放器,其中FFmpeg是为Android NDK编译的

我可以通过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);
在这种情况下,当avcodec_decode_audio2我得到-1。我做错了什么

注: 当我驾驶int data_size=417时;然后DEBUG不会出现,函数返回-1,但当我驱动一个:int data\u size=AVCODEC\u MAX\u AUDIO\u FRAME\u 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;
}

使用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;
                            }
            }
 }
JNIEXPORT int JNICALL Java\u com\u ffmpeg\u Main\u jnimanentry(JNIEnv*env、jobject obj、jstring输入){
const char*pszFileName=(*env)->GetStringUTFChars(env,输入,0);
AVFormatContext*m_fc;
INTERR;
AVPacket-pkt;
char*pAudioBuffer=(char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE*2);
int i;
avcodec_寄存器_all();
avdevice_register_all();
av_寄存器_all();
err=av_open_input_文件(&m_fc,pszFileName,0,0);
err=av_查找_流_信息(m_fc);
对于(i=0;inb_流;i++){
如果((m_fc->streams[i]->codec->codec_type==AVMEDIA_type_VIDEO)|(m_fc->streams[i]->codec->codec_type==AVMEDIA_type_VIDEO)){
AVCodec*codec=AVCodec\u find\u解码器(m\u fc->streams[i]->codec->codec\u id);
如果(编解码器==0)
继续;
err=avcodec\u open(m\u fc->streams[i]->codec,codec);
如果(错误=0){
如果(m_fc->streams[pkt.stream\u index]->codec->codec\u type==AVMEDIA\u type\u AUDIO){
int data_size=AVCODEC_MAX_AUDIO_FRAME_size*2;
int size=pkt->size;
而(大小>0){
int len=avcodec\u decode\u audio3(m_fc->streams[pkt.stream\u index]->codec,(int16\u t*)pAudioBuffer和数据大小和pkt);
LOGD(“数据大小%d长度%d”,数据大小,长度);
尺寸=包装->尺寸长度;
}
}
}

}

我无法发表评论,因此我将此作为回答

我也在尝试使用ffmpeg播放aac音频,但是对于如何让它工作有很多问题

我尝试使用作为参考,但代码的作者没有使用avcodec_decode_audio3进行解码。通过AudioTrack播放时会出现很多口吃,我认为解码器填充缓冲区的速度不够快,无法播放AudioTrack

我还有别的办法吗


再一次,很抱歉将此作为答案发布。

我更新了问题。请再次查看。有一个问题。我编辑了上面的代码,并且在我的droid上进行了尝试。avcodec\u decode\u audio3始终返回它使用的字节,并且始终为正值。我对上述代码没有任何问题。请尝试更改mallocs到av_malloc。有时也可以。您的代码我还没有编译(没有找到函数avdevice_register_all),但我升级了我的代码,现在返回了一些必要的内容。问题是:我收到的缓冲区怎么办?我明白-应该通过Java程序传输。但是如何传输?我希望不难:)P.s.我可以通过电子邮件与您联系吗?因此,如果不是太难,它可能会在其他地方有所帮助。顺便说一下,它是:while(true)使我:03-22 17:54:24.916:WARN/ActivityManager(59):启动超时已过期,放弃唤醒锁!03-22 17:54:26.387:WARN/ActivityManager(1959):历史记录的活动空闲超时{44f04510 org.libsdl.app/.SDLActivity}怎么做?这是一个非常简单的JNI问题。通过一些JNI基础知识,更新缓冲区非常容易。只需将字节数组传递给C代码,然后使用setbytearrayregion函数进行更新或将字节数组返回给JAVA。在什么设备上测试它?在emulator上速度非常慢。我在droid上尝试了同样的方法,效果很好。如果您需要,请告诉我e没有在emulator上尝试。我已经在Droid和HTC Hero上测试了这一点。两者的播放都是一样的。你能解释一下为什么在memcpy中使用“size”而不是len吗?据我所知,我们从“pAudioBuffer”复制到长度为“size”的“bytes”。但是size是avpacket的大小,而pAudioBuffer解码了长度为“len”的数据.是这样吗?(我不是C专业人士-正在努力学习)
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;
                            }
            }
 }