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