static async Task fragmp4(int seconds) { // This generates a "fragmented" MP4 which should be larger than an MP4 // with a normal single MOOV atom trailer at the end of the file. See: // https://superuser.com/a/1530949/143047 // 10 seconds of "-badmp4" is 34MB // 10 seconds of "-fragmp4" is 26MB regardless of the other options described in the link // 60 seconds (bad) is 219MB versus 208MB (frag) and again we lose 2 seconds // -g is keyframe rate, default is 250 // -flush_packets 1 flushes the I/O stream after each packet, decreasing latency (apparently) // adding two seconds to the requested duration approximates the regular file size (10s = 33MB, 60s = 218MB) seconds += 2; // HACK! see above var cam = GetConfiguredCamera(); var pathname = ramdiskPath + "video.mp4"; Directory.CreateDirectory(ramdiskPath); File.Delete(pathname); Console.WriteLine("Preparing pipeline..."); cam.ConfigureCameraSettings(); using (var ffmpeg = new ExternalProcessCaptureHandler( new ExternalProcessCaptureHandlerOptions { Filename = "ffmpeg", Arguments = $"-framerate 24 -i - -b:v 2500k -c copy -movflags +frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov {pathname}", EchoOutput = true, DrainOutputDelayMs = 500, // default TerminationSignals = new[] { Signum.SIGINT, Signum.SIGQUIT }, // not the supposedly-correct SIGINT+SIGINT but this produces some exit output })) { // quality arg-help says set bitrate zero to use quality for VBR var portCfg = new MMALPortConfig(MMALEncoding.H264, MMALEncoding.I420, quality: 10, bitrate: 0, timeout: null); using var encoder = new MMALVideoEncoder(); using var renderer = new MMALVideoRenderer(); encoder.ConfigureOutputPort(portCfg, ffmpeg); cam.Camera.VideoPort.ConnectTo(encoder); cam.Camera.PreviewPort.ConnectTo(renderer); Console.WriteLine("Camera warmup..."); await Task.Delay(2000); Console.WriteLine($"Capturing MP4: {pathname}"); var timerToken = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)); await Task.WhenAll(new Task[] { ffmpeg.ManageProcessLifecycleAsync(timerToken.Token), cam.ProcessAsync(cam.Camera.VideoPort, timerToken.Token), }).ConfigureAwait(false); } // can't use the convenient fall-through using or MMALCamera.Cleanup // throws: Argument is invalid. Unable to destroy component cam.Cleanup(); Console.WriteLine("Exiting."); }
static async Task badmp4(int seconds) { Console.WriteLine("\n\nWARNING:\nffmpeg can't create a valid MP4 when running as a child process.\nSee repository README. This code is here for reference only.\n\nPress any key..."); Console.ReadKey(true); var cam = GetConfiguredCamera(); var pathname = ramdiskPath + "video.mp4"; Directory.CreateDirectory(ramdiskPath); File.Delete(pathname); Console.WriteLine("Preparing pipeline..."); cam.ConfigureCameraSettings(); using (var ffmpeg = new ExternalProcessCaptureHandler( new ExternalProcessCaptureHandlerOptions { Filename = "ffmpeg", Arguments = $"-framerate 24 -i - -b:v 2500k -c copy {pathname}", EchoOutput = true, DrainOutputDelayMs = 500, // default TerminationSignals = new[] { Signum.SIGINT, Signum.SIGQUIT }, // not the supposedly-correct SIGINT+SIGINT but this produces some exit output })) { // quality arg-help says set bitrate zero to use quality for VBR var portCfg = new MMALPortConfig(MMALEncoding.H264, MMALEncoding.I420, quality: 10, bitrate: 0, timeout: null); using var encoder = new MMALVideoEncoder(); using var renderer = new MMALVideoRenderer(); encoder.ConfigureOutputPort(portCfg, ffmpeg); cam.Camera.VideoPort.ConnectTo(encoder); cam.Camera.PreviewPort.ConnectTo(renderer); Console.WriteLine("Camera warmup..."); await Task.Delay(2000); Console.WriteLine($"Capturing MP4: {pathname}"); var timerToken = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)); await Task.WhenAll(new Task[] { ffmpeg.ManageProcessLifecycleAsync(timerToken.Token), cam.ProcessAsync(cam.Camera.VideoPort, timerToken.Token), }).ConfigureAwait(false); } // can't use the convenient fall-through using or MMALCamera.Cleanup // throws: Argument is invalid. Unable to destroy component cam.Cleanup(); Console.WriteLine("Exiting. Remember, video.mp4 is not valid."); }
static async Task stream(int seconds) { var cam = GetConfiguredCamera(); MMALCameraConfig.VideoResolution = new MMALSharp.Common.Utility.Resolution(640, 480); MMALCameraConfig.SensorMode = MMALSensorMode.Mode7; // for some reason mode 6 has a pinkish tinge MMALCameraConfig.VideoFramerate = new MMAL_RATIONAL_T(20, 1); Console.WriteLine("Preparing pipeline..."); cam.ConfigureCameraSettings(); // note cvlc requires real quotes even though we used apostrophes for the command line equivalent using (var vlc = new ExternalProcessCaptureHandler( new ExternalProcessCaptureHandlerOptions { Filename = "cvlc", Arguments = @"stream:///dev/stdin --sout ""#transcode{vcodec=mjpg,vb=2500,fps=20,acodec=none}:standard{access=http{mime=multipart/x-mixed-replace;boundary=7b3cc56e5f51db803f790dad720ed50a},mux=mpjpeg,dst=:8554/}"" :demux=h264", EchoOutput = true, DrainOutputDelayMs = 500, // default TerminationSignals = ExternalProcessCaptureHandlerOptions.signalsVLC })) { var portCfg = new MMALPortConfig(MMALEncoding.H264, MMALEncoding.I420, quality: 0, bitrate: MMALVideoEncoder.MaxBitrateMJPEG, timeout: null); using var encoder = new MMALVideoEncoder(); using var renderer = new MMALVideoRenderer(); encoder.ConfigureOutputPort(portCfg, vlc); cam.Camera.VideoPort.ConnectTo(encoder); cam.Camera.PreviewPort.ConnectTo(renderer); Console.WriteLine("Camera warmup..."); await Task.Delay(2000); Console.WriteLine($"Streaming MJPEG for {seconds} sec to:"); Console.WriteLine($"http://{Environment.MachineName}.local:8554/"); var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)); await Task.WhenAll(new Task[] { vlc.ManageProcessLifecycleAsync(timeout.Token), cam.ProcessAsync(cam.Camera.VideoPort, timeout.Token), }).ConfigureAwait(false); } // can't use the convenient fall-through using or MMALCamera.Cleanup // throws: Argument is invalid. Unable to destroy component cam.Cleanup(); Console.WriteLine("Exiting."); }