BogdanovKirill / RtspClientSharp

Pure C# RTSP client for .NET Standard without external dependencies and with true async nature. I welcome contributions.
MIT License
706 stars 284 forks source link

System.AccessViolationException from SimpleRtspPlayer example #62

Open MysticMight opened 4 years ago

MysticMight commented 4 years ago

Describe the bug A unhandled AccessViolation exception which occurs when disconnecting (see reproduction steps). I've noticed this same issue in my own project, which uses the decoding implementation from SimpleRtspPlayer, and have been able to reproduce it in a fork of RtspClientSharp. However it is much more observable in my (closed-source) project then the RtspClientSharp fork, and often causes a crash on a single disconnect from the server. To observe more details I enabled native code debugging and found a error in avcodec-58.dll, so I am not sure if this is specific to this project or FFMPEG.

To Reproduce Steps to reproduce the behavior:

  1. Setup a RTSP server to connect to.
  2. Setup clone/fork of RtspClientSharp locally in Visual Studio. (I also made some adjustments for mine to build, though not sure if its relevant to this issue, see Additional context)
  3. Build and run SimpleRtspPlayer example in debug mode (using x86).
  4. Enter in RTSP server address setup from Step 1 and connect.
  5. Disconnect from the server and reconnect shortly afterwards (sometimes only a disconnect is required).
  6. Continue step 5 until a exception occurs.

Expected behavior I expect there to likely be issues when disconnecting/reconnecting too quickly (e.g. if a object is disposed or still exists), however from my experience it also occurs sometimes on a single disconnect (though I've only seen this in my program so far). Ideally the cause of the exception would be fixed, or at least not cause a crash.

Screenshots First Exception (only when native debugging is on for SimpleRtspPlayer): Exception thrown at 0x79B45CA0 (avcodec-58.dll) in CrawlerRTSPClient.exe: 0xC0000005: Access violation reading location 0x00001602.

Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

stacktrace below:

Stacktrace ```at SimpleRtspPlayer.RawFramesDecoding.FFmpeg.FFmpegVideoPInvoke.DecodeFrame(IntPtr handle, IntPtr rawBuffer, Int32 rawBufferLength, Int32& frameWidth, Int32& frameHeight, FFmpegPixelFormat& framePixelFormat) at SimpleRtspPlayer.RawFramesDecoding.FFmpeg.FFmpegVideoDecoder.TryDecode(RawVideoFrame rawVideoFrame) in C:\Users\DirectoryToProject\rtspclientsharp\Examples\SimpleRtspPlayer\RawFramesDecoding\FFmpeg\FFmpegVideoDecoder.cs:line 75 at SimpleRtspPlayer.GUI.RealtimeVideoSource.OnFrameReceived(Object sender, RawFrame rawFrame) in C:\Users\DirectoryToProject\rtspclientsharp\Examples\SimpleRtspPlayer\GUI\RealtimeVideoSource.cs:line 60 at System.EventHandler`1.Invoke(Object sender, TEventArgs e) at RtspClientSharp.Rtsp.RtspClientInternal.OnFrameGeneratedThreadSafe(RawFrame frame) at RtspClientSharp.MediaParsers.H264Parser.TryGenerateFrame(ArraySegment`1 frameBytes) at RtspClientSharp.MediaParsers.H264Parser.TryGenerateFrame() at RtspClientSharp.MediaParsers.H264Parser.Parse(ArraySegment`1 byteSegment, Boolean generateFrame) at RtspClientSharp.MediaParsers.H264VideoPayloadParser.ParseFU(ArraySegment`1 byteSegment, Int32 donFieldSize, Boolean markerBit) at RtspClientSharp.MediaParsers.H264VideoPayloadParser.Parse(TimeSpan timeOffset, ArraySegment`1 byteSegment, Boolean markerBit) at RtspClientSharp.Rtp.RtpStream.ProcessImmediately(RtpPacket& rtpPacket) at RtspClientSharp.Rtp.RtpSequenceAssembler.ProcessPacket(RtpPacket& rtpPacket) at RtspClientSharp.Rtsp.RtspClientInternal.d__36.MoveNext() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c__DisplayClass4_0.b__0() at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass11_0.b__0() at System.Threading.Tasks.AwaitTaskContinuation.ExecuteWorkItemHelper() at System.Threading.ThreadPoolWorkQueue.Dispatch() ```

Desktop (please complete the following information):

Additional context Not sure if this is too relevant, here are some details on some adjustments I made to the project for my environment: In order to build with my environment I found I needed to adjust the libffmeghelper's WindowsTargetPlatformVersion to 10.0.15063.0 (to use the default SDK). I also changed the SimpleRtspClient's supported runTime and target framework version to v4.6.1. I am intending to use a UDP connection.

MysticMight commented 4 years ago

After some debugging with log messages it appears that this issue comes about the possibility that a decoder (e.g. FFmpegVideoDecoder) can be disposed of before, just before or during the use of its handle in the FFmpeg library methods. This disposal appears to remove the memory allocation for the frame as well as possibly other objects, causing the exceptions based on the timing of these events.

Perhaps using a locked object (or atleast checking disposal state) when using the decoder could prevent use when it is being disposed or disposal during use. Though for many of the logs i could catch the _disposed field wasn't set just before a action (e.g. scaling/decoding using the library).

For reference in the following console logs: "Disposing FFmpegVideoDecoder": is sent just before the _disposed field is set to true "Disposed FFmpegVideoDecoder": is sent at the bottom of FFmpegVideoDecoder.Dispose() "I am disposed, so you probably don't want to do this!..": indicated the _disposed field (which I made volatile) was set to true just before a action (e.g. scaling/decoding). Also the decoder handles mentioned are just the decimal number for its address. 'Unfortunately' the added console log messages caused noticeable delay and actually made the exception harder to reproduce.

Some logs for reference:

attempting use of already disposed decoder (which could have been prevented) ``` Attempting to scale frame using decoder with handle: 106413440 Attempting to decode frame using decoder with handle: 106413440 Disposing FFmpegVideoDecoder with handle: 106413440. Disposed FFmpegVideoDecoder with handle: 106413440. I am disposed, so you probably don't want to do this! But I'll let you do it for now. (scaling frame) Attempting to scale frame using decoder with handle: 106413440 Exception thrown at 0x523C5BF3 (swscale-5.dll) in SimpleRtspPlayer.exe: 0xC0000005: Access violation reading location 0xD13F3E8C. The thread 0x1234 has exited with code 0 (0x0). The thread 0xdcc has exited with code 0 (0x0). Exception thrown: 'System.AccessViolationException' in SimpleRtspPlayer.exe ```
attempting use of the decoder which has started being disposed ``` Disposing FFmpegVideoDecoder with handle: 47366720. Disposed FFmpegVideoDecoder with handle: 47366720. The thread 0x57f0 has exited with code 0 (0x0). Exception thrown: 'System.ObjectDisposedException' in System.dll Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.OperationCanceledException' in mscorlib.dll Disposing FFmpegVideoDecoder with handle: 47366720. Exception thrown at 0x78EA5CA0 (avcodec-58.dll) in SimpleRtspPlayer.exe: 0xC0000005: Access violation reading location 0x00000212. The thread 0x615c has exited with code 0 (0x0). Exception thrown: 'System.AccessViolationException' in SimpleRtspPlayer.exe The thread 0x48ec has exited with code 0 (0x0). The thread 0x32d0 has exited with code 0 (0x0). An unhandled exception of type 'System.AccessViolationException' occurred in SimpleRtspPlayer.exe Attempted to read or write protected memory. This is often an indication that other memory is corrupt. ```
stacktrace from attempting to scale frame ``` at SimpleRtspPlayer.RawFramesDecoding.FFmpeg.FFmpegVideoPInvoke.ScaleDecodedVideoFrame(IntPtr handle, IntPtr scalerHandle, IntPtr scaledBuffer, Int32 scaledBufferStride) at SimpleRtspPlayer.RawFramesDecoding.FFmpeg.FFmpegVideoDecoder.TransformTo(IntPtr buffer, Int32 bufferStride, TransformParameters parameters) in C:\Users\DirectoryToProject\rtspclientsharp\Examples\SimpleRtspPlayer\RawFramesDecoding\FFmpeg\FFmpegVideoDecoder.cs:line 123 at SimpleRtspPlayer.RawFramesDecoding.DecodedFrames.DecodedVideoFrame.TransformTo(IntPtr buffer, Int32 bufferStride, TransformParameters transformParameters) in C:\Users\DirectoryToProject\rtspclientsharp\Examples\SimpleRtspPlayer\RawFramesDecoding\DecodedFrames\DecodedVideoFrame.cs:line 16 at SimpleRtspPlayer.GUI.Views.VideoView.Invalidate(IDecodedVideoFrame decodedVideoFrame) in C:\Users\DirectoryToProject\rtspclientsharp\Examples\SimpleRtspPlayer\GUI\Views\VideoView.xaml.cs:line 157 ```
non-crashing case I came across? ``` Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Disposing FFmpegVideoDecoder with handle: 115744576. Disposed FFmpegVideoDecoder with handle: 115744576. I am disposed, so you probably don't want to do this! But I'll let you do it for now. (scaling frame) Attempting to scale frame using decoder with handle: 115744576 The thread 0x4990 has exited with code 0 (0x0). Exception thrown: 'System.ObjectDisposedException' in System.dll Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.OperationCanceledException' in mscorlib.dll The thread 0x1d50 has exited with code 0 (0x0). The thread 0x1d50 has exited with code 0 (0x0). Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.ObjectDisposedException' in System.dll Exception thrown: 'System.OperationCanceledException' in RtspClientSharp.dll Exception thrown: 'System.IO.IOException' in System.dll Exception thrown: 'System.OperationCanceledException' in mscorlib.dll Exception thrown: 'System.IO.IOException' in mscorlib.dll Exception thrown: 'System.OperationCanceledException' in mscorlib.dll Exception thrown: 'System.IO.IOException' in mscorlib.dll Exception thrown: 'System.IO.IOException' in mscorlib.dll Exception thrown: 'System.IO.IOException' in mscorlib.dll Exception thrown: 'System.IO.IOException' in mscorlib.dll Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Attempting to decode frame using decoder with handle: 115744576 Attempting to scale frame using decoder with handle: 115744576 Disposing FFmpegVideoDecoder with handle: 115744576. Disposed FFmpegVideoDecoder with handle: 115744576. ```
AdrienAlbertini commented 4 years ago

@MysticMight Hi, did you find a fix about this issue ? I'm trying to fix the _disposed field issue you are talking about but still have the crash. Thanks !

MysticMight commented 4 years ago

@MysticMight Hi, did you find a fix about this issue ? I'm trying to fix the _disposed field issue you are talking about but still have the crash. Thanks !

Yes, I believe so. I just haven't gotten the chance to submit a pull request yet. I may submit it this week.

RandDruid commented 4 years ago

@MysticMight I added your fix to my own project - so far everything works fine. Speaking of this file - do you think it may have sense to protect _scalersMap as well? In my code I am also locking _scalersMap, because TransformTo may attempt to add scaler while DropAllVideoScalers will be doing "foreach".

MysticMight commented 4 years ago

@MysticMight I added your fix to my own project - so far everything works fine. Speaking of this file - do you think it may have sense to protect _scalersMap as well? In my code I am also locking _scalersMap, because TransformTo may attempt to add scaler while DropAllVideoScalers will be doing "foreach".

@RandDruid Yes I guess so. I haven't noticed any issues for it, but it'll atleast prevent it doing extra work when unnecessary (creating a scaler) and of course properly closing the scalar, depending on the timing. Did you just add the same disposal lock over DropAllVideoScalers() contents, as well as shifting up the lock in TransformTo() to the top?

RandDruid commented 4 years ago

@MysticMight I did it in a pretty simple way I also never have problems with Scaler, and anyway in my project TransformTo is redundant since I leave it to WPF control to adjust image. I just read your comment and remembered about this small thing. May be you can do it better. My own pull request is also waiting for author review, but I don't want to mix unrelated things.