SHA256
Fix Decoder Invalid Packet Handling
This commit is contained in:
1 file changed
+38
-3
@@ -191,6 +191,13 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
||||
private bool _isOpened;
|
||||
private bool _disposed;
|
||||
private bool _isDraining;
|
||||
|
||||
/// <summary>Number of video packets skipped because the decoder rejected them as invalid data.</summary>
|
||||
private int _skippedPacketCount;
|
||||
|
||||
/// <summary>Number of skipped packets for which a warning is logged.</summary>
|
||||
private const int MaxLoggedSkippedPackets = 5;
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
// Fields to detect SwsContext updates
|
||||
@@ -371,6 +378,16 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
||||
if (_videoStreamIndex == -1)
|
||||
throw new Exception("Failed to find video stream.");
|
||||
|
||||
// Only the video stream is used. The demuxer does not need to return the packets of the other streams
|
||||
// (audio, subtitles, ...) once they are marked as discarded.
|
||||
// Reference: AVStream.discard - "Selects which packets can be discarded at will and do not need to be demuxed."
|
||||
// https://ffmpeg.org/doxygen/trunk/structAVStream.html
|
||||
for (int i = 0; i < _pFormatContext->nb_streams; i++)
|
||||
{
|
||||
if (i != _videoStreamIndex)
|
||||
_pFormatContext->streams[i]->discard = AVDiscard.AVDISCARD_ALL;
|
||||
}
|
||||
|
||||
var stream = _pFormatContext->streams[_videoStreamIndex];
|
||||
_timeBase = stream->time_base;
|
||||
|
||||
@@ -441,9 +458,13 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
||||
TotalFrames = 0;
|
||||
}
|
||||
|
||||
Duration = _pFormatContext->duration > 0
|
||||
? _pFormatContext->duration / (double)ffmpeg.AV_TIME_BASE
|
||||
: (stream->duration > 0 ? stream->duration * ffmpeg.av_q2d(stream->time_base) : 0);
|
||||
// Duration of the video stream (AVStream.duration, in the stream time base).
|
||||
// When the stream does not carry a duration, the container duration (AVFormatContext.duration,
|
||||
// in AV_TIME_BASE units, the longest of all streams) is used.
|
||||
// Reference: https://ffmpeg.org/doxygen/trunk/structAVStream.html, https://ffmpeg.org/doxygen/trunk/structAVFormatContext.html
|
||||
Duration = stream->duration > 0
|
||||
? stream->duration * ffmpeg.av_q2d(stream->time_base)
|
||||
: _pFormatContext->duration > 0 ? _pFormatContext->duration / (double)ffmpeg.AV_TIME_BASE : 0;
|
||||
|
||||
// 9. Allocate frame and packet buffers
|
||||
// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html
|
||||
@@ -655,6 +676,8 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
||||
/// <summary>
|
||||
/// Reads packets from the container until a packet belonging to the video stream is found,
|
||||
/// then sends it to the decoder via <c>avcodec_send_packet</c>.
|
||||
/// A packet that the decoder rejects as invalid data (<c>AVERROR_INVALIDDATA</c>, e.g. a damaged packet)
|
||||
/// is skipped and the next packet is read. Any other decoder error is thrown.
|
||||
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavc__encdec.html
|
||||
/// </summary>
|
||||
/// <returns>True if a video packet was sent; false on EOF.</returns>
|
||||
@@ -670,6 +693,18 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
||||
{
|
||||
ret = ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket);
|
||||
ffmpeg.av_packet_unref(_pPacket);
|
||||
|
||||
if (ret == ffmpeg.AVERROR_INVALIDDATA)
|
||||
{
|
||||
// The warning is logged for the first few packets only (a damaged file can have many).
|
||||
_skippedPacketCount++;
|
||||
if (_skippedPacketCount <= MaxLoggedSkippedPackets)
|
||||
{
|
||||
Melon<RenderMod>.Logger.Warning($"[FFmpegNativeDecoder] Skipped an invalid video packet ({_skippedPacketCount}): {GetFFmpegErrorString(ret)}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
throw new Exception($"Error sending packet to decoder: {GetFFmpegErrorString(ret)}");
|
||||
|
||||
Reference in new issue
Block a user