[FFmpeg] Use new decode API with recent FFmpeg version.

In libavcodec 58 and later, the old avcodec_decode_video2 is broken and only return the first visible frame found after a VP9 super-frame.
This commit is contained in:
trav90 2018-12-27 11:11:35 -06:00 committed by Roy Tam
commit 5949fc61dc
5 changed files with 68 additions and 7 deletions

View file

@ -224,6 +224,48 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
packet.pos = aSample->mOffset;
#if LIBAVCODEC_VERSION_MAJOR >= 58
packet.duration = aSample->mDuration;
int res = mLib->avcodec_send_packet(mCodecContext, &packet);
if (res < 0) {
// In theory, avcodec_send_packet could sent -EAGAIN should its internal
// buffers be full. In practice this can't happen as we only feed one frame
// at a time, and we immediately call avcodec_receive_frame right after.
FFMPEG_LOG("avcodec_send_packet error: %d", res);
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
RESULT_DETAIL("avcodec_send_packet error: %d", res));
}
if (aGotFrame) {
*aGotFrame = false;
}
do {
if (!PrepareFrame()) {
NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
}
res = mLib->avcodec_receive_frame(mCodecContext, mFrame);
if (res == int(AVERROR_EOF)) {
return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
}
if (res == AVERROR(EAGAIN)) {
return NS_OK;
}
if (res < 0) {
FFMPEG_LOG("avcodec_receive_frame error: %d", res);
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
RESULT_DETAIL("avcodec_receive_frame error: %d", res));
}
MediaResult rv = CreateImage(mFrame->pkt_pos, mFrame->pkt_pts,
mFrame->pkt_duration);
if (NS_FAILED(rv)) {
return rv;
}
if (aGotFrame) {
*aGotFrame = true;
}
} while (true);
#else
// LibAV provides no API to retrieve the decoded sample's duration.
// (FFmpeg >= 1.0 provides av_frame_get_pkt_duration)
// As such we instead use a map using the dts as key that we will retrieve
@ -276,8 +318,21 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
// against the map becoming extremely big.
mDurationMap.Clear();
}
MediaResult rv = CreateImage(aSample->mOffset, pts, duration);
if (NS_SUCCEEDED(rv) && aGotFrame) {
*aGotFrame = true;
}
return rv;
#endif
}
MediaResult
FFmpegVideoDecoder<LIBAV_VER>::CreateImage(int64_t aOffset, int64_t aPts,
int64_t aDuration)
{
FFMPEG_LOG("Got one frame output with pts=%lld dts=%lld duration=%lld opaque=%lld",
pts, mFrame->pkt_dts, duration, mCodecContext->reordered_opaque);
aPts, mFrame->pkt_dts, aDuration, mCodecContext->reordered_opaque);
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = mFrame->data[0];
@ -317,9 +372,9 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
RefPtr<VideoData> v =
VideoData::CreateAndCopyData(mInfo,
mImageContainer,
aSample->mOffset,
pts,
duration,
aOffset,
aPts,
aDuration,
b,
!!mFrame->key_frame,
-1,
@ -331,9 +386,6 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
RESULT_DETAIL("image allocation error"));
}
mCallback->Output(v);
if (aGotFrame) {
*aGotFrame = true;
}
return NS_OK;
}