[FFmpeg-user] avformat_open_input with custom AVIOContext (duration incorrect)

tmason at trueprobity.com tmason at trueprobity.com
Thu Feb 6 23:11:10 EET 2020


I am trying to use a custom AVIOContext to open a wav audio file buffer and
read the duration along with other audio characteristics.  The example code
posted blow shows 2 methods of opening the same file.  In the case where I
call avformat_open_input with the audio.wav file saved on disk, I
consistently get the correct duration.  When I call avformat_open_input with
the audio.wav file saved as a file buffer using a custom AVIOContext, I am
not able to get a reasonable value for duration.  

 

>From the API documentation, I have tried 3 methods I can find for
determining duration without luck. I have tried many different test files to
verify the issue and the duration is either zero or a very large negative
number.  Are there any other methods of getting the duration of an audio
stream or am I missing a step before trying to get the duration? 

m_audioInfo->duration = static_cast<double>(pFormatContext->duration) /
AV_TIME_BASE;
m_audioInfo->duration = (pStream->duration /
(1/av_q2d(pStream->time_base)));
m_audioInfo->duration = pFormatContext->duration;

 

Any help you can provide would be greatly appreciated. Thank you!  Below is
the code that I am using:

 

#include "AsyncInspectionWorker.h"

using namespace v8;
using namespace std;


const size_t EMPTY = 0;

/* User defined data holder that will be passed to avio_alloc_context() */
struct buffer_in_data {
   uint8_t* ptr;
   size_t size;
};

/* Function for reading the user defined buffer_in_data structure passed to
avio_alloc_context() */
static int read_packet(void* opaque, uint8_t* buf, int buf_size) {
   struct buffer_in_data* bd = (struct buffer_in_data*)opaque;
   buf_size = FFMIN(buf_size, bd->size);

   memcpy(buf, bd->ptr, buf_size);
   bd->ptr += buf_size;
   bd->size -= buf_size;

   return buf_size;
}

AsyncInspectionWorker::AsyncInspectionWorker(string filename, Nan::Callback
*callback)
 : Nan::AsyncWorker(callback) {
    m_filename = filename;
   m_buffer_in_size = EMPTY;
}

AsyncInspectionWorker::AsyncInspectionWorker(char* buffer, size_t
bufferSize, Nan::Callback* callback)
 : Nan::AsyncWorker(callback) {
   m_buffer_in = buffer;
   m_buffer_in_size = bufferSize;
}

void AsyncInspectionWorker::Execute() {
    AVCodec* pCodec;
    AVStream* pStream = NULL;
   AVIOContext* avio_ctx = NULL;
    AVCodecContext* pCodecContext;
    AVCodecContext* pCodecContextOrig;
    AVFormatContext* pFormatContext = avformat_alloc_context();
   struct buffer_in_data bd = { 0, 0};

   if (m_buffer_in_size != EMPTY) {
      // This Buffer reading code based on ffmpeg project's
doc/examples/avio_reading.c 
      uint8_t* avio_ctx_buffer = NULL;
      size_t avio_ctx_buffer_size = 4096;

      bd.ptr = reinterpret_cast<uint8_t*>(m_buffer_in);
      bd.size = m_buffer_in_size;

      if (!(avio_ctx_buffer =
static_cast<uint8_t*>(av_malloc(avio_ctx_buffer_size)))) {
         SetErrorMessage("Unable to allocate enough memory for processing");
         return;
      }

      if (!(avio_ctx = avio_alloc_context(avio_ctx_buffer,
avio_ctx_buffer_size, 0, &bd, &read_packet, NULL, NULL))) {
         SetErrorMessage("Unable to allocate enough memory for processing");
         return;
      }

      pFormatContext->pb = avio_ctx;
      
      if (avformat_open_input(&pFormatContext, NULL, NULL, NULL) < 0) {
         SetErrorMessage("Unable to read Buffer data");
         return;
      }
   } else {
      if (avformat_open_input(&pFormatContext, m_filename.c_str(), NULL,
NULL) != 0) {
         SetErrorMessage("Unable to open file");
         return;
      }
   }    

    if (avformat_find_stream_info(pFormatContext, NULL) != 0) {
        SetErrorMessage("Unable to find stream info");
        return;
    }

    for (unsigned int i = 0; i < pFormatContext->nb_streams; i++)
    {
        if (pFormatContext->streams[i]->codec->codec_type ==
AVMEDIA_TYPE_AUDIO)
        {
            pStream = pFormatContext->streams[i];
            break;
        }
    }
    if (pStream == NULL) {
        SetErrorMessage("Unable to find audio stream");
        return;
    }

    pCodecContextOrig = pStream->codec;
    pCodec = avcodec_find_decoder(pCodecContextOrig->codec_id);
    if (pCodec == NULL) {
        SetErrorMessage("Unsupported codec");
        return;
    }

    pCodecContext = avcodec_alloc_context3(pCodec);
    if (avcodec_copy_context(pCodecContext, pCodecContextOrig) != 0) {
        SetErrorMessage("Unable to copy codec context");
        return;
    }
    avcodec_open2(pCodecContext, pCodec, NULL);

    m_audioInfo = new AudioInfo();
    //m_audioInfo->duration = static_cast<double>(pFormatContext->duration)
/ AV_TIME_BASE;
    //m_audioInfo->duration = (pStream->duration /
(1/av_q2d(pStream->time_base)));
    m_audioInfo->duration = pFormatContext->duration;
    m_audioInfo->sample_rate = pCodecContext->sample_rate;
    m_audioInfo->channels = pCodecContext->channels;
   
    avcodec_close(pCodecContext);
   avcodec_close(pCodecContextOrig);
    avformat_close_input(&pFormatContext);
   pFormatContext = NULL;
   
   if (avio_ctx && avio_ctx != NULL) {
      av_freep(&avio_ctx->buffer);
      avio_context_free(&avio_ctx);
   }
}

void AsyncInspectionWorker::HandleOKCallback() {
    Nan::HandleScope scope;
    const int argc = 1;
   Local<Object> audioInfo = Nan::New<Object>();

    if(m_audioInfo != NULL) {
        Nan::Set(audioInfo, Nan::New<String>("duration").ToLocalChecked(),
Nan::New<Number>(m_audioInfo->duration));
        Nan::Set(audioInfo, Nan::New<String>("sampleRate").ToLocalChecked(),
Nan::New<Number>(m_audioInfo->sample_rate));
        Nan::Set(audioInfo, Nan::New<String>("channels").ToLocalChecked(),
Nan::New<Number>(m_audioInfo->channels));
        //This inspection reports how the data will look after transcoding,
everything is converted to 16 bit (2 bytes)
        Nan::Set(audioInfo, Nan::New<String>("bitRate").ToLocalChecked(),
Nan::New<Number>(16));
        Nan::Set(audioInfo, Nan::New<String>("sampleSize").ToLocalChecked(),
Nan::New<Number>(2 * m_audioInfo->channels));

        delete m_audioInfo;
    }

    Local<Value> argv[argc] = {audioInfo};
    callback->Call(argc, argv);
}

void AsyncInspectionWorker::HandleErrorCallback() {
    Nan::HandleScope scope;
    const int argc = 1;
   Local<Object> returnObject = Nan::New<Object>();

    Nan::Set(returnObject, Nan::New<String>("error").ToLocalChecked(),
Nan::New<v8::String>(ErrorMessage()).ToLocalChecked());

    Local<Value> argv[argc] = {returnObject};
    callback->Call(argc, argv);
}

 



More information about the ffmpeg-user mailing list