mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
[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:
parent
4f308095ce
commit
5949fc61dc
5 changed files with 68 additions and 7 deletions
|
|
@ -145,6 +145,8 @@ FFmpegLibWrapper::Link()
|
|||
AV_FUNC(avcodec_alloc_frame, (AV_FUNC_53 | AV_FUNC_54))
|
||||
AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
|
||||
AV_FUNC(avcodec_free_frame, AV_FUNC_54)
|
||||
AV_FUNC(avcodec_send_packet, AV_FUNC_58)
|
||||
AV_FUNC(avcodec_receive_frame, AV_FUNC_58)
|
||||
AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
|
||||
AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
|
||||
AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ struct FFmpegLibWrapper
|
|||
// libavcodec v54 only
|
||||
void (*avcodec_free_frame)(AVFrame** frame);
|
||||
|
||||
// libavcodec v58 and later only
|
||||
int (*avcodec_send_packet)(AVCodecContext* avctx, const AVPacket* avpkt);
|
||||
int (*avcodec_receive_frame)(AVCodecContext* avctx, AVFrame* frame);
|
||||
|
||||
// libavutil
|
||||
void (*av_log_set_level)(int level);
|
||||
void* (*av_malloc)(size_t size);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ private:
|
|||
MediaResult DoDecode(MediaRawData* aSample) override;
|
||||
MediaResult DoDecode(MediaRawData* aSample, bool* aGotFrame);
|
||||
MediaResult DoDecode(MediaRawData* aSample, uint8_t* aData, int aSize, bool* aGotFrame);
|
||||
MediaResult CreateImage(int64_t aOffset, int64_t aPts, int64_t aDuration);
|
||||
void ProcessDrain() override;
|
||||
void ProcessFlush() override;
|
||||
void OutputDelayedFrames();
|
||||
|
|
|
|||
|
|
@ -127,3 +127,5 @@ avpicture_free
|
|||
avpicture_get_size
|
||||
avpicture_layout
|
||||
avsubtitle_free
|
||||
avcodec_send_packet
|
||||
avcodec_receive_frame
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue