From aa71e1d0e985f92086eac59f7c3e2eaef6650b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Canek=20Pel=C3=A1ez=20Vald=C3=A9s?= Date: Sun, 26 Apr 2020 11:45:54 -0500 Subject: [PATCH] Camera: Truly dynamic pipeline. --- src/camera.vala | 393 ++++++++++++++++++++++-------------------------- 1 file changed, 182 insertions(+), 211 deletions(-) diff --git a/src/camera.vala b/src/camera.vala index b9015b8..9540788 100644 --- a/src/camera.vala +++ b/src/camera.vala @@ -40,6 +40,7 @@ namespace Queso { /* The video and audio devices. */ private Devices devices; private VideoDevice videodev; + private VideoDevice new_videodev; private AudioDevice audiodev; private VideoSetup video_setup; private AudioSetup audio_setup; @@ -51,34 +52,29 @@ namespace Queso { private Gst.Element vfilter; private Gst.Element vq1; private Gst.Element vc; + private Gst.Element vc2; + private Gst.Element x264enc; private Gst.Element text; private Gst.Element info; private Gst.Element gtksink; + private Gst.Element vq2; + private Gst.Element vq3; + private Gst.Element tee; + private Gst.Element mp4mux; + private Gst.Element filesink; // private Gst.Element asrc; // private Gst.Element afilter; // private Gst.Element parse; // private Gst.Element aq1; // private Gst.Element aq2; - // private Gst.Element vq2; - // private Gst.Element tee; // private Gst.Element dec; // private Gst.Element ar; // private Gst.Element ac; // private Gst.Element aenc; - // private Gst.Element mp4mux; - // private Gst.Element filesink; - - /* Start/stop saving logic. */ - // private Gst.Pad aq2_pad; - // private Gst.Pad vq1_pad; - // private Gst.Pad vq2_pad; - // private ulong aq2_pad_probe_id; - // private ulong vq2_pad_probe_id; - // private int buffer_count; - // private string temporary_fn; - // private string destination_fn; - // private bool video_ready; + + /* Temporary filename . */ + private string temporary_fn; /* Timestamps. */ private double start_saving_ts; @@ -97,7 +93,6 @@ namespace Queso { video_setup = videodev.setups.first(); audio_setup = audiodev.setups.first(); - // temporary_fn = random_filename(); pipeline = new Gst.Pipeline("queso"); // asrc = audiodev.create_element("asrc"); @@ -123,17 +118,6 @@ namespace Queso { // aenc = Gst.ElementFactory.make("avenc_aac", "aenc"); // parse = Gst.ElementFactory.make("h264parse", "parse"); // parse.set("config_interval", -1); - // tee = Gst.ElementFactory.make("tee", "tee"); - // dec = Gst.ElementFactory.make("avdec_h264", "dec"); - // vq2 = Gst.ElementFactory.make("queue", "vq2"); - // vq2.set("max-size-time", 3 * Gst.SECOND); - // vq2.set("max-size-bytes", 0); - // vq2.set("max-size-buffers", 0); - // vq2.set("leaky", 2); - // mp4mux = Gst.ElementFactory.make("mpegtsmux", "mp4mux"); - // filesink = Gst.ElementFactory.make("filesink", "filesink"); - // filesink.set("async", false); - // filesink.set("location", temporary_fn); vsrc = videodev.create_element("vsrc"); vfilter = Gst.ElementFactory.make("capsfilter", "vfilter"); @@ -166,39 +150,21 @@ namespace Queso { bus = pipeline.get_bus(); - // vq1_pad = vq1.get_static_pad("src"); - // vq2_pad = vq2.get_static_pad("src"); - // aq2_pad = aq2.get_static_pad("src"); - - // aq2_pad_probe_id = aq2_pad.add_probe( - // Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, - // saving_block_probe); - // vq2_pad_probe_id = vq2_pad.add_probe( - // Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, - // saving_block_probe); - - // bus.add_watch(GLib.Priority.DEFAULT, pipeline_bus_watch); + bus.add_watch(GLib.Priority.DEFAULT, pipeline_bus_watch); } /** * Cleans the camera and stops the pipeline. */ public void clean() { - // pipeline.send_event(new Gst.Event.eos()); - // filesink.set_state(Gst.State.NULL); - // mp4mux.set_state(Gst.State.NULL); - // filesink.set("location", Devices.DEV_NULL); - // if (saving) { - // destination_fn = get_destination(); - // move_file(temporary_fn, destination_fn); - // destination_fn = null; - // } - // GLib.File tmp = GLib.File.new_for_path(temporary_fn); - // try { - // tmp.delete(); - // } catch (GLib.Error e) { - // /* Nothing to do. */ - // } + pipeline.send_event(new Gst.Event.eos()); + filesink.set_state(Gst.State.NULL); + mp4mux.set_state(Gst.State.NULL); + if (saving) { + var destination_fn = get_destination(); + move_file(temporary_fn, destination_fn); + destination_fn = temporary_fn = null; + } } /** @@ -209,10 +175,12 @@ namespace Queso { if (saving) { start_saving_ts = last_saving_ts = Timestamp.get_timestamp(); text.set("text", "•REC 00:00:00"); + text.set("halignment", 2); + text.set("valignment", 2); GLib.Idle.add(update_counter); - // start_saving(); + start_saving(); } else { - // stop_saving(); + stop_saving(); text.set("text", ""); } } @@ -231,34 +199,6 @@ namespace Queso { GLib.Idle.add(stop_camera); } - /* Starts saving. */ - // private void start_saving() { - // video_ready = false; - // buffer_count = 0; - - // vq2_pad.add_probe(Gst.PadProbeType.BUFFER, wait_keyframe_probe); - // vq2_pad.remove_probe(vq2_pad_probe_id); - // vq2_pad_probe_id = 0; - - // aq2_pad.add_probe(Gst.PadProbeType.BUFFER, wait_video_probe); - // aq2_pad.remove_probe(aq2_pad_probe_id); - // aq2_pad_probe_id = 0; - // } - - /* Stops saving. */ - // private void stop_saving() { - // Gst.Debug.bin_to_dot_file(pipeline, - // Gst.DebugGraphDetails.ALL, - // "queso-pipeline"); - // aq2_pad_probe_id = aq2_pad.add_probe( - // Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, - // saving_block_probe); - // vq2_pad_probe_id = vq2_pad.add_probe( - // Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, - // saving_block_probe); - // new Thread("eos-push-thread", push_eos_thread); - // } - /* Starts the camera asynchronously. */ private bool start_camera() { return change_pipeline_state(Gst.State.PLAYING); @@ -270,75 +210,42 @@ namespace Queso { } /* Pipeline bus watch. */ - // private bool - // pipeline_bus_watch(Gst.Bus bus, Gst.Message message) { - // switch (message.type) { - // case Gst.MessageType.ELEMENT: - // unowned Gst.Structure structure = message.get_structure(); - // if (!structure.has_name("GstBinForwarded")) - // return GLib.Source.CONTINUE; - // Gst.Message forward_message = null; - // structure.get("message", typeof(Gst.Message), - // out forward_message); - // if (forward_message.type != Gst.MessageType.EOS) - // return GLib.Source.CONTINUE; - // filesink.set_state(Gst.State.NULL); - // mp4mux.set_state(Gst.State.NULL); - // var old_fn = temporary_fn; - // temporary_fn = random_filename(); - // filesink.set("location", temporary_fn); - // filesink.set_state(Gst.State.PLAYING); - // mp4mux.set_state(Gst.State.PLAYING); - // destination_fn = get_destination(); - // move_file(old_fn, destination_fn); - // destination_fn = null; - // return GLib.Source.CONTINUE; - // default: - // return GLib.Source.CONTINUE; - // } - // } - - // /* Saving block probe. */ - // private Gst.PadProbeReturn - // saving_block_probe(Gst.Pad pad, Gst.PadProbeInfo info) { - // return Gst.PadProbeReturn.OK; - // } - - // /* Wait keyframe probe. */ - // private Gst.PadProbeReturn - // wait_keyframe_probe(Gst.Pad pad, Gst.PadProbeInfo info) { - // Gst.Buffer buffer = (Gst.Buffer)info.data; - - // var flags = buffer.get_flags(); - // if ((flags & Gst.BufferFlags.DELTA_UNIT) == 0) { - // start_saving_ts = last_saving_ts = Timestamp.get_timestamp(); - // text.set("text", "•REC 00:00:00"); - // GLib.Idle.add(update_counter); - // video_ready = true; - // return Gst.PadProbeReturn.REMOVE; - // } else { - // return Gst.PadProbeReturn.DROP; - // } - // } - - // /* Drop one buffer probe. */ - // private Gst.PadProbeReturn - // wait_video_probe(Gst.Pad pad, Gst.PadProbeInfo info) { - // if (!video_ready) - // return Gst.PadProbeReturn.DROP; - // return Gst.PadProbeReturn.REMOVE; - // } - - // /* Pushes the EOS event. */ - // private void* - // push_eos_thread() { - // var vpeer = vq2_pad.get_peer(); - // var apeer = aq2_pad.get_peer(); - // pipeline.message_forward = true; - // vpeer.send_event(new Gst.Event.eos()); - // apeer.send_event(new Gst.Event.eos()); - // return null; - // } + private bool + pipeline_bus_watch(Gst.Bus bus, Gst.Message message) { + switch (message.type) { + case Gst.MessageType.ELEMENT: + unowned Gst.Structure structure = message.get_structure(); + if (!structure.has_name("GstBinForwarded")) + return GLib.Source.CONTINUE; + Gst.Message forward_message = null; + structure.get("message", typeof(Gst.Message), + out forward_message); + if (forward_message.type != Gst.MessageType.EOS) + return GLib.Source.CONTINUE; + stderr.printf("Finishing file\n"); + + tee.set_state(Gst.State.NULL); + vq2.set_state(Gst.State.NULL); + vq3.set_state(Gst.State.NULL); + vc2.set_state(Gst.State.NULL); + x264enc.set_state(Gst.State.NULL); + mp4mux.set_state(Gst.State.NULL); + filesink.set_state(Gst.State.NULL); + + pipeline.remove_many(tee, vq2, vq3, vc2, x264enc, + mp4mux, filesink); + var src_pad = vq1.get_static_pad("src"); + var sink_pad = vc.get_static_pad("sink"); + src_pad.link(sink_pad); + + var destination_fn = get_destination(); + move_file(temporary_fn, destination_fn); + temporary_fn = destination_fn = null; + return GLib.Source.CONTINUE; + default: + return GLib.Source.CONTINUE; + } + } // /* Update counter idle callback. */ private bool update_counter() { @@ -380,59 +287,106 @@ namespace Queso { return true; } - // /* Gets the final destination for the video. */ - // private string get_destination() { - // var now = new GLib.DateTime.now_local(); - // string sts = "%04d-%02d-%02d %02d:%02d:%02d".printf( - // now.get_year(), now.get_month(), now.get_day_of_month(), - // now.get_hour(), now.get_minute(), now.get_second()); - // var video_dir = GLib.Environment.get_user_special_dir( - // GLib.UserDirectory.VIDEOS); - // return string.join(GLib.Path.DIR_SEPARATOR_S, video_dir, - // "Queso-%s.mp4".printf(sts)); - // } - - // /* Gets a random filename. */ - // private string random_filename() { - // int ts = (int)Timestamp.get_timestamp(); - // return string.join(GLib.Path.DIR_SEPARATOR_S, - // GLib.Environment.get_tmp_dir(), - // "queso-%08X.mp4".printf(ts)); - // } - - // /* Moves a file. */ - // private void move_file(string src_path, string dest_path) { - // GLib.File src = GLib.File.new_for_path(src_path); - // GLib.File dest = GLib.File.new_for_path(dest_path); - - // try { - // src.move(dest, GLib.FileCopyFlags.NONE, null); - // } catch (GLib.Error e) { - // GLib.warning("Could not move “%s” to “%s”: %s", - // src_path, dest_path, e.message); - // } - // } - - private string get_info() { - var r = new StringBuilder(); - r.append("Video source: "); - r.append(videodev.name); - r.append("\n"); - r.append("Audio source: "); - r.append(audiodev.name); - return r.str; + /* Gets the final destination for the video. */ + private string get_destination() { + var now = new GLib.DateTime.now_local(); + string sts = "%04d-%02d-%02d %02d:%02d:%02d".printf( + now.get_year(), now.get_month(), now.get_day_of_month(), + now.get_hour(), now.get_minute(), now.get_second()); + var video_dir = GLib.Environment.get_user_special_dir( + GLib.UserDirectory.VIDEOS); + return string.join(GLib.Path.DIR_SEPARATOR_S, video_dir, + "Queso-%s.mp4".printf(sts)); } - public void toggle_info() { - show_info = !show_info; - if (show_info) { - info.set("text", get_info()); - } else { - info.set("text", ""); + /* Moves a file. */ + private void move_file(string src_path, string dest_path) { + GLib.File src = GLib.File.new_for_path(src_path); + GLib.File dest = GLib.File.new_for_path(dest_path); + + try { + src.move(dest, GLib.FileCopyFlags.NONE, null); + } catch (GLib.Error e) { + GLib.warning("Could not move “%s” to “%s”: %s", + src_path, dest_path, e.message); } } - private VideoDevice new_videodev; + private void start_saving() { + var pad = vq1.get_static_pad("src"); + pad.add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, + start_saving_block_probe); + } + + private void stop_saving() { + stderr.printf("stop_saving\n"); + var pad = vq1.get_static_pad("src"); + pad.add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, + stop_saving_block_probe); + } + + /* Pushes the EOS event. */ + private void* + push_eos_thread() { + stderr.printf("push_eos_thread\n"); + var pad = vq3.get_static_pad("src"); + var peer = pad.get_peer(); + pipeline.message_forward = true; + peer.send_event(new Gst.Event.eos()); + return null; + } + + /* Saving block probe. */ + private Gst.PadProbeReturn + stop_saving_block_probe(Gst.Pad pad, Gst.PadProbeInfo info) { + stderr.printf("stop_saving_block_probe: %s::%s\n", + pad.parent.name, pad.name); + pad.remove_probe(info.id); + var src = vq1.get_static_pad("src"); + var sink = tee.get_static_pad("sink"); + src.unlink(sink); + src = vq2.get_static_pad("src"); + sink = vc.get_static_pad("sink"); + src.unlink(sink); + src = vq1.get_static_pad("src"); + sink = vc.get_static_pad("sink"); + src.link(sink); + new Thread("eos-push-thread", push_eos_thread); + return Gst.PadProbeReturn.DROP; + } + + /* Change video source block probe. */ + private Gst.PadProbeReturn + start_saving_block_probe(Gst.Pad pad, Gst.PadProbeInfo info) { + pad.remove_probe(info.id); + temporary_fn = random_filename(); + + var src_pad = vq1.get_static_pad("src"); + var sink_pad = vc.get_static_pad("sink"); + + src_pad.unlink(sink_pad); + + tee = Gst.ElementFactory.make("tee", "tee"); + vq2 = Gst.ElementFactory.make("queue", "vq2"); + vq3 = Gst.ElementFactory.make("queue", "vq3"); + vq3.set("leaky", 2); + vc2 = Gst.ElementFactory.make("videoconvert", "vc2"); + x264enc = Gst.ElementFactory.make("x264enc", "x264enc"); + mp4mux = Gst.ElementFactory.make("mpegtsmux", "mp4mux"); + filesink = Gst.ElementFactory.make("filesink", "filesink"); + filesink.set("async", false); + filesink.set("location", temporary_fn); + + pipeline.add_many(tee, vq2, vq3, vc2, x264enc, mp4mux, filesink); + vq1.link(tee); + tee.link_many(vq2, vc); + tee.link_many(vq3, vc2, x264enc, mp4mux, filesink); + + new Thread("add-wait-keyframe-thread", + add_wait_keyframe_probe); + + return Gst.PadProbeReturn.DROP; + } private void device_changed(DeviceType device_type) { switch (device_type) { @@ -491,7 +445,7 @@ namespace Queso { pad.add_probe(Gst.PadProbeType.BLOCK | Gst.PadProbeType.BUFFER, wait_keyframe_probe); return null; - } + } private Gst.PadProbeReturn wait_keyframe_probe(Gst.Pad pad, Gst.PadProbeInfo info) { @@ -500,15 +454,13 @@ namespace Queso { if ((flags & Gst.BufferFlags.DELTA_UNIT) == 0) { start_saving_ts = last_saving_ts = Timestamp.get_timestamp(); text.set("text", ""); - text.set("halignment", 2); - text.set("valignment", 2); return Gst.PadProbeReturn.REMOVE; } else { return Gst.PadProbeReturn.DROP; } } - int debug_counter; + private int debug_counter; public void debug() { var output = "queso-pipeline-%03d".printf(debug_counter++); @@ -517,12 +469,31 @@ namespace Queso { output); } - // public void audio_device_changed() { - - // } + /* Gets a random filename. */ + private string random_filename() { + int ts = (int)Timestamp.get_timestamp(); + return string.join(GLib.Path.DIR_SEPARATOR_S, + GLib.Environment.get_tmp_dir(), + "queso-%08X.mp4".printf(ts)); + } + + private string get_info() { + var r = new StringBuilder(); + r.append("Video source: "); + r.append(videodev.name); + r.append("\n"); + r.append("Audio source: "); + r.append(audiodev.name); + return r.str; + } - // public void video_device_changed() { - - // } + public void toggle_info() { + show_info = !show_info; + if (show_info) { + info.set("text", get_info()); + } else { + info.set("text", ""); + } + } } } -- GitLab