Skip to content

Commit

Permalink
Fix compatibility with FFmpeg 5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
traversaro committed Mar 20, 2022
1 parent 7299152 commit 96c8538
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 17 deletions.
121 changes: 115 additions & 6 deletions gazebo/common/AudioDecoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ void AudioDecoder::Cleanup()
#ifdef HAVE_FFMPEG
bool AudioDecoder::Decode(uint8_t **_outBuffer, unsigned int *_outBufferSize)
{
AVPacket packet, packet1;
#if LIBAVFORMAT_VERSION_MAJOR < 59
AVPacket *packet, packet1;
int bytesDecoded = 0;
#else
AVPacket *packet;
#endif
unsigned int maxBufferSize = 0;
AVFrame *decodedFrame = nullptr;

Expand Down Expand Up @@ -97,11 +101,63 @@ bool AudioDecoder::Decode(uint8_t **_outBuffer, unsigned int *_outBufferSize)
else
common::AVFrameUnref(decodedFrame);

av_init_packet(&packet);
while (av_read_frame(this->formatCtx, &packet) == 0)
packet = av_packet_alloc();
if (!packet)
{
gzerr << "Failed to allocate AVPacket" << std::endl;
return false;
}
while (av_read_frame(this->formatCtx, packet) == 0)
{
if (packet.stream_index == this->audioStream)
if (packet->stream_index == this->audioStream)
{
#if LIBAVFORMAT_VERSION_MAJOR >= 59
// Inspired from
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/doc/examples/decode_audio.c#L71

// send the packet with the compressed data to the decoder
int ret = avcodec_send_packet(this->codecCtx, packet);
if (ret < 0)
{
gzerr << "Error submitting the packet to the decoder" << std::endl;
return false;
}

// read all the output frames
// (in general there may be any number of them)
while (ret >= 0)
{
ret = avcodec_receive_frame(this->codecCtx, decodedFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
break;
}
else if (ret < 0)
{
gzerr << "Error during decoding" << std::endl;
return false;
}

// Total size of the data. Some padding can be added to
// decodedFrame->data[0], which is why we can't use
// decodedFrame->linesize[0].
int size = decodedFrame->nb_samples *
av_get_bytes_per_sample(this->codecCtx->sample_fmt) *
this->codecCtx->channels;

// Resize the audio buffer as necessary
if (*_outBufferSize + size > maxBufferSize)
{
maxBufferSize += size * 5;
*_outBuffer = reinterpret_cast<uint8_t*>(realloc(*_outBuffer,
maxBufferSize * sizeof(*_outBuffer[0])));
}

memcpy(*_outBuffer + *_outBufferSize, decodedFrame->data[0],
size);
*_outBufferSize += size;
}
#else
int gotFrame = 0;

packet1 = packet;
Expand Down Expand Up @@ -144,11 +200,12 @@ bool AudioDecoder::Decode(uint8_t **_outBuffer, unsigned int *_outBufferSize)
packet1.data += bytesDecoded;
packet1.size -= bytesDecoded;
}
#endif
}
AVPacketUnref(&packet);
av_packet_unref(packet);
}

AVPacketUnref(&packet);
av_packet_unref(packet);

// Seek to the beginning so that it can be decoded again, if necessary.
av_seek_frame(this->formatCtx, this->audioStream, 0, 0);
Expand Down Expand Up @@ -214,7 +271,11 @@ bool AudioDecoder::SetFile(const std::string &_filename)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#if LIBAVFORMAT_VERSION_MAJOR >= 59
if (this->formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
#else
if (this->formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
#endif
#ifndef _WIN32
# pragma GCC diagnostic pop
#endif
Expand All @@ -238,13 +299,61 @@ bool AudioDecoder::SetFile(const std::string &_filename)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#if LIBAVFORMAT_VERSION_MAJOR < 59
this->codecCtx = this->formatCtx->streams[audioStream]->codec;
#endif
#ifndef _WIN32
# pragma GCC diagnostic pop
#endif

// Find a decoder
#if LIBAVFORMAT_VERSION_MAJOR >= 59
const AVCodec * local_codec = avcodec_find_decoder(this->formatCtx->streams[
this->audioStream]->codecpar->codec_id);
if (!local_codec)
{
gzerr << "Failed to find the codec" << std::endl;
return false;
}
this->codecCtx = avcodec_alloc_context3(local_codec);
if (!this->codecCtx)
{
gzerr << "Failed to allocate the codec context" << std::endl;
return false;
}

// Copy all relevant parameters from codepar to codecCtx
if (avcodec_parameters_to_context(this->codecCtx,
this->formatCtx->streams[this->audioStream]->codecpar) < 0)
{
gzerr << "Failed to copy codec parameters to decoder context"
<< std::endl;
return false;
}

// This copy should be done by avcodec_parameters_to_context, but at least on
// conda-forge with 5.0.0 h594f047_1 this is not happening for some reason.
// As temporary workaround, we copy directly the data structures, taking the code from
// https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavcodec/codec_par.c#L120
AVCodecParameters* par = this->formatCtx->streams[this->audioStream]->codecpar;
this->codecCtx->sample_fmt = static_cast<AVSampleFormat>(par->format);
this->codecCtx->channel_layout = par->channel_layout;
this->codecCtx->channels = par->channels;
this->codecCtx->sample_rate = par->sample_rate;
this->codecCtx->block_align = par->block_align;
this->codecCtx->frame_size = par->frame_size;
this->codecCtx->delay =
this->codecCtx->initial_padding = par->initial_padding;
this->codecCtx->trailing_padding = par->trailing_padding;
this->codecCtx->seek_preroll = par->seek_preroll;

// It would be better to just define codec as const AVCodec *,
// but that is not done to avoid ABI problem. Anyhow, as codec
// it is a private attribute there should be no problem
this->codec = const_cast<AVCodec *>(local_codec);
#else
this->codec = avcodec_find_decoder(codecCtx->codec_id);
#endif

if (this->codec == nullptr)
{
Expand Down
85 changes: 76 additions & 9 deletions gazebo/common/Video.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,53 @@ using namespace common;
// }
// #endif

/////////////////////////////////////////////////
#ifdef HAVE_FFMPEG
int GazeboAVCodecDecodeHelper(AVCodecContext *_codecCtx,
AVFrame *_frame, int *_gotFrame, AVPacket *_packet)
{
#if LIBAVFORMAT_VERSION_MAJOR >= 59
// from https://blogs.gentoo.org/lu_zero/2016/03/29/new-avcodec-api/
int ret;

*_gotFrame = 0;

if (_packet)
{
ret = avcodec_send_packet(_codecCtx, _packet);
if (ret < 0)
{
return ret == AVERROR_EOF ? 0 : ret;
}
}

ret = avcodec_receive_frame(_codecCtx, _frame);
if (ret < 0 && ret != AVERROR(EAGAIN))
{
return ret;
}
if (ret >= 0)
{
*_gotFrame = 1;
}

// new API always consumes the whole packet
return _packet ? _packet->size : 0;
#else
// this was deprecated in ffmpeg version 3.1
// github.com/FFmpeg/FFmpeg/commit/7fc329e2dd6226dfecaa4a1d7adf353bf2773726
# ifndef _WIN32
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# endif
return avcodec_decode_video2(_codecCtx, _frame, _gotFrame, _packet);
# ifndef _WIN32
# pragma GCC diagnostic pop
# endif
#endif
}
#endif

/////////////////////////////////////////////////
Video::Video()
{
Expand Down Expand Up @@ -77,7 +124,7 @@ void Video::Cleanup()
#ifdef HAVE_FFMPEG
bool Video::Load(const std::string &_filename)
{
AVCodec *codec = nullptr;
const AVCodec *codec = nullptr;
this->videoStream = -1;

if (this->formatCtx || this->avFrame || this->codecCtx)
Expand All @@ -103,13 +150,17 @@ bool Video::Load(const std::string &_filename)
// Find the first video stream
for (unsigned int i = 0; i < this->formatCtx->nb_streams; ++i)
{
#if LIBAVFORMAT_VERSION_MAJOR >= 59
if (this->formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
#else
#ifndef _WIN32
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
if (this->formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
#ifndef _WIN32
# pragma GCC diagnostic pop
#endif
#endif
{
this->videoStream = static_cast<int>(i);
Expand All @@ -124,6 +175,26 @@ bool Video::Load(const std::string &_filename)
}

// Get a pointer to the codec context for the video stream
#if LIBAVFORMAT_VERSION_MAJOR >= 59
// AVCodecContext is not included in an AVStream as of ffmpeg 3.1
// allocate a codec context based on updated example
// github.com/FFmpeg/FFmpeg/commit/bba6a03b2816d805d44bce4f9701a71f7d3f8dad
this->codecCtx = avcodec_alloc_context3(codec);
if (!this->codecCtx)
{
gzerr << "Failed to allocate the codec context" << std::endl;
return false;
}

// Copy codec parameters from input stream to output codec context
if (avcodec_parameters_to_context(this->codecCtx,
this->formatCtx->streams[this->videoStream]->codecpar) < 0)
{
gzerr << "Failed to copy codec parameters to decoder context"
<< std::endl;
return false;
}
#else
#ifndef _WIN32
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Expand All @@ -132,6 +203,8 @@ bool Video::Load(const std::string &_filename)
#ifndef _WIN32
# pragma GCC diagnostic pop
#endif
#endif


// Find the decoder for the video stream
codec = avcodec_find_decoder(this->codecCtx->codec_id);
Expand Down Expand Up @@ -227,15 +300,9 @@ bool Video::GetNextFrame(unsigned char **_buffer)
while (tmpPacket.size > 0)
{
// sending data to libavcodec
#ifndef _WIN32
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
int processedLength = avcodec_decode_video2(this->codecCtx, this->avFrame,
int processedLength = GazeboAVCodecDecodeHelper(this->codecCtx, this->avFrame,
&frameAvailable, &tmpPacket);
#ifndef _WIN32
# pragma GCC diagnostic pop
#endif

if (processedLength < 0)
{
gzerr << "Error while processing the data\n";
Expand Down
4 changes: 2 additions & 2 deletions gazebo/common/VideoEncoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ bool VideoEncoder::Start(const std::string &_format,

// The remainder of this function handles FFMPEG initialization of a video
// stream
AVOutputFormat *outputFormat = nullptr;
const AVOutputFormat *outputFormat = nullptr;

// This 'if' and 'free' are just for safety. We chech the value of formatCtx
// below.
Expand Down Expand Up @@ -294,7 +294,7 @@ bool VideoEncoder::Start(const std::string &_format,
}

// find the video encoder
AVCodec *encoder = avcodec_find_encoder(
const AVCodec *encoder = avcodec_find_encoder(
this->dataPtr->formatCtx->oformat->video_codec);
if (!encoder)
{
Expand Down

0 comments on commit 96c8538

Please sign in to comment.