714ff4aa3a9ae52c890806f284856c58f06f4ddd
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / tests / examples / d3d11 / d3d11videosink-present.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/gst.h>
26 #include <gst/video/video.h>
27 #include <gst/d3d11/gstd3d11.h>
28 #include <d2d1.h>
29 #include <dwrite.h>
30 #include <wrl.h>
31 #include <string>
32 #include <queue>
33
34 /* *INDENT-OFF* */
35 using namespace Microsoft::WRL;
36
37 struct DisplayContext
38 {
39   HWND window_handle = nullptr;
40   GstElement *pipeline = nullptr;
41   GstElement *sink = nullptr;
42   GIOChannel *io_ch = nullptr;
43
44   bool enable_overlay = false;
45
46   ID2D1Factory *d2d_factory = nullptr;
47   IDWriteFactory *dwrite_factory = nullptr;
48
49   IDWriteTextFormat *format = nullptr;
50   IDWriteTextLayout *layout = nullptr;
51
52   /* D3D objects for background redraw with alpha blending */
53   ID3D11BlendState *blend = nullptr;
54   ID3D11PixelShader *ps = nullptr;
55   ID3D11VertexShader *vs = nullptr;
56   ID3D11InputLayout *input_layout = nullptr;
57   ID3D11Buffer *index_buf = nullptr;
58   ID3D11Buffer *vertex_buf = nullptr;
59
60   UINT width = 0;
61   UINT height = 0;
62
63   SRWLOCK lock = RTL_SRWLOCK_INIT;
64   double avg_framerate = 0;
65   double last_framerate = 0;
66
67   LARGE_INTEGER frequency;
68   std::queue < LARGE_INTEGER > render_timestamp;
69
70   GMainLoop *loop = nullptr;
71 };
72
73 static const gchar templ_vs_color[] =
74     "struct VS_INPUT {\n"
75     "  float4 Position: POSITION;\n"
76     "  float4 Color: COLOR;\n"
77     "};\n"
78     "struct VS_OUTPUT {\n"
79     "  float4 Position: SV_POSITION;\n"
80     "  float4 Color: COLOR;\n"
81     "};\n"
82     "VS_OUTPUT main (VS_INPUT input)\n"
83     "{\n"
84     "  return input;\n"
85     "}";
86
87 static const gchar templ_ps_color[] =
88     "struct PS_INPUT {\n"
89     "  float4 Position: SV_POSITION;\n"
90     "  float4 Color: COLOR;\n"
91     "};\n"
92     "float4 main(PS_INPUT input) : SV_TARGET\n"
93     "{\n"
94     "  return input.Color;\n"
95     "}";
96 /* *INDENT-ON* */
97
98 #define DISPLAY_CONTEXT_PROP "d3d11videosink.example.context"
99
100 #define CLEAR_COM(obj) do { \
101   if (obj) { \
102     obj->Release (); \
103     obj = nullptr; \
104   } \
105 } while (0)
106
107 static LRESULT CALLBACK
108 window_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
109 {
110   DisplayContext *context = (DisplayContext *) GetPropA (hwnd,
111       DISPLAY_CONTEXT_PROP);
112
113   switch (message) {
114     case WM_DESTROY:
115       gst_println ("Window is destroying");
116       if (context) {
117         context->window_handle = nullptr;
118         RemovePropA (hwnd, DISPLAY_CONTEXT_PROP);
119         g_main_loop_quit (context->loop);
120       }
121       break;
122     case WM_LBUTTONUP:
123       if (!context) {
124         gst_printerrln ("Display context is not attached on HWND");
125       } else {
126         context->enable_overlay = !context->enable_overlay;
127         gst_println ("Enable overlay %d", context->enable_overlay);
128         /* Call expose method so that videosink can immediately
129          * redraw client area */
130         if (context->sink)
131           gst_video_overlay_expose (GST_VIDEO_OVERLAY (context->sink));
132       }
133       break;
134     default:
135       break;
136   }
137
138   return DefWindowProc (hwnd, message, wParam, lParam);
139 }
140
141 static gboolean
142 bus_msg (GstBus * bus, GstMessage * msg, DisplayContext * context)
143 {
144   switch (GST_MESSAGE_TYPE (msg)) {
145     case GST_MESSAGE_ERROR:{
146       GError *err;
147       gchar *dbg;
148
149       gst_message_parse_error (msg, &err, &dbg);
150       gst_printerrln ("ERROR %s", err->message);
151       if (dbg)
152         gst_printerrln ("ERROR debug information: %s", dbg);
153       g_clear_error (&err);
154       g_free (dbg);
155
156       g_main_loop_quit (context->loop);
157       break;
158     }
159     case GST_MESSAGE_EOS:
160       gst_println ("Got EOS");
161       g_main_loop_quit (context->loop);
162       break;
163     default:
164       break;
165   }
166
167   return TRUE;
168 }
169
170 static gboolean
171 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
172 {
173   MSG msg;
174
175   if (!PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE))
176     return G_SOURCE_CONTINUE;
177
178   TranslateMessage (&msg);
179   DispatchMessage (&msg);
180
181   return G_SOURCE_CONTINUE;
182 }
183
184 typedef struct
185 {
186   struct
187   {
188     FLOAT x;
189     FLOAT y;
190     FLOAT z;
191   } position;
192   struct
193   {
194     FLOAT r;
195     FLOAT g;
196     FLOAT b;
197     FLOAT a;
198   } color;
199 } VertexData;
200
201 static void
202 ensure_d3d11_resource (DisplayContext * context, GstD3D11Device * device)
203 {
204   D3D11_BLEND_DESC blend_desc;
205   D3D11_INPUT_ELEMENT_DESC input_desc[2];
206   D3D11_BUFFER_DESC buffer_desc;
207   D3D11_MAPPED_SUBRESOURCE map;
208   VertexData *vertex_data;
209   WORD *indices;
210   HRESULT hr;
211   ID3D11Device *device_handle;
212   ID3D11DeviceContext *context_handle;
213
214   if (context->blend)
215     return;
216
217   device_handle = gst_d3d11_device_get_device_handle (device);
218   context_handle = gst_d3d11_device_get_device_context_handle (device);
219
220   ZeroMemory (&blend_desc, sizeof (blend_desc));
221   ZeroMemory (input_desc, sizeof (input_desc));
222   ZeroMemory (&buffer_desc, sizeof (buffer_desc));
223
224   input_desc[0].SemanticName = "POSITION";
225   input_desc[0].SemanticIndex = 0;
226   input_desc[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
227   input_desc[0].InputSlot = 0;
228   input_desc[0].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
229   input_desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
230   input_desc[0].InstanceDataStepRate = 0;
231
232   input_desc[1].SemanticName = "COLOR";
233   input_desc[1].SemanticIndex = 0;
234   input_desc[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
235   input_desc[1].InputSlot = 0;
236   input_desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
237   input_desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
238   input_desc[1].InstanceDataStepRate = 0;
239
240   hr = gst_d3d11_create_vertex_shader_simple (device, templ_vs_color,
241       "main", input_desc, G_N_ELEMENTS (input_desc), &context->vs,
242       &context->input_layout);
243   g_assert (SUCCEEDED (hr));
244
245   hr = gst_d3d11_create_pixel_shader_simple (device,
246       templ_ps_color, "main", &context->ps);
247
248   buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
249   buffer_desc.ByteWidth = sizeof (VertexData) * 4;
250   buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
251   buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
252
253   hr = device_handle->CreateBuffer (&buffer_desc, nullptr,
254       &context->vertex_buf);
255   g_assert (SUCCEEDED (hr));
256
257   buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
258   buffer_desc.ByteWidth = sizeof (WORD) * 6;
259   buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
260   buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
261
262   hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &context->index_buf);
263   g_assert (SUCCEEDED (hr));
264
265   blend_desc.AlphaToCoverageEnable = FALSE;
266   blend_desc.IndependentBlendEnable = FALSE;
267   blend_desc.RenderTarget[0].BlendEnable = TRUE;
268   blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
269   blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
270   blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
271   blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
272   blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
273   blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
274   blend_desc.RenderTarget[0].RenderTargetWriteMask =
275       D3D11_COLOR_WRITE_ENABLE_ALL;
276
277   hr = device_handle->CreateBlendState (&blend_desc, &context->blend);
278   g_assert (SUCCEEDED (hr));
279
280   hr = context_handle->Map (context->vertex_buf, 0, D3D11_MAP_WRITE_DISCARD, 0,
281       &map);
282   g_assert (SUCCEEDED (hr));
283   vertex_data = (VertexData *) map.pData;
284
285   hr = context_handle->Map (context->index_buf, 0, D3D11_MAP_WRITE_DISCARD, 0,
286       &map);
287   g_assert (SUCCEEDED (hr));
288   indices = (WORD *) map.pData;
289   for (guint i = 0; i < 4; i++) {
290     vertex_data[i].color.r = 0.0f;
291     vertex_data[i].color.g = 0.5f;
292     vertex_data[i].color.b = 0.5f;
293     vertex_data[i].color.a = 0.5f;
294   }
295
296   /* bottom left */
297   vertex_data[0].position.x = -1.0f;
298   vertex_data[0].position.y = -1.0f;
299   vertex_data[0].position.z = 0.0f;
300
301   /* top left */
302   vertex_data[1].position.x = -1.0f;
303   vertex_data[1].position.y = 1.0f;
304   vertex_data[1].position.z = 0.0f;
305
306   /* top right */
307   vertex_data[2].position.x = 1.0f;
308   vertex_data[2].position.y = 1.0f;
309   vertex_data[2].position.z = 0.0f;
310
311   /* bottom right */
312   vertex_data[3].position.x = 1.0f;
313   vertex_data[3].position.y = -1.0f;
314   vertex_data[3].position.z = 0.0f;
315
316   /* clockwise indexing */
317   indices[0] = 0;               /* bottom left */
318   indices[1] = 1;               /* top left */
319   indices[2] = 2;               /* top right */
320
321   indices[3] = 3;               /* bottom right */
322   indices[4] = 0;               /* bottom left  */
323   indices[5] = 2;               /* top right */
324
325   context_handle->Unmap (context->vertex_buf, 0);
326   context_handle->Unmap (context->index_buf, 0);
327 }
328
329 /* This callback will be called with gst_d3d11_device_lock() taken by
330  * d3d11videosink. We can perform GPU operation here safely */
331 static void
332 on_present (GstElement * sink, GstD3D11Device * device,
333     ID3D11RenderTargetView * rtv, DisplayContext * context)
334 {
335   ComPtr < ID3D11Resource > resource;
336   ComPtr < ID3D11Texture2D > texture;
337   ComPtr < IDXGISurface > surface;
338   ComPtr < ID2D1RenderTarget > d2d_target;
339   ComPtr < ID2D1SolidColorBrush > text_brush;
340   ID3D11DeviceContext *device_context;
341   HRESULT hr;
342   D3D11_TEXTURE2D_DESC desc;
343   ID2D1Factory *d2d_factory;
344   double framerate;
345   D3D11_VIEWPORT viewport;
346   UINT vertex_stride = sizeof (VertexData);
347   UINT offsets = 0;
348
349   if (!context->enable_overlay)
350     return;
351
352   rtv->GetResource (&resource);
353   hr = resource.As (&texture);
354   g_assert (SUCCEEDED (hr));
355
356   hr = texture.As (&surface);
357   g_assert (SUCCEEDED (hr));
358
359   texture->GetDesc (&desc);
360
361   ensure_d3d11_resource (context, device);
362   device_context = gst_d3d11_device_get_device_context_handle (device);
363
364   viewport.TopLeftX = 0;
365   viewport.TopLeftY = 0;
366   viewport.Width = desc.Width;
367   viewport.Height = desc.Height / 5.0f;
368   viewport.MinDepth = 0.0f;
369   viewport.MaxDepth = 1.0f;
370
371   /* Draw background using D3D11 */
372   device_context->IASetPrimitiveTopology
373       (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
374   device_context->IASetInputLayout (context->input_layout);
375   device_context->IASetVertexBuffers (0, 1, &context->vertex_buf,
376       &vertex_stride, &offsets);
377   device_context->IASetIndexBuffer (context->index_buf, DXGI_FORMAT_R16_UINT,
378       0);
379   device_context->VSSetShader (context->vs, nullptr, 0);
380   device_context->PSSetShader (context->ps, nullptr, 0);
381   device_context->RSSetViewports (1, &viewport);
382   device_context->OMSetRenderTargets (1, &rtv, nullptr);
383   device_context->OMSetBlendState (context->blend, nullptr, 0xffffffff);
384   device_context->DrawIndexed (6, 0, 0);
385
386   /* Creates new layout on window size or framerate change */
387   AcquireSRWLockExclusive (&context->lock);
388   framerate = context->avg_framerate;
389   if (context->layout && (context->width != desc.Width ||
390           context->height != desc.Height
391           || context->last_framerate != framerate)) {
392     CLEAR_COM (context->layout);
393   }
394   context->last_framerate = framerate;
395   ReleaseSRWLockExclusive (&context->lock);
396
397   context->width = desc.Width;
398   context->height = desc.Height;
399
400   if (!context->layout) {
401     IDWriteFactory *factory = context->dwrite_factory;
402     std::wstring overlay_string;
403     wchar_t fps_buf[128];
404     DWRITE_TEXT_METRICS metrics;
405     FLOAT font_size;
406     bool was_decreased = false;
407     DWRITE_TEXT_RANGE range;
408
409     overlay_string = L"Text Overlay, FPS: ";
410     std::swprintf (fps_buf, L"%.1f", framerate);
411
412     overlay_string += fps_buf;
413
414     hr = factory->CreateTextLayout (overlay_string.c_str (),
415         overlay_string.length (),
416         context->format, desc.Width, desc.Height / 5.0f, &context->layout);
417     g_assert (SUCCEEDED (hr));
418
419     context->layout->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_CENTER);
420     context->layout->SetParagraphAlignment (DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
421
422     range.startPosition = 0;
423     range.length = overlay_string.length ();
424
425     /* Calculate best font size */
426     do {
427       hr = context->layout->GetMetrics (&metrics);
428       g_assert (SUCCEEDED (hr));
429
430       context->layout->GetFontSize (0, &font_size);
431       if (metrics.widthIncludingTrailingWhitespace >= (FLOAT) desc.Width) {
432         if (font_size > 1.0f) {
433           font_size -= 0.5f;
434           was_decreased = true;
435           hr = context->layout->SetFontSize (font_size, range);
436           g_assert (SUCCEEDED (hr));
437           continue;
438         }
439
440         break;
441       }
442
443       if (was_decreased)
444         break;
445
446       if (metrics.widthIncludingTrailingWhitespace < (FLOAT) desc.Width) {
447         if (metrics.widthIncludingTrailingWhitespace >= desc.Width * 0.7)
448           break;
449
450         font_size += 0.5f;
451         hr = context->layout->SetFontSize (font_size, range);
452         g_assert (SUCCEEDED (hr));
453         continue;
454       }
455     } while (true);
456   }
457
458   d2d_factory = context->d2d_factory;
459   D2D1_RENDER_TARGET_PROPERTIES props;
460   props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
461   props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
462   props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
463   /* default DPI */
464   props.dpiX = 0;
465   props.dpiY = 0;
466   props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
467   props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
468
469   /* Creates D2D render target using swapchin's backbuffer */
470   hr = d2d_factory->CreateDxgiSurfaceRenderTarget (surface.Get (), props,
471       &d2d_target);
472   g_assert (SUCCEEDED (hr));
473
474   /* text brush */
475   hr = d2d_target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black),
476       &text_brush);
477   g_assert (SUCCEEDED (hr));
478
479   D2D1_RECT_F rect;
480   rect.top = 0;
481   rect.bottom = 0;
482   rect.right = desc.Width;
483   rect.bottom = desc.Height / 5.0f;
484
485   d2d_target->BeginDraw ();
486   /* Draw text */
487   d2d_target->DrawTextLayout (D2D1::Point2F (0, 0),
488       context->layout, text_brush.Get (), D2D1_DRAW_TEXT_OPTIONS_NONE);
489   d2d_target->EndDraw ();
490 }
491
492 static GstPadProbeReturn
493 framerate_calculate_probe (GstPad * pad, GstPadProbeInfo * info,
494     DisplayContext * context)
495 {
496   LARGE_INTEGER now;
497
498   AcquireSRWLockExclusive (&context->lock);
499
500   QueryPerformanceCounter (&now);
501   context->render_timestamp.push (now);
502
503   if (context->render_timestamp.size () > 10) {
504     LARGE_INTEGER last = context->render_timestamp.back ();
505     LARGE_INTEGER first = context->render_timestamp.front ();
506     double diff = last.QuadPart - first.QuadPart;
507     context->avg_framerate =
508         (double) context->frequency.QuadPart *
509         (context->render_timestamp.size () - 1) / diff;
510
511     std::queue < LARGE_INTEGER > empty_queue;
512     std::swap (context->render_timestamp, empty_queue);
513   }
514
515 out:
516   ReleaseSRWLockExclusive (&context->lock);
517
518   return GST_PAD_PROBE_OK;
519 }
520
521 gint
522 main (gint argc, gchar ** argv)
523 {
524   WNDCLASSEXA wc = { 0, };
525   HINSTANCE hinstance = GetModuleHandle (nullptr);
526   GOptionContext *option_ctx;
527   GError *error = nullptr;
528   RECT wr = { 0, 0, 320, 240 };
529   gboolean ret;
530   gchar *uri = nullptr;
531   GOptionEntry options[] = {
532     {"uri", 0, 0, G_OPTION_ARG_STRING, &uri, "URI to play", nullptr},
533     {nullptr}
534   };
535   HRESULT hr;
536   DisplayContext context;
537   GstPad *pad;
538
539   option_ctx =
540       g_option_context_new ("d3d11videosink \"present\" signal example");
541   g_option_context_add_main_entries (option_ctx, options, nullptr);
542   g_option_context_add_group (option_ctx, gst_init_get_option_group ());
543   ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
544   g_option_context_free (option_ctx);
545
546   if (!ret) {
547     gst_printerrln ("option parsing failed: %s", error->message);
548     g_clear_error (&error);
549     return 1;
550   }
551
552   if (!uri) {
553     gst_printerrln ("File name or URI must be provided");
554     return 1;
555   }
556
557   if (!gst_uri_is_valid (uri)) {
558     gchar *file = gst_filename_to_uri (uri, nullptr);
559     g_free (uri);
560     uri = file;
561   }
562
563   if (!uri) {
564     gst_printerrln ("No valid URI");
565     return 1;
566   }
567
568   /* Prepare device independent D2D objects */
569   hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED,
570       IID_PPV_ARGS (&context.d2d_factory));
571   if (FAILED (hr)) {
572     gst_printerrln ("Couldn't create D2D factory");
573     return 1;
574   }
575
576   hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
577       __uuidof (context.dwrite_factory),
578       reinterpret_cast < IUnknown ** >(&context.dwrite_factory));
579   if (FAILED (hr)) {
580     gst_printerrln ("Couldn't create DirectWrite factory");
581     return 1;
582   }
583
584   /* Font size will be re-calculated on present */
585   hr = context.dwrite_factory->CreateTextFormat (L"Consolas", nullptr,
586       DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL,
587       DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &context.format);
588
589   /* For rendered framerate calculation */
590   QueryPerformanceFrequency (&context.frequency);
591
592   context.loop = g_main_loop_new (nullptr, FALSE);
593
594   wc.cbSize = sizeof (WNDCLASSEXA);
595   wc.style = CS_HREDRAW | CS_VREDRAW;
596   wc.lpfnWndProc = (WNDPROC) window_proc;
597   wc.hInstance = hinstance;
598   wc.hCursor = LoadCursor (nullptr, IDC_ARROW);
599   wc.lpszClassName = "GstD3D11VideoSinkExample";
600   RegisterClassExA (&wc);
601
602   AdjustWindowRect (&wr, WS_OVERLAPPEDWINDOW, FALSE);
603   context.window_handle =
604       CreateWindowExA (0, wc.lpszClassName, "GstD3D11VideoSinkExample",
605       WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
606       CW_USEDEFAULT, CW_USEDEFAULT,
607       wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr,
608       hinstance, nullptr);
609
610   context.io_ch = g_io_channel_win32_new_messages (0);
611   g_io_add_watch (context.io_ch, G_IO_IN, msg_cb, context.window_handle);
612
613   context.pipeline = gst_element_factory_make ("playbin", nullptr);
614   g_assert (context.pipeline);
615
616   context.sink = gst_element_factory_make ("d3d11videosink", nullptr);
617   g_assert (context.sink);
618
619   /* Enables present signal */
620   g_object_set (context.sink, "emit-present", TRUE, nullptr);
621
622   /* D2D <-> DXGI interop requires BGRA format */
623   g_object_set (context.sink, "display-format", DXGI_FORMAT_B8G8R8A8_UNORM,
624       nullptr);
625
626   g_signal_connect (context.sink, "present", G_CALLBACK (on_present), &context);
627   gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (context.sink),
628       (guintptr) context.window_handle);
629
630   /* Attach our display context on HWND */
631   SetPropA (context.window_handle, DISPLAY_CONTEXT_PROP, &context);
632
633   g_object_set (context.pipeline,
634       "uri", uri, "video-sink", context.sink, nullptr);
635   gst_bus_add_watch (GST_ELEMENT_BUS (context.pipeline),
636       (GstBusFunc) bus_msg, &context);
637
638   pad = gst_element_get_static_pad (context.sink, "sink");
639   gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
640       (GstPadProbeCallback) framerate_calculate_probe, &context, nullptr);
641
642   if (gst_element_set_state (context.pipeline, GST_STATE_PLAYING) ==
643       GST_STATE_CHANGE_FAILURE) {
644     gst_printerrln ("Could not set state to playing for uri %s", uri);
645     return 1;
646   }
647
648   ShowWindow (context.window_handle, SW_SHOW);
649   gst_println ("Click window client area to toggle overlay");
650
651   g_main_loop_run (context.loop);
652
653   gst_element_set_state (context.pipeline, GST_STATE_NULL);
654   gst_bus_remove_watch (GST_ELEMENT_BUS (context.pipeline));
655   gst_object_unref (context.pipeline);
656   g_io_channel_unref (context.io_ch);
657
658   if (context.window_handle)
659     DestroyWindow (context.window_handle);
660
661   CLEAR_COM (context.blend);
662   CLEAR_COM (context.ps);
663   CLEAR_COM (context.vs);
664   CLEAR_COM (context.input_layout);
665   CLEAR_COM (context.index_buf);
666   CLEAR_COM (context.vertex_buf);
667
668   CLEAR_COM (context.layout);
669
670   context.format->Release ();
671   context.d2d_factory->Release ();
672   context.dwrite_factory->Release ();
673
674   g_main_loop_unref (context.loop);
675
676   return 0;
677 }