aboutsummaryrefslogtreecommitdiff
path: root/plugins/rtp/src/video_widget.vala
blob: 76e4dcca8997404ee8d7bc6c5c57ce6d852565b3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#if !VALA_0_52
[CCode (cheader_filename = "gst/gst.h")]
private static extern void gst_value_set_fraction(ref GLib.Value value, int numerator, int denominator);
#endif

public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidget {
    private static uint last_id = 0;

    public uint id { get; private set; }
    public Gst.Element element { get; private set; }
    public Gtk.Widget widget { get; private set; }

    public Plugin plugin { get; private set; }
    public Gst.Pipeline pipe { get {
        return plugin.pipe;
    }}

    private bool attached;
    private Device? connected_device;
    private Gst.Element? connected_device_element;
    private Stream? connected_stream;
    private Gst.Element prepare;

    public VideoWidget(Plugin plugin) {
        this.plugin = plugin;

        id = last_id++;
        element = Gst.ElementFactory.make("gtksink", @"video_widget_$id");
        if (element != null) {
            Gtk.Widget widget;
            element.@get("widget", out widget);
            element.@set("async", false);
            element.@set("sync", true);
            this.widget = widget;
            add(widget);
            widget.visible = true;
        } else {
            warning("Could not create GTK video sink. Won't display videos.");
        }
        size_allocate.connect_after(after_size_allocate);
    }

    public void input_caps_changed(GLib.Object pad, ParamSpec spec) {
        Gst.Caps? caps = (pad as Gst.Pad).caps;
        if (caps == null) return;

        int width, height;
        caps.get_structure(0).get_int("width", out width);
        caps.get_structure(0).get_int("height", out height);
        resolution_changed(width, height);
    }

    public void after_size_allocate(Gtk.Allocation allocation) {
        if (prepare != null) {
            Gst.Element crop = ((Gst.Bin)prepare).get_by_name(@"video_widget_$(id)_crop");
            if (crop != null) {
                Value ratio = new Value(typeof(Gst.Fraction));
#if VALA_0_52
                Gst.Value.set_fraction(ref ratio, allocation.width, allocation.height);
#else
                gst_value_set_fraction(ref ratio, allocation.width, allocation.height);
#endif
                crop.set_property("aspect-ratio", ratio);
            }
        }
    }

    public void display_stream(Xmpp.Xep.JingleRtp.Stream stream, Xmpp.Jid jid) {
        if (element == null) return;
        detach();
        if (stream.media != "video") return;
        connected_stream = stream as Stream;
        if (connected_stream == null) return;
        plugin.pause();
        pipe.add(element);
        prepare = Gst.parse_bin_from_description(@"aspectratiocrop aspect-ratio=4/3 name=video_widget_$(id)_crop ! videoconvert name=video_widget_$(id)_convert", true);
        prepare.name = @"video_widget_$(id)_prepare";
        prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
        pipe.add(prepare);
        connected_stream.add_output(prepare);
        prepare.link(element);
        element.set_locked_state(false);
        plugin.unpause();
        attached = true;
    }

    public void display_device(MediaDevice media_device) {
        if (element == null) return;
        detach();
        connected_device = media_device as Device;
        if (connected_device == null) return;
        plugin.pause();
        pipe.add(element);
        prepare = Gst.parse_bin_from_description(@"aspectratiocrop aspect-ratio=4/3 name=video_widget_$(id)_crop ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
        prepare.name = @"video_widget_$(id)_prepare";
        prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
        pipe.add(prepare);
        connected_device_element = connected_device.link_source();
        connected_device_element.link(prepare);
        prepare.link(element);
        element.set_locked_state(false);
        plugin.unpause();
        attached = true;
    }

    public void detach() {
        if (element == null) return;
        if (attached) {
            if (connected_stream != null) {
                connected_stream.remove_output(prepare);
                connected_stream = null;
            }
            if (connected_device != null) {
                connected_device_element.unlink(element);
                connected_device_element = null;
                connected_device.unlink();
                connected_device = null;
            }
            prepare.set_locked_state(true);
            prepare.set_state(Gst.State.NULL);
            pipe.remove(prepare);
            prepare = null;
            element.set_locked_state(true);
            element.set_state(Gst.State.NULL);
            pipe.remove(element);
            attached = false;
        }
    }

    public override void dispose() {
        detach();
        widget = null;
        element = null;
    }
}