1 /*
2  * GStreamer DirectShow codecs wrapper
3  * Copyright <2006, 2007, 2008, 2009, 2010> Fluendo <support@fluendo.com>
4  * Copyright <2006, 2007, 2008> Pioneers of the Inevitable <songbird@songbirdnest.com>
5  * Copyright <2007,2008> Sebastien Moutte <sebastien@moutte.net>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  *
25  * Alternatively, the contents of this file may be used under the
26  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27  * which case the following provisions apply instead of the ones
28  * mentioned above:
29  *
30  * This library is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU Library General Public
32  * License as published by the Free Software Foundation; either
33  * version 2 of the License, or (at your option) any later version.
34  *
35  * This library is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
38  * Library General Public License for more details.
39  *
40  * You should have received a copy of the GNU Library General Public
41  * License along with this library; if not, write to the
42  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
43  * Boston, MA 02110-1301, USA.
44  */
45 
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
49 
50 #include <dmoreg.h>
51 #include <wmcodecdsp.h>
52 
53 #include "gstdshowvideodec.h"
54 #include <gst/video/video.h>
55 
56 GST_DEBUG_CATEGORY_STATIC (dshowvideodec_debug);
57 #define GST_CAT_DEFAULT dshowvideodec_debug
58 
59 #define gst_dshowvideodec_parent_class parent_class
60 G_DEFINE_TYPE(GstDshowVideoDec, gst_dshowvideodec, GST_TYPE_ELEMENT)
61 
62 static void gst_dshowvideodec_finalize (GObject * object);
63 static GstStateChangeReturn gst_dshowvideodec_change_state
64     (GstElement * element, GstStateChange transition);
65 
66 /* sink pad overwrites */
67 static gboolean gst_dshowvideodec_sink_setcaps (GstPad * pad, GstCaps * caps);
68 static gboolean gst_dshowvideodec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
69 static GstFlowReturn gst_dshowvideodec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer);
70 
71 /* src pad overwrites */
72 static GstCaps *gst_dshowvideodec_src_getcaps (GstPad * pad);
73 static gboolean gst_dshowvideodec_src_setcaps (GstPad * pad, GstCaps * caps);
74 
75 /* utils */
76 static gboolean gst_dshowvideodec_create_graph_and_filters (GstDshowVideoDec *
77     vdec);
78 static gboolean gst_dshowvideodec_destroy_graph_and_filters (GstDshowVideoDec *
79     vdec);
80 static gboolean gst_dshowvideodec_flush (GstDshowVideoDec * adec);
81 static gboolean gst_dshowvideodec_get_filter_output_format (GstDshowVideoDec *
82     vdec, const GUID subtype, VIDEOINFOHEADER ** format, guint * size);
83 
84 
85 #define GUID_MEDIATYPE_VIDEO    {0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
86 #define GUID_MEDIASUBTYPE_WMVV1 {0x31564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
87 #define GUID_MEDIASUBTYPE_WMVV2 {0x32564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
88 #define GUID_MEDIASUBTYPE_WMVV3 {0x33564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
89 #define GUID_MEDIASUBTYPE_WMVP  {0x50564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
90 #define GUID_MEDIASUBTYPE_WMVA  {0x41564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
91 #define GUID_MEDIASUBTYPE_WVC1  {0x31435657, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
92 #define GUID_MEDIASUBTYPE_CVID  {0x64697663, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
93 #define GUID_MEDIASUBTYPE_MP4S  {0x5334504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
94 #define GUID_MEDIASUBTYPE_MP42  {0x3234504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
95 #define GUID_MEDIASUBTYPE_MP43  {0x3334504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
96 #define GUID_MEDIASUBTYPE_M4S2  {0x3253344d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
97 #define GUID_MEDIASUBTYPE_XVID  {0x44495658, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
98 #define GUID_MEDIASUBTYPE_DX50  {0x30355844, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
99 #define GUID_MEDIASUBTYPE_DIVX  {0x58564944, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
100 #define GUID_MEDIASUBTYPE_DIV3  {0x33564944, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
101 
102 #define GUID_MEDIASUBTYPE_MPG4          {0x3447504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
103 #define GUID_MEDIASUBTYPE_MPEG1Payload  {0xe436eb81, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}}
104 
105 
106 /* output types */
107 #define GUID_MEDIASUBTYPE_YUY2    {0x32595559, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
108 #define GUID_MEDIASUBTYPE_YV12    {0x32315659, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
109 #define GUID_MEDIASUBTYPE_RGB32   {0xe436eb7e, 0x524f, 0x11ce, { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 }}
110 #define GUID_MEDIASUBTYPE_RGB565  {0xe436eb7b, 0x524f, 0x11ce, { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 }}
111 
112 /* WMV always uses the WMV DMO */
113 static PreferredFilter preferred_wmv_filters[] = {
114   {&CLSID_CWMVDecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}
115 };
116 
117 static const GUID CLSID_AVI_DECOMPRESSOR =
118   {0xCF49D4E0, 0x1115, 0x11CE,
119    {0xB0, 0x3A, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70}};
120 static PreferredFilter preferred_cinepack_filters[] = {
121   {&CLSID_AVI_DECOMPRESSOR}, {0}
122 };
123 
124 /* Various MPEG-4 video variants */
125 // MPG4, mpg4, MP42, mp42
126 static PreferredFilter preferred_mpeg4_filters[] = {
127   {&CLSID_CMpeg4DecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
128 // MP4S, mp4s, M4S2, m4s2
129 static PreferredFilter preferred_mp4s_filters[] = {
130   {&CLSID_CMpeg4sDecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
131 // MP43, mp43
132 static PreferredFilter preferred_mp43_filters[] = {
133   {&CLSID_CMpeg43DecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
134 
135 static const GUID CLSID_MPEG_VIDEO_DECODER =
136   {0xFEB50740, 0x7BEF, 0x11CE,
137    {0x9B, 0xD9, 0x00, 0x00, 0xE2, 0x02, 0x59, 0x9C}};
138 static PreferredFilter preferred_mpeg1_filters[] = {
139   {&CLSID_MPEG_VIDEO_DECODER}, {0}
140 };
141 
142 /* video codecs array */
143 static const VideoCodecEntry video_dec_codecs[] = {
144   {"dshowvdec_wmv1", "Windows Media Video 7",
145    GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
146    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV1,
147    "video/x-wmv, wmvversion = (int) 1",
148    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
149    GST_VIDEO_CAPS_MAKE("YUY2"),
150    preferred_wmv_filters},
151 
152   {"dshowvdec_wmv2", "Windows Media Video 8",
153    GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
154    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV2,
155    "video/x-wmv, wmvversion = (int) 2",
156    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
157    GST_VIDEO_CAPS_MAKE("YUY2"),
158    preferred_wmv_filters},
159 
160   {"dshowvdec_wmv3", "Windows Media Video 9",
161    GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
162    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV3,
163    "video/x-wmv, wmvversion = (int) 3, " "format = (string) WMV3",
164    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
165    GST_VIDEO_CAPS_MAKE("YUY2"),
166    preferred_wmv_filters},
167 
168   {"dshowvdec_wmvp", "Windows Media Video 9 Image",
169    GST_MAKE_FOURCC ('W', 'M', 'V', 'P'),
170    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVP,
171    "video/x-wmv, wmvversion = (int) 3, " "format = (string) { WMVP, MSS1 }",
172    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
173    GST_VIDEO_CAPS_MAKE("YUY2"),
174    preferred_wmv_filters},
175 
176   {"dshowvdec_wmva", "Windows Media Video 9 Advanced",
177    GST_MAKE_FOURCC ('W', 'M', 'V', 'A'),
178    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVA,
179    "video/x-wmv, wmvversion = (int) 3, " "format = (string) WMVA",
180    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
181    GST_VIDEO_CAPS_MAKE("YUY2"),
182    preferred_wmv_filters},
183 
184    {"dshowvdec_wvc1", "Windows Media VC1 video",
185    GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
186    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WVC1,
187    "video/x-wmv, wmvversion = (int) 3, " "format = (string) WVC1",
188    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
189    GST_VIDEO_CAPS_MAKE("YUY2"),
190    preferred_wmv_filters},
191 
192   {"dshowvdec_cinepak", "Cinepack",
193    0x64697663,
194    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_CVID,
195    "video/x-cinepak",
196    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_RGB32,
197    "video/x-raw, format=(string)RGB, bpp=(int)32, depth=(int)24, "
198        "endianness=(int)4321, red_mask=(int)65280, "
199        "green_mask=(int)16711680, blue_mask=(int)-16777216",
200    preferred_cinepack_filters},
201 
202   {"dshowvdec_msmpeg41", "Microsoft ISO MPEG-4 version 1",
203    GST_MAKE_FOURCC ('M', 'P', '4', 'S'),
204    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP4S,
205    "video/x-msmpeg, msmpegversion=(int)41",
206    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
207    GST_VIDEO_CAPS_MAKE("YUY2"),
208    preferred_mp4s_filters},
209 
210   {"dshowvdec_msmpeg42", "Microsoft ISO MPEG-4 version 2",
211    GST_MAKE_FOURCC ('M', 'P', '4', '2'),
212    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP42,
213    "video/x-msmpeg, msmpegversion=(int)42",
214    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
215    GST_VIDEO_CAPS_MAKE("YUY2"),
216    preferred_mpeg4_filters},
217 
218   {"dshowvdec_msmpeg43", "Microsoft ISO MPEG-4 version 3",
219    GST_MAKE_FOURCC ('M', 'P', '4', '3'),
220    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP43,
221    "video/x-msmpeg, msmpegversion=(int)43",
222    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
223    GST_VIDEO_CAPS_MAKE("YUY2"),
224    preferred_mp43_filters},
225 
226   {"dshowvdec_msmpeg4", "Microsoft ISO MPEG-4 version 1.1",
227    GST_MAKE_FOURCC ('M', '4', 'S', '2'),
228    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_M4S2,
229    "video/x-msmpeg, msmpegversion=(int)4",
230    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
231    GST_VIDEO_CAPS_MAKE("YUY2"),
232    preferred_mp4s_filters},
233 
234   {"dshowvdec_mpeg1",
235    "MPEG-1 Video",
236    GST_MAKE_FOURCC ('M', 'P', 'E', 'G'),
237    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MPEG1Payload,
238    "video/mpeg, mpegversion= (int) 1, "
239        "parsed= (boolean) true, " "systemstream= (boolean) false",
240    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
241    GST_VIDEO_CAPS_MAKE("YUY2"),
242    preferred_mpeg1_filters},
243 
244   {"dshowvdec_mpeg4", "MPEG-4 Video",
245    GST_MAKE_FOURCC ('M', 'P', 'G', '4'),
246    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MPG4,
247    "video/mpeg, msmpegversion=(int)4",
248    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
249    GST_VIDEO_CAPS_MAKE("YUY2"),
250    preferred_mpeg4_filters},
251 
252   /* The rest of these have no preferred filter; windows doesn't come
253    * with anything appropriate */
254   {"dshowvdec_xvid", "XVID Video",
255    GST_MAKE_FOURCC ('X', 'V', 'I', 'D'),
256    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_XVID,
257    "video/x-xvid",
258    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
259    GST_VIDEO_CAPS_MAKE("YUY2")},
260 
261   {"dshowvdec_divx5", "DIVX 5.0 Video",
262    GST_MAKE_FOURCC ('D', 'X', '5', '0'),
263    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_DX50,
264    "video/x-divx, divxversion=(int)5",
265    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
266    GST_VIDEO_CAPS_MAKE("YUY2")},
267 
268   {"dshowvdec_divx4", "DIVX 4.0 Video",
269    GST_MAKE_FOURCC ('D', 'I', 'V', 'X'),
270    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_DIVX,
271    "video/x-divx, divxversion=(int)4",
272    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
273    GST_VIDEO_CAPS_MAKE("YUY2")},
274 
275   {"dshowvdec_divx3", "DIVX 3.0 Video",
276    GST_MAKE_FOURCC ('D', 'I', 'V', '3'),
277    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP43,
278    "video/x-divx, divxversion=(int)3",
279    GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
280    GST_VIDEO_CAPS_MAKE("YUY2")}
281 };
282 
DoRenderSample(IMediaSample * pMediaSample)283 HRESULT VideoFakeSink::DoRenderSample(IMediaSample *pMediaSample)
284 {
285   gboolean in_seg = FALSE;
286   guint64 clip_start = 0, clip_stop = 0;
287   GstDshowVideoDecClass *klass =
288       (GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (mDec);
289   GstBuffer *buf = NULL;
290   GstClockTime start, stop;
291   GstMapInfo map;
292 
293   if(pMediaSample)
294   {
295     BYTE *pBuffer = NULL;
296     LONGLONG lStart = 0, lStop = 0;
297     long size = pMediaSample->GetActualDataLength();
298 
299     pMediaSample->GetPointer(&pBuffer);
300     pMediaSample->GetTime(&lStart, &lStop);
301 
302     start = lStart * 100;
303     stop = lStop * 100;
304     /* check if this buffer is in our current segment */
305     in_seg = gst_segment_clip (mDec->segment, GST_FORMAT_TIME,
306         start, stop, &clip_start, &clip_stop);
307 
308     /* if the buffer is out of segment do not push it downstream */
309     if (!in_seg) {
310       GST_DEBUG_OBJECT (mDec,
311         "buffer is out of segment, start %" GST_TIME_FORMAT " stop %"
312         GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
313       goto done;
314     }
315 
316     /* buffer is in our segment, allocate a new out buffer and clip its
317      * timestamps */
318     gst_buffer_pool_acquire_buffer(mDec->buffer_pool, &buf, NULL);
319     if (!buf) {
320       GST_WARNING_OBJECT (mDec,
321           "cannot allocate a new GstBuffer");
322       goto done;
323     }
324 
325     /* set buffer properties */
326     GST_BUFFER_TIMESTAMP (buf) = clip_start;
327     GST_BUFFER_DURATION (buf) = clip_stop - clip_start;
328 
329     gst_buffer_map(buf, &map, GST_MAP_WRITE);
330     if (strstr (klass->entry->srccaps, "rgb")) {
331       /* FOR RGB directshow decoder will return bottom-up BITMAP
332        * There is probably a way to get top-bottom video frames from
333        * the decoder...
334        */
335       gint line = 0;
336       guint stride = mDec->width * 4;
337 
338       for (; line < mDec->height; line++) {
339         memcpy (map.data + (line * stride),
340             pBuffer + (size - ((line + 1) * (stride))), stride);
341       }
342     } else {
343       memcpy (map.data, pBuffer, MIN ((unsigned int)size, map.size));
344     }
345     gst_buffer_unmap(buf, &map);
346 
347     GST_LOG_OBJECT (mDec,
348         "push_buffer (size %d)=> pts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
349         " duration %" GST_TIME_FORMAT, size,
350         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
351         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)),
352         GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
353 
354     /* push the buffer downstream */
355     mDec->last_ret = gst_pad_push (mDec->srcpad, buf);
356   }
357 done:
358 
359   return S_OK;
360 }
361 
CheckMediaType(const CMediaType * pmt)362 HRESULT VideoFakeSink::CheckMediaType(const CMediaType *pmt)
363 {
364   if (pmt != NULL) {
365     if (*pmt == m_MediaType)
366       return S_OK;
367   }
368 
369   return S_FALSE;
370 }
371 
372 static void
gst_dshowvideodec_base_init(gpointer klass)373 gst_dshowvideodec_base_init (gpointer klass)
374 {
375   GstDshowVideoDecClass *videodec_class = (GstDshowVideoDecClass *) klass;
376   GstPadTemplate *src, *sink;
377   GstCaps *srccaps, *sinkcaps;
378   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
379   const VideoCodecEntry *tmp;
380   gpointer qdata;
381   gchar *longname, *description;
382 
383   qdata = g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), DSHOW_CODEC_QDATA);
384 
385   /* element details */
386   tmp = videodec_class->entry = (VideoCodecEntry *) qdata;
387 
388   longname = g_strdup_printf ("DirectShow %s Decoder Wrapper",
389       tmp->element_longname);
390   description = g_strdup_printf ("DirectShow %s Decoder Wrapper",
391       tmp->element_longname);
392 
393   gst_element_class_set_metadata(element_class, longname, "Codec/Decoder/Video", description,
394     "Sebastien Moutte <sebastien@moutte.net>");
395 
396   g_free (longname);
397   g_free (description);
398 
399   sinkcaps = gst_caps_from_string (tmp->sinkcaps);
400   gst_caps_set_simple (sinkcaps,
401       "width", GST_TYPE_INT_RANGE, 16, 4096,
402       "height", GST_TYPE_INT_RANGE, 16, 4096,
403       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
404 
405   srccaps = gst_caps_from_string (tmp->srccaps);
406 
407   sink = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sinkcaps);
408   src = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
409 
410   gst_element_class_add_pad_template (element_class, src);
411   gst_element_class_add_pad_template (element_class, sink);
412 
413   if (sinkcaps)
414     gst_caps_unref(sinkcaps);
415 
416   if (srccaps)
417     gst_caps_unref(srccaps);
418 }
419 
420 static void
gst_dshowvideodec_class_init(GstDshowVideoDecClass * klass)421 gst_dshowvideodec_class_init (GstDshowVideoDecClass * klass)
422 {
423   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
424   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
425 
426   gobject_class->finalize = gst_dshowvideodec_finalize;
427 
428   gstelement_class->change_state =
429       GST_DEBUG_FUNCPTR (gst_dshowvideodec_change_state);
430 
431   parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
432 }
433 
434 static void
gst_dshowvideodec_com_thread(GstDshowVideoDec * vdec)435 gst_dshowvideodec_com_thread (GstDshowVideoDec * vdec)
436 {
437   HRESULT res;
438 
439   g_mutex_lock (&vdec->com_init_lock);
440 
441   /* Initialize COM with a MTA for this process. This thread will
442    * be the first one to enter the apartement and the last one to leave
443    * it, unitializing COM properly */
444 
445   res = CoInitializeEx (0, COINIT_MULTITHREADED);
446   if (res == S_FALSE)
447     GST_WARNING_OBJECT (vdec, "COM has been already initialized in the same process");
448   else if (res == RPC_E_CHANGED_MODE)
449     GST_WARNING_OBJECT (vdec, "The concurrency model of COM has changed.");
450   else
451     GST_INFO_OBJECT (vdec, "COM intialized succesfully");
452 
453   vdec->comInitialized = TRUE;
454 
455   /* Signal other threads waiting on this condition that COM was initialized */
456   g_cond_signal (&vdec->com_initialized);
457 
458   g_mutex_unlock (&vdec->com_init_lock);
459 
460   /* Wait until the unitialize condition is met to leave the COM apartement */
461   g_mutex_lock (&vdec->com_deinit_lock);
462   g_cond_wait (&vdec->com_uninitialize, &vdec->com_deinit_lock);
463 
464   CoUninitialize ();
465   GST_INFO_OBJECT (vdec, "COM unintialized succesfully");
466   vdec->comInitialized = FALSE;
467   g_cond_signal (&vdec->com_uninitialized);
468   g_mutex_unlock (&vdec->com_deinit_lock);
469 }
470 
471 static void
gst_dshowvideodec_init(GstDshowVideoDec * vdec)472 gst_dshowvideodec_init (GstDshowVideoDec * vdec)
473 {
474   GstElementClass *element_class = GST_ELEMENT_GET_CLASS (vdec);
475 
476   /* setup pads */
477   vdec->sinkpad =
478       gst_pad_new_from_template (gst_element_class_get_pad_template
479       (element_class, "sink"), "sink");
480 
481   gst_pad_set_event_function (vdec->sinkpad, gst_dshowvideodec_sink_event);
482   gst_pad_set_chain_function (vdec->sinkpad, gst_dshowvideodec_chain);
483   gst_element_add_pad (GST_ELEMENT (vdec), vdec->sinkpad);
484 
485   vdec->srcpad =
486       gst_pad_new_from_template (gst_element_class_get_pad_template
487       (element_class, "src"), "src");
488 /* needed to implement caps negociation on our src pad */
489 /*  gst_pad_set_getcaps_function (vdec->srcpad, gst_dshowvideodec_src_getcaps);
490   gst_pad_set_setcaps_function (vdec->srcpad, gst_dshowvideodec_src_setcaps);*/
491   gst_element_add_pad (GST_ELEMENT (vdec), vdec->srcpad);
492 
493   vdec->fakesrc = NULL;
494   vdec->fakesink = NULL;
495   vdec->decfilter = NULL;
496 
497   vdec->last_ret = GST_FLOW_OK;
498 
499   vdec->filtergraph = NULL;
500   vdec->mediafilter = NULL;
501   vdec->srccaps = NULL;
502   vdec->segment = gst_segment_new ();
503 
504   vdec->setup = FALSE;
505   vdec->buffer_pool = NULL;
506 
507   g_mutex_init (&vdec->com_init_lock);
508   g_mutex_init (&vdec->com_deinit_lock);
509   g_cond_init (&vdec->com_initialized);
510   g_cond_init (&vdec->com_uninitialize);
511   g_cond_init (&vdec->com_uninitialized);
512 
513   g_mutex_lock (&vdec->com_init_lock);
514 
515   /* create the COM initialization thread */
516   g_thread_new ("COM Init Thread", (GThreadFunc)gst_dshowvideodec_com_thread,
517     vdec);
518 
519   /* wait until the COM thread signals that COM has been initialized */
520   g_cond_wait (&vdec->com_initialized, &vdec->com_init_lock);
521   g_mutex_unlock (&vdec->com_init_lock);
522 }
523 
524 static void
gst_dshowvideodec_finalize(GObject * object)525 gst_dshowvideodec_finalize (GObject * object)
526 {
527   GstDshowVideoDec *vdec = (GstDshowVideoDec *) (object);
528 
529   if (vdec->segment) {
530     gst_segment_free (vdec->segment);
531     vdec->segment = NULL;
532   }
533 
534   if(vdec->buffer_pool) {
535     gst_object_unref(vdec->buffer_pool);
536     vdec->buffer_pool = NULL;
537   }
538 
539   /* signal the COM thread that it sould uninitialize COM */
540   if (vdec->comInitialized) {
541     g_mutex_lock (&vdec->com_deinit_lock);
542     g_cond_signal (&vdec->com_uninitialize);
543     g_cond_wait (&vdec->com_uninitialized, &vdec->com_deinit_lock);
544     g_mutex_unlock (&vdec->com_deinit_lock);
545   }
546 
547   g_mutex_clear (&vdec->com_init_lock);
548   g_mutex_clear (&vdec->com_deinit_lock);
549   g_cond_clear (&vdec->com_initialized);
550   g_cond_clear (&vdec->com_uninitialize);
551   g_cond_clear (&vdec->com_uninitialized);
552 
553   G_OBJECT_CLASS (parent_class)->finalize (object);
554 }
555 
556 static GstStateChangeReturn
gst_dshowvideodec_change_state(GstElement * element,GstStateChange transition)557 gst_dshowvideodec_change_state (GstElement * element, GstStateChange transition)
558 {
559   GstDshowVideoDec *vdec = (GstDshowVideoDec *) (element);
560 
561   switch (transition) {
562     case GST_STATE_CHANGE_NULL_TO_READY:
563       if (!gst_dshowvideodec_create_graph_and_filters (vdec))
564         return GST_STATE_CHANGE_FAILURE;
565       break;
566     case GST_STATE_CHANGE_READY_TO_PAUSED:
567       break;
568     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
569       break;
570     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
571       break;
572     case GST_STATE_CHANGE_PAUSED_TO_READY:
573       break;
574     case GST_STATE_CHANGE_READY_TO_NULL:
575       if (!gst_dshowvideodec_destroy_graph_and_filters (vdec))
576         return GST_STATE_CHANGE_FAILURE;
577       break;
578     default:
579       break;
580   }
581 
582   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
583 }
584 
585 static gboolean
gst_dshowvideodec_sink_setcaps(GstPad * pad,GstCaps * caps)586 gst_dshowvideodec_sink_setcaps (GstPad * pad, GstCaps * caps)
587 {
588   gboolean ret = FALSE;
589   HRESULT hres;
590   GstStructure *s = gst_caps_get_structure (caps, 0);
591   GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
592   GstDshowVideoDecClass *klass =
593       (GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (vdec);
594   GstBuffer *extradata = NULL;
595   gsize extra_size;
596   const GValue *v = NULL;
597   guint size = 0;
598   GstCaps *caps_out = NULL;
599   AM_MEDIA_TYPE output_mediatype, input_mediatype;
600   VIDEOINFOHEADER *input_vheader = NULL, *output_vheader = NULL;
601   IPinPtr output_pin;
602   IPinPtr input_pin;
603   IBaseFilter *srcfilter = NULL;
604   IBaseFilter *sinkfilter = NULL;
605   const GValue *fps, *par;
606   GstQuery *query = NULL;
607   GstBufferPool *pool = NULL;
608   GstStructure *pool_config = NULL;
609   guint pool_size, pool_min, pool_max;
610   GstVideoInfo video_info;
611 
612   /* read data */
613   if (!gst_structure_get_int (s, "width", &vdec->width) ||
614       !gst_structure_get_int (s, "height", &vdec->height)) {
615     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
616         ("error getting video width or height from caps"), (NULL));
617     goto end;
618   }
619   fps = gst_structure_get_value (s, "framerate");
620   if (fps) {
621     vdec->fps_n = gst_value_get_fraction_numerator (fps);
622     vdec->fps_d = gst_value_get_fraction_denominator (fps);
623   }
624   else {
625     /* Invent a sane default framerate; the timestamps matter
626      * more anyway. */
627     vdec->fps_n = 25;
628     vdec->fps_d = 1;
629   }
630 
631   par = gst_structure_get_value (s, "pixel-aspect-ratio");
632   if (par) {
633     vdec->par_n = gst_value_get_fraction_numerator (par);
634     vdec->par_d = gst_value_get_fraction_denominator (par);
635   }
636   else {
637     vdec->par_n = vdec->par_d = 1;
638   }
639 
640   if ((v = gst_structure_get_value (s, "codec_data"))) {
641     extradata = gst_value_get_buffer (v);
642     extra_size = gst_buffer_get_size(extradata);
643   }
644 
645   /* define the input type format */
646   memset (&input_mediatype, 0, sizeof (AM_MEDIA_TYPE));
647   input_mediatype.majortype = klass->entry->input_majortype;
648   input_mediatype.subtype = klass->entry->input_subtype;
649   input_mediatype.bFixedSizeSamples = FALSE;
650   input_mediatype.bTemporalCompression = TRUE;
651 
652   if (strstr (klass->entry->sinkcaps, "video/mpeg, mpegversion= (int) 1")) {
653     size =
654         sizeof (MPEG1VIDEOINFO) + (extradata ? extra_size - 1 : 0);
655     input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size);
656 
657     input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
658     if (extradata) {
659       MPEG1VIDEOINFO *mpeg_info = (MPEG1VIDEOINFO *) input_vheader;
660 
661       gst_buffer_extract(extradata, 0, mpeg_info->bSequenceHeader, extra_size);
662       mpeg_info->cbSequenceHeader = extra_size;
663     }
664     input_mediatype.formattype = FORMAT_MPEGVideo;
665   } else {
666     size =
667         sizeof (VIDEOINFOHEADER) + (extradata ? extra_size : 0);
668     input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size);
669     input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
670 
671     if (extradata) {            /* Codec data is appended after our header */
672       gst_buffer_extract(extradata, 0,
673         ((guchar *) input_vheader) + sizeof (VIDEOINFOHEADER), extra_size);
674       input_vheader->bmiHeader.biSize += extra_size;
675     }
676     input_mediatype.formattype = FORMAT_VideoInfo;
677   }
678 
679   input_vheader->rcSource.top = input_vheader->rcSource.left = 0;
680   input_vheader->rcSource.right = vdec->width;
681   input_vheader->rcSource.bottom = vdec->height;
682   input_vheader->rcTarget = input_vheader->rcSource;
683   input_vheader->bmiHeader.biWidth = vdec->width;
684   input_vheader->bmiHeader.biHeight = vdec->height;
685   input_vheader->bmiHeader.biPlanes = 1;
686   input_vheader->bmiHeader.biBitCount = 16;
687   input_vheader->bmiHeader.biCompression = klass->entry->format;
688   input_vheader->bmiHeader.biSizeImage =
689       (vdec->width * vdec->height) * (input_vheader->bmiHeader.biBitCount / 8);
690 
691   input_mediatype.cbFormat = size;
692   input_mediatype.pbFormat = (BYTE *) input_vheader;
693   input_mediatype.lSampleSize = input_vheader->bmiHeader.biSizeImage;
694 
695   vdec->fakesrc->GetOutputPin()->SetMediaType(&input_mediatype);
696 
697   /* set the sample size for fakesrc filter to the output buffer size */
698   vdec->fakesrc->GetOutputPin()->SetSampleSize(input_mediatype.lSampleSize);
699 
700   /* connect our fake src to decoder */
701   hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
702       (void **) &srcfilter);
703   if (FAILED (hres)) {
704     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
705       ("Can't QT fakesrc to IBaseFilter: %x", hres), (NULL));
706     goto end;
707   }
708 
709   output_pin = gst_dshow_get_pin_from_filter (srcfilter, PINDIR_OUTPUT);
710   if (!output_pin) {
711     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
712         ("Can't get output pin from our directshow fakesrc filter"), (NULL));
713     goto end;
714   }
715   input_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_INPUT);
716   if (!input_pin) {
717     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
718         ("Can't get input pin from decoder filter"), (NULL));
719     goto end;
720   }
721 
722   hres = vdec->filtergraph->ConnectDirect (output_pin, input_pin, NULL);
723   if (hres != S_OK) {
724     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
725         ("Can't connect fakesrc with decoder (error=%x)", hres), (NULL));
726     goto end;
727   }
728 
729   /* get decoder output video format */
730   if (!gst_dshowvideodec_get_filter_output_format (vdec,
731           klass->entry->output_subtype, &output_vheader, &size)) {
732     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
733         ("Can't get decoder output video format"), (NULL));
734     goto end;
735   }
736 
737   memset (&output_mediatype, 0, sizeof (AM_MEDIA_TYPE));
738   output_mediatype.majortype = klass->entry->output_majortype;
739   output_mediatype.subtype = klass->entry->output_subtype;
740   output_mediatype.bFixedSizeSamples = TRUE;
741   output_mediatype.bTemporalCompression = FALSE;
742   output_mediatype.lSampleSize = output_vheader->bmiHeader.biSizeImage;
743   output_mediatype.formattype = FORMAT_VideoInfo;
744   output_mediatype.cbFormat = size;
745   output_mediatype.pbFormat = (BYTE *) output_vheader;
746 
747   vdec->fakesink->SetMediaType (&output_mediatype);
748 
749   /* connect decoder to our fake sink */
750   output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
751   if (!output_pin) {
752     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
753         ("Can't get output pin from our decoder filter"), (NULL));
754     goto end;
755   }
756 
757   hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
758       (void **) &sinkfilter);
759   if (FAILED (hres)) {
760     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
761       ("Can't QT fakesink to IBaseFilter: %x", hres), (NULL));
762     goto end;
763   }
764 
765   input_pin = gst_dshow_get_pin_from_filter (sinkfilter, PINDIR_INPUT);
766   if (!input_pin) {
767     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
768         ("Can't get input pin from our directshow fakesink filter"), (NULL));
769     goto end;
770   }
771 
772   hres = vdec->filtergraph->ConnectDirect(output_pin, input_pin,
773       &output_mediatype);
774   if (hres != S_OK) {
775     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
776         ("Can't connect decoder with fakesink (error=%x)", hres), (NULL));
777     goto end;
778   }
779 
780   /* negotiate output */
781   caps_out = gst_caps_from_string (klass->entry->srccaps);
782   gst_caps_set_simple (caps_out,
783       "width", G_TYPE_INT, vdec->width,
784       "height", G_TYPE_INT, vdec->height, NULL);
785 
786   if (vdec->fps_n && vdec->fps_d) {
787       gst_caps_set_simple (caps_out,
788           "framerate", GST_TYPE_FRACTION, vdec->fps_n, vdec->fps_d, NULL);
789   }
790 
791   gst_caps_set_simple (caps_out,
792       "pixel-aspect-ratio", GST_TYPE_FRACTION, vdec->par_n, vdec->par_d, NULL);
793 
794   if (!gst_pad_set_caps (vdec->srcpad, caps_out)) {
795     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
796         ("Failed to negotiate output"), (NULL));
797     goto end;
798   }
799 
800   /* request or create a buffer pool */
801   if (vdec->buffer_pool) {
802     gst_object_unref (vdec->buffer_pool);
803   }
804 
805   query = gst_query_new_allocation(caps_out, TRUE);
806   gst_pad_peer_query(vdec->srcpad, query);
807 
808   if (gst_query_get_n_allocation_pools (query) > 0) {
809     gst_query_parse_nth_allocation_pool (query, 0, &pool, &pool_size, &pool_min,
810       &pool_max);
811   }
812   else {
813     pool = NULL;
814     pool_size = output_mediatype.lSampleSize;
815     pool_min = 1;
816     pool_max = 0;
817   }
818 
819   if (pool == NULL) {
820     pool = gst_video_buffer_pool_new ();
821   }
822 
823   if (!pool) {
824     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
825         ("Could not create buffer bool"), (NULL));
826     goto end;
827   }
828 
829   pool_config = gst_buffer_pool_get_config (pool);
830   gst_buffer_pool_config_set_params (pool_config, caps_out, pool_size,
831     pool_min, pool_max);
832   gst_buffer_pool_set_config (pool, pool_config);
833 
834   if (!gst_buffer_pool_set_active (pool, TRUE)) {
835     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
836       ("Failed set buffer pool active"), (NULL));
837     goto end;
838   }
839 
840   vdec->buffer_pool = pool;
841 
842   hres = vdec->mediafilter->Run (-1);
843   if (hres != S_OK) {
844     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
845         ("Can't run the directshow graph (error=%d)", hres), (NULL));
846     goto end;
847   }
848 
849   ret = TRUE;
850 end:
851   if (caps_out)
852     gst_caps_unref (caps_out);
853   gst_object_unref (vdec);
854   g_free (input_vheader);
855   if (srcfilter)
856     srcfilter->Release();
857   if (sinkfilter)
858     sinkfilter->Release();
859   if (query)
860     gst_query_unref(query);
861   return ret;
862 }
863 
864 static gboolean
gst_dshowvideodec_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)865 gst_dshowvideodec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
866 {
867   gboolean ret = TRUE;
868   GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
869 
870   switch (GST_EVENT_TYPE (event)) {
871     case GST_EVENT_CAPS:
872       GstCaps *caps;
873       gst_event_parse_caps(event, &caps);
874       ret = gst_dshowvideodec_sink_setcaps(pad, caps);
875       break;
876 
877     case GST_EVENT_FLUSH_STOP:
878       gst_dshowvideodec_flush (vdec);
879       ret = gst_pad_event_default (pad, parent, event);
880       break;
881     case GST_EVENT_SEGMENT:
882     {
883       const GstSegment *segment;
884 
885       gst_event_parse_segment (event, &segment);
886 
887       /* save the new segment in our local current segment */
888       gst_segment_copy_into(segment, vdec->segment);
889 
890       GST_CAT_DEBUG_OBJECT (dshowvideodec_debug, vdec,
891           "new segment received => start=%" GST_TIME_FORMAT " stop=%"
892           GST_TIME_FORMAT, GST_TIME_ARGS (vdec->segment->start),
893           GST_TIME_ARGS (vdec->segment->stop));
894 
895       ret = gst_pad_event_default (pad, parent, event);
896       break;
897     }
898     default:
899       ret = gst_pad_event_default (pad, parent, event);
900       break;
901   }
902 
903   gst_object_unref (vdec);
904 
905   return ret;
906 }
907 
908 static GstFlowReturn
gst_dshowvideodec_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)909 gst_dshowvideodec_chain (GstPad * pad, GstObject *parent, GstBuffer * buffer)
910 {
911   GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
912   bool discont = FALSE;
913   GstClockTime stop;
914   GstMapInfo map;
915 
916   if (!vdec->setup) {
917     /* we are not setup */
918     GST_WARNING_OBJECT (vdec, "Decoder not set up, failing");
919     vdec->last_ret = GST_FLOW_FLUSHING;
920     goto beach;
921   }
922 
923   if (vdec->last_ret != GST_FLOW_OK) {
924     GST_DEBUG_OBJECT (vdec, "last decoding iteration generated a fatal error "
925         "%s", gst_flow_get_name (vdec->last_ret));
926     goto beach;
927   }
928 
929   /* check if duration is valid and use duration only when it's valid
930      /* because dshow is not decoding frames having stop smaller than start */
931   if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
932     stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
933   } else {
934     stop = GST_BUFFER_TIMESTAMP (buffer);
935   }
936 
937   GST_CAT_LOG_OBJECT (dshowvideodec_debug, vdec,
938       "chain (size %d)=> pts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT,
939       gst_buffer_get_size (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
940       GST_TIME_ARGS (stop));
941 
942   /* if the incoming buffer has discont flag set => flush decoder data */
943   if (buffer && GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
944     GST_CAT_DEBUG_OBJECT (dshowvideodec_debug, vdec,
945         "this buffer has a DISCONT flag (%" GST_TIME_FORMAT "), flushing",
946         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
947     gst_dshowvideodec_flush (vdec);
948     discont = TRUE;
949   }
950 
951   gst_buffer_map(buffer, &map, GST_MAP_READ);
952   /* push the buffer to the directshow decoder */
953   vdec->fakesrc->GetOutputPin()->PushBuffer(
954       map.data, GST_BUFFER_TIMESTAMP (buffer), stop,
955       map.size, discont);
956   gst_buffer_unmap(buffer, &map);
957 
958 beach:
959   gst_buffer_unref (buffer);
960   gst_object_unref (vdec);
961 
962   return vdec->last_ret;
963 }
964 
965 static GstCaps *
gst_dshowvideodec_src_getcaps(GstPad * pad)966 gst_dshowvideodec_src_getcaps (GstPad * pad)
967 {
968   GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
969   GstCaps *caps = NULL;
970 
971   if (!vdec->srccaps)
972     vdec->srccaps = gst_caps_new_empty ();
973 
974   if (vdec->decfilter) {
975     IPinPtr output_pin;
976     IEnumMediaTypesPtr enum_mediatypes;
977     HRESULT hres;
978     ULONG fetched;
979 
980     output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
981     if (!output_pin) {
982       GST_ELEMENT_ERROR (vdec, STREAM, FAILED,
983           ("failed getting ouput pin from the decoder"), (NULL));
984       goto beach;
985     }
986 
987     hres = output_pin->EnumMediaTypes (&enum_mediatypes);
988     if (hres == S_OK && enum_mediatypes) {
989       AM_MEDIA_TYPE *mediatype = NULL;
990 
991       enum_mediatypes->Reset();
992       while (hres =
993           enum_mediatypes->Next(1, &mediatype, &fetched),
994           hres == S_OK)
995       {
996         VIDEOINFOHEADER *video_info;
997         GstCaps *mediacaps = NULL;
998 
999         /* RGB24 */
1000         if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_RGB24) &&
1001             IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo))
1002         {
1003           video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
1004 
1005           /* ffmpegcolorspace handles RGB24 in BIG_ENDIAN */
1006           mediacaps = gst_caps_new_simple ("video/x-raw-rgb",
1007               "bpp", G_TYPE_INT, 24,
1008               "depth", G_TYPE_INT, 24,
1009               "width", G_TYPE_INT, video_info->bmiHeader.biWidth,
1010               "height", G_TYPE_INT, video_info->bmiHeader.biHeight,
1011               "framerate", GST_TYPE_FRACTION,
1012               (int) (10000000 / video_info->AvgTimePerFrame), 1, "endianness",
1013               G_TYPE_INT, G_BIG_ENDIAN, "red_mask", G_TYPE_INT, 255,
1014               "green_mask", G_TYPE_INT, 65280, "blue_mask", G_TYPE_INT,
1015               16711680, NULL);
1016 
1017           if (mediacaps) {
1018             vdec->mediatypes = g_list_append (vdec->mediatypes, mediatype);
1019             gst_caps_append (vdec->srccaps, mediacaps);
1020           } else {
1021             DeleteMediaType (mediatype);
1022           }
1023         } else {
1024           DeleteMediaType (mediatype);
1025         }
1026 
1027       }
1028     }
1029   }
1030 
1031   if (vdec->srccaps)
1032     caps = gst_caps_ref (vdec->srccaps);
1033 
1034 beach:
1035   gst_object_unref (vdec);
1036 
1037   return caps;
1038 }
1039 
1040 static gboolean
gst_dshowvideodec_src_setcaps(GstPad * pad,GstCaps * caps)1041 gst_dshowvideodec_src_setcaps (GstPad * pad, GstCaps * caps)
1042 {
1043   gboolean ret = FALSE;
1044 
1045   return ret;
1046 }
1047 
1048 static gboolean
gst_dshowvideodec_flush(GstDshowVideoDec * vdec)1049 gst_dshowvideodec_flush (GstDshowVideoDec * vdec)
1050 {
1051   if (!vdec->fakesrc)
1052     return FALSE;
1053 
1054   /* flush dshow decoder and reset timestamp */
1055   vdec->fakesrc->GetOutputPin()->Flush();
1056   vdec->last_ret = GST_FLOW_OK;
1057 
1058   return TRUE;
1059 }
1060 
1061 static gboolean
gst_dshowvideodec_get_filter_output_format(GstDshowVideoDec * vdec,const GUID subtype,VIDEOINFOHEADER ** format,guint * size)1062 gst_dshowvideodec_get_filter_output_format (GstDshowVideoDec * vdec,
1063     const GUID subtype, VIDEOINFOHEADER ** format, guint * size)
1064 {
1065   IPinPtr output_pin;
1066   IEnumMediaTypesPtr enum_mediatypes;
1067   HRESULT hres;
1068   ULONG fetched;
1069   BOOL ret = FALSE;
1070 
1071   if (!vdec->decfilter)
1072     return FALSE;
1073 
1074   output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
1075   if (!output_pin) {
1076     GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
1077         ("failed getting ouput pin from the decoder"), (NULL));
1078     return FALSE;
1079   }
1080 
1081   hres = output_pin->EnumMediaTypes (&enum_mediatypes);
1082   if (hres == S_OK && enum_mediatypes) {
1083     AM_MEDIA_TYPE *mediatype = NULL;
1084 
1085     enum_mediatypes->Reset();
1086     while (hres =
1087         enum_mediatypes->Next(1, &mediatype, &fetched),
1088         hres == S_OK)
1089     {
1090       if (IsEqualGUID (mediatype->subtype, subtype) &&
1091           IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo))
1092       {
1093         *size = mediatype->cbFormat;
1094         *format = (VIDEOINFOHEADER *)g_malloc0 (*size);
1095         memcpy (*format, mediatype->pbFormat, *size);
1096         ret = TRUE;
1097       }
1098       DeleteMediaType (mediatype);
1099       if (ret)
1100         break;
1101     }
1102   }
1103 
1104   return ret;
1105 }
1106 
1107 static gboolean
gst_dshowvideodec_create_graph_and_filters(GstDshowVideoDec * vdec)1108 gst_dshowvideodec_create_graph_and_filters (GstDshowVideoDec * vdec)
1109 {
1110   HRESULT hres = S_FALSE;
1111   GstDshowVideoDecClass *klass =
1112       (GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (vdec);
1113   IBaseFilter *srcfilter = NULL;
1114   IBaseFilter *sinkfilter = NULL;
1115   gboolean ret = FALSE;
1116 
1117   /* create the filter graph manager object */
1118   hres = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
1119       IID_IFilterGraph, (LPVOID *) & vdec->filtergraph);
1120   if (hres != S_OK || !vdec->filtergraph) {
1121     GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't create an instance "
1122             "of the directshow graph manager (error=%d)", hres), (NULL));
1123     goto error;
1124   }
1125 
1126   hres = vdec->filtergraph->QueryInterface(IID_IMediaFilter,
1127       (void **) &vdec->mediafilter);
1128   if (hres != S_OK || !vdec->mediafilter) {
1129     GST_ELEMENT_ERROR (vdec, STREAM, FAILED,
1130         ("Can't get IMediacontrol interface "
1131             "from the graph manager (error=%d)", hres), (NULL));
1132     goto error;
1133   }
1134 
1135   /* create fake src filter */
1136   vdec->fakesrc = new FakeSrc();
1137   /* Created with a refcount of zero, so increment that */
1138   vdec->fakesrc->AddRef();
1139 
1140   hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
1141       (void **) &srcfilter);
1142   if (FAILED (hres)) {
1143     GST_WARNING_OBJECT (vdec, "Failed to QI fakesrc to IBaseFilter");
1144     goto error;
1145   }
1146 
1147   /* search a decoder filter and create it */
1148   vdec->decfilter = gst_dshow_find_filter (
1149           klass->entry->input_majortype,
1150           klass->entry->input_subtype,
1151           klass->entry->output_majortype,
1152           klass->entry->output_subtype,
1153           klass->entry->preferred_filters);
1154   if (vdec->decfilter == NULL) {
1155     GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't create an instance "
1156             "of the decoder filter"), (NULL));
1157     goto error;
1158   }
1159 
1160   /* create fake sink filter */
1161   vdec->fakesink = new VideoFakeSink(vdec);
1162   /* Created with a refcount of zero, so increment that */
1163   vdec->fakesink->AddRef();
1164 
1165   hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
1166       (void **) &sinkfilter);
1167   if (FAILED (hres)) {
1168     GST_WARNING_OBJECT (vdec, "Failed to QI fakesink to IBaseFilter");
1169     goto error;
1170   }
1171 
1172   /* add filters to the graph */
1173   hres = vdec->filtergraph->AddFilter (srcfilter, L"src");
1174   if (hres != S_OK) {
1175     GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add fakesrc filter "
1176             "to the graph (error=%d)", hres), (NULL));
1177     goto error;
1178   }
1179 
1180   hres = vdec->filtergraph->AddFilter(vdec->decfilter, L"decoder");
1181   if (hres != S_OK) {
1182     GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add decoder filter "
1183             "to the graph (error=%d)", hres), (NULL));
1184     goto error;
1185   }
1186 
1187   hres = vdec->filtergraph->AddFilter(sinkfilter, L"sink");
1188   if (hres != S_OK) {
1189     GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add fakesink filter "
1190             "to the graph (error=%d)", hres), (NULL));
1191     goto error;
1192   }
1193 
1194   vdec->setup = TRUE;
1195 
1196   ret = TRUE;
1197 
1198 done:
1199   if (srcfilter)
1200     srcfilter->Release();
1201   if (sinkfilter)
1202     sinkfilter->Release();
1203   return ret;
1204 
1205 error:
1206   if (vdec->fakesrc) {
1207     vdec->fakesrc->Release();
1208     vdec->fakesrc = NULL;
1209   }
1210   if (vdec->decfilter) {
1211     vdec->decfilter->Release();
1212     vdec->decfilter = NULL;
1213   }
1214   if (vdec->fakesink) {
1215     vdec->fakesink->Release();
1216     vdec->fakesink = NULL;
1217   }
1218   if (vdec->mediafilter) {
1219     vdec->mediafilter->Release();
1220     vdec->mediafilter = NULL;
1221   }
1222   if (vdec->filtergraph) {
1223     vdec->filtergraph->Release();
1224     vdec->filtergraph = NULL;
1225   }
1226 
1227   goto done;
1228 }
1229 
1230 static gboolean
gst_dshowvideodec_destroy_graph_and_filters(GstDshowVideoDec * vdec)1231 gst_dshowvideodec_destroy_graph_and_filters (GstDshowVideoDec * vdec)
1232 {
1233   HRESULT hres;
1234 
1235   if (vdec->mediafilter) {
1236     vdec->mediafilter->Stop();
1237   }
1238 
1239   if (vdec->fakesrc) {
1240     if (vdec->filtergraph) {
1241       IBaseFilter *filter;
1242       hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
1243           (void **) &filter);
1244       if (SUCCEEDED (hres)) {
1245         vdec->filtergraph->RemoveFilter(filter);
1246         filter->Release();
1247       }
1248     }
1249 
1250     vdec->fakesrc->Release();
1251     vdec->fakesrc = NULL;
1252   }
1253   if (vdec->decfilter) {
1254     if (vdec->filtergraph)
1255       vdec->filtergraph->RemoveFilter(vdec->decfilter);
1256     vdec->decfilter->Release();
1257     vdec->decfilter = NULL;
1258   }
1259   if (vdec->fakesink) {
1260     if (vdec->filtergraph) {
1261       IBaseFilter *filter;
1262       hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
1263           (void **) &filter);
1264       if (SUCCEEDED (hres)) {
1265         vdec->filtergraph->RemoveFilter(filter);
1266         filter->Release();
1267       }
1268     }
1269 
1270     vdec->fakesink->Release();
1271     vdec->fakesink = NULL;
1272   }
1273   if (vdec->mediafilter) {
1274     vdec->mediafilter->Release();
1275     vdec->mediafilter = NULL;
1276   }
1277   if (vdec->filtergraph) {
1278     vdec->filtergraph->Release();
1279     vdec->filtergraph = NULL;
1280   }
1281 
1282   vdec->setup = FALSE;
1283 
1284   return TRUE;
1285 }
1286 
1287 gboolean
dshow_vdec_register(GstPlugin * plugin)1288 dshow_vdec_register (GstPlugin * plugin)
1289 {
1290   GTypeInfo info = {
1291     sizeof (GstDshowVideoDecClass),
1292     (GBaseInitFunc) gst_dshowvideodec_base_init,
1293     NULL,
1294     (GClassInitFunc) gst_dshowvideodec_class_init,
1295     NULL,
1296     NULL,
1297     sizeof (GstDshowVideoDec),
1298     0,
1299     (GInstanceInitFunc) gst_dshowvideodec_init,
1300   };
1301   gint i;
1302   HRESULT hr;
1303 
1304   GST_DEBUG_CATEGORY_INIT (dshowvideodec_debug, "dshowvideodec", 0,
1305       "Directshow filter video decoder");
1306 
1307   hr = CoInitialize (0);
1308 
1309   for (i = 0; i < sizeof (video_dec_codecs) / sizeof (VideoCodecEntry); i++) {
1310     GType type;
1311     IBaseFilterPtr filter;
1312     guint rank = GST_RANK_MARGINAL;
1313 
1314     filter = gst_dshow_find_filter (
1315             video_dec_codecs[i].input_majortype,
1316             video_dec_codecs[i].input_subtype,
1317             video_dec_codecs[i].output_majortype,
1318             video_dec_codecs[i].output_subtype,
1319             video_dec_codecs[i].preferred_filters);
1320     if (filter != NULL) {
1321 
1322       if (video_dec_codecs[i].format == GST_MAKE_FOURCC ('W', 'V', 'C', '1')) {
1323         /* FFMPEG WVC1 decoder sucks, get higher priority for ours */
1324         rank = GST_RANK_MARGINAL + 2;
1325       }
1326       GST_DEBUG ("Registering %s with rank %u", video_dec_codecs[i].element_name, rank);
1327 
1328       type = g_type_register_static (GST_TYPE_ELEMENT,
1329           video_dec_codecs[i].element_name, &info, (GTypeFlags)0);
1330       g_type_set_qdata (type, DSHOW_CODEC_QDATA, (gpointer) (video_dec_codecs + i));
1331       if (!gst_element_register (plugin, video_dec_codecs[i].element_name, rank, type)) {
1332         return FALSE;
1333       }
1334       GST_DEBUG ("Registered %s", video_dec_codecs[i].element_name);
1335     } else {
1336       GST_DEBUG ("Element %s not registered "
1337         "(the format is not supported by the system)",
1338         video_dec_codecs[i].element_name);
1339     }
1340   }
1341 
1342   if (SUCCEEDED(hr))
1343     CoUninitialize ();
1344 
1345   return TRUE;
1346 }
1347