2

I have built ffmpeg 0.8.12 (love) with the android NDK (r8c) on ubuntu. I then use the generated library in another android application through JNI.

Essentially what I want to do is pass a byte stream from java to my c jni function and use ffmpeg to decode it into a PCM audio buffer which will then be passed back to java to be played using Android's AudioTrack. I can successfully pass the buffer through to jni (have checked the values) and ffmpeg seems to initialise correctly, but when it tries to decode the first frame, it throws an error in the aac_decode_frame_int method in aacdec.c "channel element 0.0 is not allocated". The aac file plays fine and is valid.

Here is my jni code to do the decoding

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

The problem occurs in the call to avcodec_decode_audio3, when the decoding first occurs. I have stepped through the ffmpeg code, but can't find the problem. Any help would be greatly appreciated!

JeffG
  • 3,312
  • 1
  • 26
  • 34
  • Have you tried to decode the file on the host machine? Is it decoded by ffmpeg? What about SoX, it is capable on doing that, also normalize, fade-in/out, change volume, etc? – rraallvv Nov 21 '12 at 18:26
  • I have not tried decoding the file by using ffmpeg in the console - I am trying to decode from within my application code, so this wouldn't help me anyway. The file does play correctly in any player on my phone, so it is not corrupt. – JeffG Nov 26 '12 at 17:14
  • 1
    I mean to compile SoX with NDK, please see [this link](http://stackoverflow.com/questions/8582765/android-ndk-linking-problems). – rraallvv Nov 26 '12 at 23:51
  • Hhmm interesting. I'm going to keep trying with ffmpeg, but if I can't get it to work, i'll give SoX a go. Thanks! – JeffG Nov 27 '12 at 11:36

1 Answers1

4

You must set some additional settings for AVCodecContext before you call avcodec_open2.

I usually set these required settings (variables beginning with 'k' mean predefined constatns):

avCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
avCtx->codec_type = AVMEDIA_TYPE_AUDIO;
avCtx->channels = kChannelsCount; // for example, 2
avCtx->sample_fmt = kSampleFmt; // AV_SAMPLE_FMT_S16
avCtx->sample_rate = kSampleRate; // 44100
avCtx->channel_layout = kSampleLayout; // 3
const AVRational timeBase = {1, avCtx->sample_rate};
avCtx->time_base = timeBase;

UPD

I'm sorry, I wrote the parameters that must be set for audio encoding. For audio decoding usually sufficient to set avCtx->channels, ctx->sample_rate or set avCtx->extrdata and avCtx->extradata_size.

To find the cause of the error, try to see the ffmpeg output. If on the device is difficult to do, you can redirect the ffmpeg output and perform logging by own callback. Example:

    // 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;
      }
    }
pogorskiy
  • 4,705
  • 1
  • 22
  • 21
  • Thanks for the info. I tried this, but I get the same error :( Do you have any other suggestions? – JeffG Nov 26 '12 at 17:12
  • Thanks for the code to get the logs out - the only log is an error "channel element 0.0 not allocated". Unfortunately setting the attribs you mentioned doesn't help. The aac I'm decoding is in a m4a container (which shouldn't matter). The ffmpeg docs say that extradata can contain the global headers, but they can also be in the bytestream, which they are :( – JeffG Nov 27 '12 at 13:14
  • For my test mp4 file with aac aduio sufficient to set either sample_rate/channels or extradata/extradata_sizedata. Error "channel element 1.0 is not allocated" occurs if I don't set channels. – pogorskiy Nov 27 '12 at 14:02
  • Maybe I haven't built ffmpeg for android with the correct flags to handle mp4 files? I have set sample rate (44100) and channels (2) but the errors says channel 0.0 not allocated so it seems like it is having getting the channels out of the mp4 container. I'm going to try convert the file to have an AAC container and see if the code works. – JeffG Nov 27 '12 at 15:42
  • use avCtx->channel_layout = AV_CH_LAYOUT_STEREO – Matt Wolfe Aug 17 '15 at 08:31