avformat_open_input with custom AVIOContext (duration incorrect)
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); }
On Thu, Feb 6, 2020, at 12:11 PM, tmason@trueprobity.com wrote:
I am trying to use a custom AVIOContext...
This question belongs on the libav-user mailing list. ffmpeg-user is for questions about using the command-line tools.
participants (2)
-
Lou Logan -
tmason@trueprobity.com