Android ndk 在android上使用ffmpeg将AAC解码到PCM

Android ndk 在android上使用ffmpeg将AAC解码到PCM,android-ndk,ffmpeg,java-native-interface,aac,Android Ndk,Ffmpeg,Java Native Interface,Aac,我在ubuntu上用android NDK(r8c)构建了ffmpeg 0.8.12(love)。 然后,我通过JNI在另一个android应用程序中使用生成的库 基本上,我想做的是将一个字节流从java传递到我的c jni函数,并使用ffmpeg将其解码到PCM音频缓冲区,然后将其传递回java,以便使用Android的AudioTrack播放。我可以成功地将缓冲区传递给jni(已检查值),ffmpeg似乎正确初始化,但当它尝试解码第一帧时,它在aacdec.c中的aac_decode_fra

我在ubuntu上用android NDK(r8c)构建了ffmpeg 0.8.12(love)。 然后,我通过JNI在另一个android应用程序中使用生成的库

基本上,我想做的是将一个字节流从java传递到我的c jni函数,并使用ffmpeg将其解码到PCM音频缓冲区,然后将其传递回java,以便使用Android的AudioTrack播放。我可以成功地将缓冲区传递给jni(已检查值),ffmpeg似乎正确初始化,但当它尝试解码第一帧时,它在aacdec.c中的aac_decode_frame_int方法中抛出错误“未分配通道元素0.0”。aac文件播放良好且有效

下面是我的jni代码来进行解码

jint Java_com_example_testffmpeg_MainActivity_decodeAacBytes(JNIEnv * env,
        jobject this, jbyteArray input, jint numBytes) {

    //copy bytes from java
    jbyte* bufferPtr = (*env)->GetByteArrayElements(env, input, NULL);
    uint8_t inputBytes[numBytes + FF_INPUT_BUFFER_PADDING_SIZE];
    memset(inputBytes, 0, numBytes + FF_INPUT_BUFFER_PADDING_SIZE);
    memcpy(inputBytes, bufferPtr, numBytes);
    (*env)->ReleaseByteArrayElements(env, input, bufferPtr, 0);

    av_register_all();

    AVCodec *codec = avcodec_find_decoder(CODEC_ID_AAC);

    if (codec == NULL) {
        LOGE("Cant find AAC codec\n");
        return 0;
    }
    LOGI("AAC codec found\n");

    AVCodecContext *avCtx = avcodec_alloc_context();

    if (avCtx == NULL) {
        LOGE("Could not allocate codec context\n");
        return 0;
    }
    LOGI("codec context allocated\n");

    if (avcodec_open2(avCtx, codec, NULL) < 0) {
        LOGE("Could not open codec\n");
        return 0;
    }
    LOGI("AAC codec opened");

    //the input buffer
    AVPacket avPacket;
    av_init_packet(&avPacket);

    LOGI("AVPacket initialised\n");

    avPacket.size = numBytes; //input buffer size
    avPacket.data = inputBytes; // the input buffer

    int outSize;
    int len;
    uint8_t *outbuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);

    while (avPacket.size > 0) {
        outSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
        len = avcodec_decode_audio3(avCtx, (short *) outbuf, &outSize,
                &avPacket);

        if (len < 0) {
            LOGE("Error while decoding\n");
            return 0;
        }

        if (outSize > 0) {
            LOGI("Decoded some stuff\n");
        }

        avPacket.size -= len;
        avPacket.data += len;
    }

    LOGI("Freeing memory\n");

    av_free_packet(&avPacket);
    avcodec_close(avCtx);
    av_free(avCtx);

    return 0;
}
jint Java_com_example_testffmpeg_main activity_decodeAcBytes(JNIEnv*env,
jobject this,jbyte数组输入,jint numBytes){
//从java复制字节
jbyte*bufferPtr=(*env)->GetByteArrayElements(env,input,NULL);
uint8_t inputBytes[numBytes+FF_INPUT_BUFFER_PADDING_SIZE];
memset(inputBytes,0,numBytes+FF\u INPUT\u BUFFER\u PADDING\u SIZE);
memcpy(inputBytes、bufferPtr、numBytes);
(*env)->ReleaseByteArrayElements(env,input,bufferPtr,0);
av_寄存器_all();
AVCodec*codec=AVCodec\u find\u解码器(codec\u ID\u AAC);
如果(编解码器==NULL){
LOGE(“找不到AAC编解码器”);
返回0;
}
LOGI(“找到AAC编解码器\n”);
AVCodecContext*avCtx=avcodec_alloc_context();
如果(avCtx==NULL){
LOGE(“无法分配编解码器上下文\n”);
返回0;
}
LOGI(“编解码器上下文已分配\n”);
if(avcodec_open2(avCtx,codec,NULL)<0){
LOGE(“无法打开编解码器\n”);
返回0;
}
LOGI(“AAC编解码器已打开”);
//输入缓冲区
AVPacket-AVPacket;
av_初始_数据包(&av数据包);
LOGI(“已初始化的数据包\n”);
avPacket.size=numBytes;//输入缓冲区大小
avPacket.data=inputBytes;//输入缓冲区
int超大;
内伦;
uint8_t*extuf=malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
而(avPacket.size>0){
超大=AVCODEC\最大\音频\帧\大小;
len=avcodec\U decode\U AUDIO 3(avCtx,(短*)EXBUFF和超大,
&数据包);
if(len<0){
LOGE(“解码时出错\n”);
返回0;
}
如果(超大>0){
LOGI(“解码了一些东西\n”);
}
avPacket.size-=len;
avPacket.data+=len;
}
LOGI(“释放内存”);
av_免费_数据包(&av数据包);
avcodec_close(avCtx);
av_免费(avCtx);
返回0;
}

问题发生在对avcodec_decode_audio3的调用中,即首次进行解码时。我已经仔细阅读了ffmpeg代码,但找不到问题所在。任何帮助都将不胜感激

在调用
avcodec\u open2
之前,必须为
AVCodecContext
设置一些附加设置

我通常设置这些必需的设置(以“k”开头的变量表示预定义的常量):

UPD

很抱歉,我编写了必须为音频编码设置的参数。 对于音频解码,通常足以设置
avCtx->channels
ctx->sample\u rate
或设置
avCtx->extradata
avCtx->extradata\u size

要查找错误原因,请尝试查看ffmpeg输出。如果在设备上很难执行,您可以重定向ffmpeg输出,并通过自己的回调执行日志记录。例如:

    // initialize:
    ::av_log_set_callback(&my_ffmpeg_log);

    // callback
    void my_ffmpeg_log(void *ptr, int level, const char *fmt, va_list vl)
    {
      /// Here you can set a more detailed level
      if (level < AV_LOG_VERBOSE)
      {
        static char message[8192];
        const char *module = NULL;

        if (ptr)
        {
          AVClass *avc = *(AVClass**) ptr;
          if (avc->item_name)
            module = avc->item_name(ptr);
        }
        vsnprintf(message, sizeof message, fmt, vl);
        // you can set own function here, for example LOGI, as you have in your example
        std::cout << "ffmpeg message : " << module << " " << level << " " << message;
      }
    }
//初始化:
::av_log_set_回调(&my_ffmpeg_log);
//回拨
void my_ffmpeg_日志(void*ptr、int-level、const-char*fmt、va_-list-vl)
{
///在这里,您可以设置更详细的级别
if(级别项目名称)
模块=avc->项目名称(ptr);
}
vsnprintf(消息、消息大小、fmt、vl);
//您可以在这里设置自己的函数,例如LOGI,正如您在示例中所做的那样

std::cout在调用
avcodec\u open2
之前,必须为
AVCodecContext
设置一些附加设置

我通常设置这些必需的设置(以“k”开头的变量表示预定义的常量):

UPD

很抱歉,我编写了必须为音频编码设置的参数。 对于音频解码,通常足以设置
avCtx->channels
ctx->sample\u rate
或设置
avCtx->extradata
avCtx->extradata\u size

要查找错误原因,请尝试查看ffmpeg输出。如果在设备上很难执行此操作,您可以重定向ffmpeg输出并通过自己的回调执行日志记录。示例:

    // initialize:
    ::av_log_set_callback(&my_ffmpeg_log);

    // callback
    void my_ffmpeg_log(void *ptr, int level, const char *fmt, va_list vl)
    {
      /// Here you can set a more detailed level
      if (level < AV_LOG_VERBOSE)
      {
        static char message[8192];
        const char *module = NULL;

        if (ptr)
        {
          AVClass *avc = *(AVClass**) ptr;
          if (avc->item_name)
            module = avc->item_name(ptr);
        }
        vsnprintf(message, sizeof message, fmt, vl);
        // you can set own function here, for example LOGI, as you have in your example
        std::cout << "ffmpeg message : " << module << " " << level << " " << message;
      }
    }
//初始化:
::av_log_set_回调(&my_ffmpeg_log);
//回拨
void my_ffmpeg_日志(void*ptr、int-level、const-char*fmt、va_-list-vl)
{
///在这里,您可以设置更详细的级别
if(级别项目名称)
模块=avc->项目名称(ptr);
}
vsnprintf(消息、消息大小、fmt、vl);
//您可以在这里设置自己的函数,例如LOGI,正如您在示例中所做的那样

std::你不能尝试在主机上解码该文件吗?它是由ffmpeg解码的吗?SoX呢,它可以做到这一点,还可以正常化、淡入/淡出、更改音量等?我没有尝试在控制台中使用ffmpeg解码该文件-我试图从我的应用程序代码中解码,所以这对我没有任何帮助。文件does在我手机上的任何播放器中都能正确播放,所以它不会损坏。我的意思是用NDK编译SoX,请看。Hhmm很有趣。我将继续