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 _isOpened;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private bool _isDraining;
|
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();
|
private readonly object _lock = new();
|
||||||
|
|
||||||
// Fields to detect SwsContext updates
|
// Fields to detect SwsContext updates
|
||||||
@@ -371,6 +378,16 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
|||||||
if (_videoStreamIndex == -1)
|
if (_videoStreamIndex == -1)
|
||||||
throw new Exception("Failed to find video stream.");
|
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];
|
var stream = _pFormatContext->streams[_videoStreamIndex];
|
||||||
_timeBase = stream->time_base;
|
_timeBase = stream->time_base;
|
||||||
|
|
||||||
@@ -441,9 +458,13 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
|||||||
TotalFrames = 0;
|
TotalFrames = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration = _pFormatContext->duration > 0
|
// Duration of the video stream (AVStream.duration, in the stream time base).
|
||||||
? _pFormatContext->duration / (double)ffmpeg.AV_TIME_BASE
|
// When the stream does not carry a duration, the container duration (AVFormatContext.duration,
|
||||||
: (stream->duration > 0 ? stream->duration * ffmpeg.av_q2d(stream->time_base) : 0);
|
// 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
|
// 9. Allocate frame and packet buffers
|
||||||
// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html
|
// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html
|
||||||
@@ -655,6 +676,8 @@ internal sealed unsafe class FFmpegNativeDecoder : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads packets from the container until a packet belonging to the video stream is found,
|
/// 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>.
|
/// 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
|
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavc__encdec.html
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if a video packet was sent; false on EOF.</returns>
|
/// <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);
|
ret = ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket);
|
||||||
ffmpeg.av_packet_unref(_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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
throw new Exception($"Error sending packet to decoder: {GetFFmpegErrorString(ret)}");
|
throw new Exception($"Error sending packet to decoder: {GetFFmpegErrorString(ret)}");
|
||||||
|
|||||||
Reference in new issue
Block a user