BogdanovKirill / RtspClientSharp

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

Recording to file example #34

Open wjax opened 5 years ago

wjax commented 5 years ago

Hi There

I am trying to save a rtsp stream to a file in order to be able to see it afterwards by opening like a regular mp4 file with vlc for example.

I have been doing this with regular udp multicast streaming quite often. Just saving UDP payload to file.

RSTP is new to me and I have tried with your SimpleRtspClient by modifing the FrameReceived event to this: rtspClient.FrameReceived += (sender, frame) => { if (frame is RawH264Frame) fileStream.Write(frame.FrameSegment); };

The resulting file does not play in VLC.

Can you please point to the right solution?

Many thanks

fotels commented 5 years ago

I support this thread. I would be very interested in the same thing. I have been struggling with this problem for a long time, but the only thing I managed to achieve is to save a single frame to a PNG file. It takes about 1s, so marging single frames into movie is not an option :(

BogdanovKirill commented 5 years ago

Hi,

Please save SpsPpsSegment of every RawH264IFrame before FrameSegment. Set h264 extension for output file and try again.

BogdanovKirill commented 5 years ago

Any news?

fotels commented 5 years ago

Hi Kirill, Thanks for your advice. I don't know how mr wjax is doing, but I tried to apply his code along with your suggestion. I'm still not getting working mp4 file. I think that maybe I am doing something wrong with closing the file. Not sure...

I'm still trying to dig more into the topic on my own, but lately I had unfortunately a little less time. Tomorrow ill try again :)

I will let you know when I will succeed in this topic :))

wjax commented 5 years ago

Hi there.

I have stop to test it because what I wanted was to forward UDP traffic from rtsp to another multicast. I have realized that sending udp packets already reconstructed may be a loss of time and cpu.

So I have taken every packet coming from camera and resend to another multicast. I have a mpv player listening and it works. However I see some artifacts from time to time.

Many thanks

BogdanovKirill commented 5 years ago

@wjax

You see artifacts because of packet loss. It's by design problem, because you use UDP

wjax commented 5 years ago

But they are in the same machine.

Camera -- PC (ReMulticast + MPV)

BogdanovKirill commented 5 years ago

And what about connection between Camera and PC ? Is it internet?

wjax commented 5 years ago

Nop. Lan.

Un fact if I tell mpv to read camera RTSP it works flawlessly.

But if I attach mpv to my resend, it has artifacts from time to time.

El mar., 4 jun. 2019 17:45, Kirill notifications@github.com escribió:

And what about connection between Camera and PC ? Is it internet?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BogdanovKirill/RtspClientSharp/issues/34?email_source=notifications&email_token=AACLVHQBVDCAU24BLZKH37LPY2E3DA5CNFSM4HPVNEDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODW477JA#issuecomment-498728868, or mute the thread https://github.com/notifications/unsubscribe-auth/AACLVHT34USBF2C4CMWBMSTPY2E3DANCNFSM4HPVNEDA .

fotels commented 5 years ago

Hello Kirill,

First of all, thank you very much for your suggestions. I spent some time on your code, and after a few sessions of tinkering with it, with the help of your suggestions, I was able to actually save the recording to the mp4 file. And it actually works!

Thank you very much!

During all this time I probably tried to do it not in the right place, besides WPF gives me some trouble, but in the end I ended up with something like this:

DateTime now = DateTime.Now; if (recordStart < now.AddSeconds(-30)) { if (recordFile != null) recordFile.Close(); string path = @"E:\Rec_" + now.ToString("yyyyMMdd_HHmmss") + ".mp4"; recordFile = File.Create(path); recordStart = now; } if (recordFile != null && rawVideoFrame is RawH264IFrame rawH264IFrame) { RawH264IFrame frm = rawVideoFrame as RawH264IFrame; recordFile.Write( frm.SpsPpsSegment.Array, frm.SpsPpsSegment.Offset, frm.SpsPpsSegment.Count); firsIFrameSaved = true; } if (firsIFrameSaved && recordFile != null && rawVideoFrame is RawH264Frame) { recordFile.Write( rawVideoFrame.FrameSegment.Array, rawVideoFrame.FrameSegment.Offset, rawVideoFrame.FrameSegment.Count); }

This code produces me a new file with a recording every 30 seconds. The file plays in MPC.

I have a different problem now. I've tried to defeat it, but I have to ask you for help again.

The recording that is produced by me is quite strange.

The material is played about 2x faster. Recording according to MPC takes 17 seconds, but watching the clock on the recording produced by the Foscam camera, the recording takes about 27 seconds.

It seems to me that the missing 3 seconds according to the time of the camera is waiting for the first IFrame, while 2x a faster playing of material is a mystery to me.

Another less important, but perhaps related problem is the non-moving position marker in the film (small square).

Should the mp4 file have somehow saved duration and speed of playing informations?

Exactly the same stream from the camera in the window of your program plays perfectly in real time.

One more time - thanks for your help! I really appreciate it!

BogdanovKirill commented 5 years ago

@fotels

Thank you for your feedback. Relly I dont know how MP4 format works, so I would like to suggest to check that you set FPS in MP4 and/or frame timestamps. May be non-moving position marker is also connected with frame timestamps. I think this is a root of too fast playing speed. Also I would advise you to start every MP4 from I-frame this must solve problem when you don't see first seconds of video file.

fotels commented 5 years ago

Hi Kirill,

Small update - I have examined the image from your player and that one I made by ripping frames into files. It looks like everything is OK.

After a very close look, I noticed that the image from your player is not so super smooth as i thought. Do not get me wrong - your player is great! It turns out that my camera gives a signal of poor quality.

Accurate analysis shows that your program catches only those "fully good and pretty frames that do not contain interference". Such a fully good frame happens only once in a while with my camera (it's probably a matter of connecting through UDP). The effect I mentioned was created because in the MP4 movie recorded on the disc, only those "good" frames are put down. The same happens probably in the window of the player, with the difference that until it get a new full "good" frame, it presents the previous "good" frame for all that time and the film is generally played in real time.

That's why, when you look closely, you can see a slight stuttering in the live player.

Going further - good frames come irregularly - that's why you can see that the "recorded second" sometimes lasts shorter and sometimes longer.

For me, this is a very good effect, because thanks to this the film is free of "bad frames", thanks to which it takes up less disk space and can be analyzed better in the next stage.

In conclusion - your code is great, everything works very well and your advice was 100% accurate and good, I really thank you very much!

I tried to test the same on another camera that allows to connect to RTSP over TCP. I tried to connect to it via TCP with your player but I get an error about missing PPS in the "sprop-parameter-sets" parameter. The same error appears in the ffmpeg player, but there it is marked as "ignored". Now, unfortunately, I do not have access to the code so I can not send the exact content of the error.

I would just like to ask - does your player allow to connect via TCP to the RTSP stream?

BogdanovKirill commented 5 years ago

@fotels

Yes, you absolutely right about stuttering and this problem could be solve via small bufferization 100-500 ms depending on connection quality (see https://github.com/BogdanovKirill/RtspClientSharp/issues/27).

RTSP over TCP should work perfect. It's very useful information about PPS error, thank you very much. Could you create separate issue with this bug here please? I will fix it.

WoodsterDK commented 5 years ago

Hi, your project seems pretty cool - do you have a working example of storing a stream to a file that can be played by VLC ?

I can't get it to play the file save from this example.

Regards

BogdanovKirill commented 5 years ago

@WoodsterDK

Hi! No, you should write such logic by yourself

WoodsterDK commented 5 years ago

@BogdanovKirill I tried to add the logic from fotels - it gives an mp4 file, but it does not play in VLC. Do you have an idea of what could be wrong - the camera I connect to outputs data, that in the eventhandler is being reported as: RawH264IFrame.

I could really use some help ...

WoodsterDK commented 5 years ago

Ahh,, I had kind of success....

The stream is stored to a file, and then run through ffmpeg - and then a mp4 file appears that can be played in VLC.

Is this the way to do it?

What about an example app, that can store the stream in 5 minutes files with timestamps, which then can be used for quickly finding the right file when something happened. Or an example with a rolling window buffer?

BogdanovKirill commented 5 years ago

@WoodsterDK

Sorry, I have no free time to do it. You could write it and make pull request.

kubilayorcun commented 5 years ago

Hello Kirill,

First of all, thank you very much for your suggestions. I spent some time on your code, and after a few sessions of tinkering with it, with the help of your suggestions, I was able to actually save the recording to the mp4 file. And it actually works!

Thank you very much!

During all this time I probably tried to do it not in the right place, besides WPF gives me some trouble, but in the end I ended up with something like this:

DateTime now = DateTime.Now; if (recordStart < now.AddSeconds(-30)) { if (recordFile != null) recordFile.Close(); string path = @"E:\Rec_" + now.ToString("yyyyMMdd_HHmmss") + ".mp4"; recordFile = File.Create(path); recordStart = now; } if (recordFile != null && rawVideoFrame is RawH264IFrame rawH264IFrame) { RawH264IFrame frm = rawVideoFrame as RawH264IFrame; recordFile.Write( frm.SpsPpsSegment.Array, frm.SpsPpsSegment.Offset, frm.SpsPpsSegment.Count); firsIFrameSaved = true; } if (firsIFrameSaved && recordFile != null && rawVideoFrame is RawH264Frame) { recordFile.Write( rawVideoFrame.FrameSegment.Array, rawVideoFrame.FrameSegment.Offset, rawVideoFrame.FrameSegment.Count); }

This code produces me a new file with a recording every 30 seconds. The file plays in MPC.

I have a different problem now. I've tried to defeat it, but I have to ask you for help again.

The recording that is produced by me is quite strange.

The material is played about 2x faster. Recording according to MPC takes 17 seconds, but watching the clock on the recording produced by the Foscam camera, the recording takes about 27 seconds.

It seems to me that the missing 3 seconds according to the time of the camera is waiting for the first IFrame, while 2x a faster playing of material is a mystery to me.

Another less important, but perhaps related problem is the non-moving position marker in the film (small square).

Should the mp4 file have somehow saved duration and speed of playing informations?

Exactly the same stream from the camera in the window of your program plays perfectly in real time.

One more time - thanks for your help! I really appreciate it!

@fotels

Can you please share the complete code that is able to save raw frames as mp4 files. I have been trying different solutions with the help of the code snippet that you've shared above. So far i have been dealing with some issues.

As i said, if you can share the rest of it, it would be awesome.

hagaygo commented 4 years ago

Just sharing my finding regarding this issue.

I tried to accomplish the same ting , capturing h264 frame from RTSP source (ip camera) and saving them without re-encoding to mp4 at will.

Latest VLC can play the "raw" h264 file , just output to a file and drag it to vlc , it should be able to play just fine (its not a mp4 file , just a file that contain h264 raw frames)

I have access to 3 different IP cameras with RTSP streams.

Using this code i managed to create a kinda perfect mp4 from only 1 camera.

On the other 2 cameras the result was bad.

  1. vlc reported lost frames , and video was clearly missing chunk of frames.
  2. vlc playback was at half of the fps.

image

Converting the raw h264 file to mp4 using https://github.com/TalAloni/MP4Maker result in same playback.

using VLC to convert the same 3 RTSP sources to mp4/raw h264 resulted in perfect file. So i guess the code not compatible with all RTSP sources.

The main difference i found between the working IP Camera was that it had iframe every 1 second instead of about 2 seconds and had no audio frames.

NapalmRain commented 3 years ago

@fotels , tell me please, do you have success with recording to file? If yes, please, share you code as example! thanks in advance!

fotels commented 3 years ago

Hello @NapalmRain (great nick by the way) :) Sorry, for a long time to write back, I haven't been here for a long time.

I kind of solved the problem, but you probably won't be happy with the solution I have adopted. The code provided by @BogdanovKirill is great, I don't dispute it by any means, but when I struggled with this task I had quite a big time pressure. I also started to test second solution independently, and finally I managed to solve it in a different way faster.

And this way is just using the ffmpeg library. It is a console program so you can easily operate it from your own C# app. FFMpeg has many configuration options, you can run multiple instances at the same time, etc. In my case, I needed a way to record video from many cameras at the same time, automatically dividing the recordings into short parts of a given length. I have cameras from 3 or even 4 manufacturers, each of them required a slightly different approach, so setting up ffmpeg was simply faster and easier in my case.

However, i assume that this is not a good solution for everyone. I was creating that project under time pressure, but it was a typical home project, so I could accept such a thing.

I have to try to work again on the code provided by @BogdanovKirill in my free time because everything was looking great with this approach.

Forgive me for not being able to help you better.

NapalmRain commented 3 years ago

@fotels thank you for your answer! Unfortunately, i can't use ffmpeg in my project. I Tryed it and it don't work with my camera (i don't know why). i'll try to complete task with this project... Have a nice day!

joaofurlanetto commented 3 years ago

Hello Kirill,

First of all, thank you very much for your suggestions. I spent some time on your code, and after a few sessions of tinkering with it, with the help of your suggestions, I was able to actually save the recording to the mp4 file. And it actually works!

Thank you very much!

During all this time I probably tried to do it not in the right place, besides WPF gives me some trouble, but in the end I ended up with something like this:

DateTime now = DateTime.Now; if (recordStart < now.AddSeconds(-30)) { if (recordFile != null) recordFile.Close(); string path = @"E:\Rec_" + now.ToString("yyyyMMdd_HHmmss") + ".mp4"; recordFile = File.Create(path); recordStart = now; } if (recordFile != null && rawVideoFrame is RawH264IFrame rawH264IFrame) { RawH264IFrame frm = rawVideoFrame as RawH264IFrame; recordFile.Write( frm.SpsPpsSegment.Array, frm.SpsPpsSegment.Offset, frm.SpsPpsSegment.Count); firsIFrameSaved = true; } if (firsIFrameSaved && recordFile != null && rawVideoFrame is RawH264Frame) { recordFile.Write( rawVideoFrame.FrameSegment.Array, rawVideoFrame.FrameSegment.Offset, rawVideoFrame.FrameSegment.Count); }

This code produces me a new file with a recording every 30 seconds. The file plays in MPC.

I have a different problem now. I've tried to defeat it, but I have to ask you for help again.

The recording that is produced by me is quite strange.

The material is played about 2x faster. Recording according to MPC takes 17 seconds, but watching the clock on the recording produced by the Foscam camera, the recording takes about 27 seconds.

It seems to me that the missing 3 seconds according to the time of the camera is waiting for the first IFrame, while 2x a faster playing of material is a mystery to me.

Another less important, but perhaps related problem is the non-moving position marker in the film (small square).

Should the mp4 file have somehow saved duration and speed of playing informations?

Exactly the same stream from the camera in the window of your program plays perfectly in real time.

One more time - thanks for your help! I really appreciate it!

I had exactly the same problem. @fotels arrived at any solution?