mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 11:11:08 +00:00
1346 lines
44 KiB
C++
1346 lines
44 KiB
C++
/*
|
|
* GStreamer DirectShow codecs wrapper
|
|
* Copyright <2006, 2007, 2008, 2009, 2010> Fluendo <support@fluendo.com>
|
|
* Copyright <2006, 2007, 2008> Pioneers of the Inevitable <songbird@songbirdnest.com>
|
|
* Copyright <2007,2008> Sebastien Moutte <sebastien@moutte.net>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
|
|
* which case the following provisions apply instead of the ones
|
|
* mentioned above:
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <dmoreg.h>
|
|
#include <wmcodecdsp.h>
|
|
|
|
#include "gstdshowvideodec.h"
|
|
#include <gst/video/video.h>
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (dshowvideodec_debug);
|
|
#define GST_CAT_DEFAULT dshowvideodec_debug
|
|
|
|
#define gst_dshowvideodec_parent_class parent_class
|
|
G_DEFINE_TYPE(GstDshowVideoDec, gst_dshowvideodec, GST_TYPE_ELEMENT)
|
|
|
|
static void gst_dshowvideodec_finalize (GObject * object);
|
|
static GstStateChangeReturn gst_dshowvideodec_change_state
|
|
(GstElement * element, GstStateChange transition);
|
|
|
|
/* sink pad overwrites */
|
|
static gboolean gst_dshowvideodec_sink_setcaps (GstPad * pad, GstCaps * caps);
|
|
static gboolean gst_dshowvideodec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
|
|
static GstFlowReturn gst_dshowvideodec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer);
|
|
|
|
/* src pad overwrites */
|
|
static GstCaps *gst_dshowvideodec_src_getcaps (GstPad * pad);
|
|
static gboolean gst_dshowvideodec_src_setcaps (GstPad * pad, GstCaps * caps);
|
|
|
|
/* utils */
|
|
static gboolean gst_dshowvideodec_create_graph_and_filters (GstDshowVideoDec *
|
|
vdec);
|
|
static gboolean gst_dshowvideodec_destroy_graph_and_filters (GstDshowVideoDec *
|
|
vdec);
|
|
static gboolean gst_dshowvideodec_flush (GstDshowVideoDec * adec);
|
|
static gboolean gst_dshowvideodec_get_filter_output_format (GstDshowVideoDec *
|
|
vdec, const GUID subtype, VIDEOINFOHEADER ** format, guint * size);
|
|
|
|
|
|
#define GUID_MEDIATYPE_VIDEO {0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WMVV1 {0x31564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WMVV2 {0x32564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WMVV3 {0x33564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WMVP {0x50564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WMVA {0x41564d57, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_WVC1 {0x31435657, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_CVID {0x64697663, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_MP4S {0x5334504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_MP42 {0x3234504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_MP43 {0x3334504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_M4S2 {0x3253344d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_XVID {0x44495658, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_DX50 {0x30355844, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_DIVX {0x58564944, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_DIV3 {0x33564944, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
|
|
#define GUID_MEDIASUBTYPE_MPG4 {0x3447504d, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_MPEG1Payload {0xe436eb81, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}}
|
|
|
|
|
|
/* output types */
|
|
#define GUID_MEDIASUBTYPE_YUY2 {0x32595559, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_YV12 {0x32315659, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }}
|
|
#define GUID_MEDIASUBTYPE_RGB32 {0xe436eb7e, 0x524f, 0x11ce, { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 }}
|
|
#define GUID_MEDIASUBTYPE_RGB565 {0xe436eb7b, 0x524f, 0x11ce, { 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70 }}
|
|
|
|
/* WMV always uses the WMV DMO */
|
|
static PreferredFilter preferred_wmv_filters[] = {
|
|
{&CLSID_CWMVDecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}
|
|
};
|
|
|
|
static const GUID CLSID_AVI_DECOMPRESSOR =
|
|
{0xCF49D4E0, 0x1115, 0x11CE,
|
|
{0xB0, 0x3A, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70}};
|
|
static PreferredFilter preferred_cinepack_filters[] = {
|
|
{&CLSID_AVI_DECOMPRESSOR}, {0}
|
|
};
|
|
|
|
/* Various MPEG-4 video variants */
|
|
// MPG4, mpg4, MP42, mp42
|
|
static PreferredFilter preferred_mpeg4_filters[] = {
|
|
{&CLSID_CMpeg4DecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
|
|
// MP4S, mp4s, M4S2, m4s2
|
|
static PreferredFilter preferred_mp4s_filters[] = {
|
|
{&CLSID_CMpeg4sDecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
|
|
// MP43, mp43
|
|
static PreferredFilter preferred_mp43_filters[] = {
|
|
{&CLSID_CMpeg43DecMediaObject, &DMOCATEGORY_VIDEO_DECODER}, {0}};
|
|
|
|
static const GUID CLSID_MPEG_VIDEO_DECODER =
|
|
{0xFEB50740, 0x7BEF, 0x11CE,
|
|
{0x9B, 0xD9, 0x00, 0x00, 0xE2, 0x02, 0x59, 0x9C}};
|
|
static PreferredFilter preferred_mpeg1_filters[] = {
|
|
{&CLSID_MPEG_VIDEO_DECODER}, {0}
|
|
};
|
|
|
|
/* video codecs array */
|
|
static const VideoCodecEntry video_dec_codecs[] = {
|
|
{"dshowvdec_wmv1", "Windows Media Video 7",
|
|
GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV1,
|
|
"video/x-wmv, wmvversion = (int) 1",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_wmv2", "Windows Media Video 8",
|
|
GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV2,
|
|
"video/x-wmv, wmvversion = (int) 2",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_wmv3", "Windows Media Video 9",
|
|
GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV3,
|
|
"video/x-wmv, wmvversion = (int) 3, " "format = (string) WMV3",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_wmvp", "Windows Media Video 9 Image",
|
|
GST_MAKE_FOURCC ('W', 'M', 'V', 'P'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVP,
|
|
"video/x-wmv, wmvversion = (int) 3, " "format = (string) { WMVP, MSS1 }",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_wmva", "Windows Media Video 9 Advanced",
|
|
GST_MAKE_FOURCC ('W', 'M', 'V', 'A'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVA,
|
|
"video/x-wmv, wmvversion = (int) 3, " "format = (string) WMVA",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_wvc1", "Windows Media VC1 video",
|
|
GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WVC1,
|
|
"video/x-wmv, wmvversion = (int) 3, " "format = (string) WVC1",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_wmv_filters},
|
|
|
|
{"dshowvdec_cinepak", "Cinepack",
|
|
0x64697663,
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_CVID,
|
|
"video/x-cinepak",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_RGB32,
|
|
"video/x-raw, format=(string)RGB, bpp=(int)32, depth=(int)24, "
|
|
"endianness=(int)4321, red_mask=(int)65280, "
|
|
"green_mask=(int)16711680, blue_mask=(int)-16777216",
|
|
preferred_cinepack_filters},
|
|
|
|
{"dshowvdec_msmpeg41", "Microsoft ISO MPEG-4 version 1",
|
|
GST_MAKE_FOURCC ('M', 'P', '4', 'S'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP4S,
|
|
"video/x-msmpeg, msmpegversion=(int)41",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mp4s_filters},
|
|
|
|
{"dshowvdec_msmpeg42", "Microsoft ISO MPEG-4 version 2",
|
|
GST_MAKE_FOURCC ('M', 'P', '4', '2'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP42,
|
|
"video/x-msmpeg, msmpegversion=(int)42",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mpeg4_filters},
|
|
|
|
{"dshowvdec_msmpeg43", "Microsoft ISO MPEG-4 version 3",
|
|
GST_MAKE_FOURCC ('M', 'P', '4', '3'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP43,
|
|
"video/x-msmpeg, msmpegversion=(int)43",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mp43_filters},
|
|
|
|
{"dshowvdec_msmpeg4", "Microsoft ISO MPEG-4 version 1.1",
|
|
GST_MAKE_FOURCC ('M', '4', 'S', '2'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_M4S2,
|
|
"video/x-msmpeg, msmpegversion=(int)4",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mp4s_filters},
|
|
|
|
{"dshowvdec_mpeg1",
|
|
"MPEG-1 Video",
|
|
GST_MAKE_FOURCC ('M', 'P', 'E', 'G'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MPEG1Payload,
|
|
"video/mpeg, mpegversion= (int) 1, "
|
|
"parsed= (boolean) true, " "systemstream= (boolean) false",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mpeg1_filters},
|
|
|
|
{"dshowvdec_mpeg4", "MPEG-4 Video",
|
|
GST_MAKE_FOURCC ('M', 'P', 'G', '4'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MPG4,
|
|
"video/mpeg, msmpegversion=(int)4",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2"),
|
|
preferred_mpeg4_filters},
|
|
|
|
/* The rest of these have no preferred filter; windows doesn't come
|
|
* with anything appropriate */
|
|
{"dshowvdec_xvid", "XVID Video",
|
|
GST_MAKE_FOURCC ('X', 'V', 'I', 'D'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_XVID,
|
|
"video/x-xvid",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2")},
|
|
|
|
{"dshowvdec_divx5", "DIVX 5.0 Video",
|
|
GST_MAKE_FOURCC ('D', 'X', '5', '0'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_DX50,
|
|
"video/x-divx, divxversion=(int)5",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2")},
|
|
|
|
{"dshowvdec_divx4", "DIVX 4.0 Video",
|
|
GST_MAKE_FOURCC ('D', 'I', 'V', 'X'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_DIVX,
|
|
"video/x-divx, divxversion=(int)4",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2")},
|
|
|
|
{"dshowvdec_divx3", "DIVX 3.0 Video",
|
|
GST_MAKE_FOURCC ('D', 'I', 'V', '3'),
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MP43,
|
|
"video/x-divx, divxversion=(int)3",
|
|
GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2,
|
|
GST_VIDEO_CAPS_MAKE("YUY2")}
|
|
};
|
|
|
|
HRESULT VideoFakeSink::DoRenderSample(IMediaSample *pMediaSample)
|
|
{
|
|
gboolean in_seg = FALSE;
|
|
guint64 clip_start = 0, clip_stop = 0;
|
|
GstDshowVideoDecClass *klass =
|
|
(GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (mDec);
|
|
GstBuffer *buf = NULL;
|
|
GstClockTime start, stop;
|
|
GstMapInfo map;
|
|
|
|
if(pMediaSample)
|
|
{
|
|
BYTE *pBuffer = NULL;
|
|
LONGLONG lStart = 0, lStop = 0;
|
|
long size = pMediaSample->GetActualDataLength();
|
|
|
|
pMediaSample->GetPointer(&pBuffer);
|
|
pMediaSample->GetTime(&lStart, &lStop);
|
|
|
|
start = lStart * 100;
|
|
stop = lStop * 100;
|
|
/* check if this buffer is in our current segment */
|
|
in_seg = gst_segment_clip (mDec->segment, GST_FORMAT_TIME,
|
|
start, stop, &clip_start, &clip_stop);
|
|
|
|
/* if the buffer is out of segment do not push it downstream */
|
|
if (!in_seg) {
|
|
GST_DEBUG_OBJECT (mDec,
|
|
"buffer is out of segment, start %" GST_TIME_FORMAT " stop %"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
|
|
goto done;
|
|
}
|
|
|
|
/* buffer is in our segment, allocate a new out buffer and clip its
|
|
* timestamps */
|
|
gst_buffer_pool_acquire_buffer(mDec->buffer_pool, &buf, NULL);
|
|
if (!buf) {
|
|
GST_WARNING_OBJECT (mDec,
|
|
"cannot allocate a new GstBuffer");
|
|
goto done;
|
|
}
|
|
|
|
/* set buffer properties */
|
|
GST_BUFFER_TIMESTAMP (buf) = clip_start;
|
|
GST_BUFFER_DURATION (buf) = clip_stop - clip_start;
|
|
|
|
gst_buffer_map(buf, &map, GST_MAP_WRITE);
|
|
if (strstr (klass->entry->srccaps, "rgb")) {
|
|
/* FOR RGB directshow decoder will return bottom-up BITMAP
|
|
* There is probably a way to get top-bottom video frames from
|
|
* the decoder...
|
|
*/
|
|
gint line = 0;
|
|
guint stride = mDec->width * 4;
|
|
|
|
for (; line < mDec->height; line++) {
|
|
memcpy (map.data + (line * stride),
|
|
pBuffer + (size - ((line + 1) * (stride))), stride);
|
|
}
|
|
} else {
|
|
memcpy (map.data, pBuffer, MIN ((unsigned int)size, map.size));
|
|
}
|
|
gst_buffer_unmap(buf, &map);
|
|
|
|
GST_LOG_OBJECT (mDec,
|
|
"push_buffer (size %d)=> pts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
|
|
" duration %" GST_TIME_FORMAT, size,
|
|
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
|
|
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf)),
|
|
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
|
|
|
|
/* push the buffer downstream */
|
|
mDec->last_ret = gst_pad_push (mDec->srcpad, buf);
|
|
}
|
|
done:
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT VideoFakeSink::CheckMediaType(const CMediaType *pmt)
|
|
{
|
|
if (pmt != NULL) {
|
|
if (*pmt == m_MediaType)
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_dshowvideodec_base_init (gpointer klass)
|
|
{
|
|
GstDshowVideoDecClass *videodec_class = (GstDshowVideoDecClass *) klass;
|
|
GstPadTemplate *src, *sink;
|
|
GstCaps *srccaps, *sinkcaps;
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
const VideoCodecEntry *tmp;
|
|
gpointer qdata;
|
|
gchar *longname, *description;
|
|
|
|
qdata = g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), DSHOW_CODEC_QDATA);
|
|
|
|
/* element details */
|
|
tmp = videodec_class->entry = (VideoCodecEntry *) qdata;
|
|
|
|
longname = g_strdup_printf ("DirectShow %s Decoder Wrapper",
|
|
tmp->element_longname);
|
|
description = g_strdup_printf ("DirectShow %s Decoder Wrapper",
|
|
tmp->element_longname);
|
|
|
|
gst_element_class_set_metadata(element_class, longname, "Codec/Decoder/Video", description,
|
|
"Sebastien Moutte <sebastien@moutte.net>");
|
|
|
|
g_free (longname);
|
|
g_free (description);
|
|
|
|
sinkcaps = gst_caps_from_string (tmp->sinkcaps);
|
|
gst_caps_set_simple (sinkcaps,
|
|
"width", GST_TYPE_INT_RANGE, 16, 4096,
|
|
"height", GST_TYPE_INT_RANGE, 16, 4096,
|
|
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
|
|
|
|
srccaps = gst_caps_from_string (tmp->srccaps);
|
|
|
|
sink = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sinkcaps);
|
|
src = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
|
|
|
|
gst_element_class_add_pad_template (element_class, src);
|
|
gst_element_class_add_pad_template (element_class, sink);
|
|
|
|
if (sinkcaps)
|
|
gst_caps_unref(sinkcaps);
|
|
|
|
if (srccaps)
|
|
gst_caps_unref(srccaps);
|
|
}
|
|
|
|
static void
|
|
gst_dshowvideodec_class_init (GstDshowVideoDecClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
gobject_class->finalize = gst_dshowvideodec_finalize;
|
|
|
|
gstelement_class->change_state =
|
|
GST_DEBUG_FUNCPTR (gst_dshowvideodec_change_state);
|
|
|
|
parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
|
|
}
|
|
|
|
static void
|
|
gst_dshowvideodec_com_thread (GstDshowVideoDec * vdec)
|
|
{
|
|
HRESULT res;
|
|
|
|
g_mutex_lock (&vdec->com_init_lock);
|
|
|
|
/* Initialize COM with a MTA for this process. This thread will
|
|
* be the first one to enter the apartement and the last one to leave
|
|
* it, unitializing COM properly */
|
|
|
|
res = CoInitializeEx (0, COINIT_MULTITHREADED);
|
|
if (res == S_FALSE)
|
|
GST_WARNING_OBJECT (vdec, "COM has been already initialized in the same process");
|
|
else if (res == RPC_E_CHANGED_MODE)
|
|
GST_WARNING_OBJECT (vdec, "The concurrency model of COM has changed.");
|
|
else
|
|
GST_INFO_OBJECT (vdec, "COM initialized successfully");
|
|
|
|
vdec->comInitialized = TRUE;
|
|
|
|
/* Signal other threads waiting on this condition that COM was initialized */
|
|
g_cond_signal (&vdec->com_initialized);
|
|
|
|
g_mutex_unlock (&vdec->com_init_lock);
|
|
|
|
/* Wait until the uninitialize condition is met to leave the COM apartement */
|
|
g_mutex_lock (&vdec->com_deinit_lock);
|
|
g_cond_wait (&vdec->com_uninitialize, &vdec->com_deinit_lock);
|
|
|
|
CoUninitialize ();
|
|
GST_INFO_OBJECT (vdec, "COM uninitialized successfully");
|
|
vdec->comInitialized = FALSE;
|
|
g_cond_signal (&vdec->com_uninitialized);
|
|
g_mutex_unlock (&vdec->com_deinit_lock);
|
|
}
|
|
|
|
static void
|
|
gst_dshowvideodec_init (GstDshowVideoDec * vdec)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_GET_CLASS (vdec);
|
|
|
|
/* setup pads */
|
|
vdec->sinkpad =
|
|
gst_pad_new_from_template (gst_element_class_get_pad_template
|
|
(element_class, "sink"), "sink");
|
|
|
|
gst_pad_set_event_function (vdec->sinkpad, gst_dshowvideodec_sink_event);
|
|
gst_pad_set_chain_function (vdec->sinkpad, gst_dshowvideodec_chain);
|
|
gst_element_add_pad (GST_ELEMENT (vdec), vdec->sinkpad);
|
|
|
|
vdec->srcpad =
|
|
gst_pad_new_from_template (gst_element_class_get_pad_template
|
|
(element_class, "src"), "src");
|
|
/* needed to implement caps negotiation on our src pad */
|
|
/* gst_pad_set_getcaps_function (vdec->srcpad, gst_dshowvideodec_src_getcaps);
|
|
gst_pad_set_setcaps_function (vdec->srcpad, gst_dshowvideodec_src_setcaps);*/
|
|
gst_element_add_pad (GST_ELEMENT (vdec), vdec->srcpad);
|
|
|
|
vdec->fakesrc = NULL;
|
|
vdec->fakesink = NULL;
|
|
vdec->decfilter = NULL;
|
|
|
|
vdec->last_ret = GST_FLOW_OK;
|
|
|
|
vdec->filtergraph = NULL;
|
|
vdec->mediafilter = NULL;
|
|
vdec->srccaps = NULL;
|
|
vdec->segment = gst_segment_new ();
|
|
|
|
vdec->setup = FALSE;
|
|
vdec->buffer_pool = NULL;
|
|
|
|
g_mutex_init (&vdec->com_init_lock);
|
|
g_mutex_init (&vdec->com_deinit_lock);
|
|
g_cond_init (&vdec->com_initialized);
|
|
g_cond_init (&vdec->com_uninitialize);
|
|
g_cond_init (&vdec->com_uninitialized);
|
|
|
|
g_mutex_lock (&vdec->com_init_lock);
|
|
|
|
/* create the COM initialization thread */
|
|
g_thread_new ("COM Init Thread", (GThreadFunc)gst_dshowvideodec_com_thread,
|
|
vdec);
|
|
|
|
/* wait until the COM thread signals that COM has been initialized */
|
|
g_cond_wait (&vdec->com_initialized, &vdec->com_init_lock);
|
|
g_mutex_unlock (&vdec->com_init_lock);
|
|
}
|
|
|
|
static void
|
|
gst_dshowvideodec_finalize (GObject * object)
|
|
{
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) (object);
|
|
|
|
if (vdec->segment) {
|
|
gst_segment_free (vdec->segment);
|
|
vdec->segment = NULL;
|
|
}
|
|
|
|
if(vdec->buffer_pool) {
|
|
gst_object_unref(vdec->buffer_pool);
|
|
vdec->buffer_pool = NULL;
|
|
}
|
|
|
|
/* signal the COM thread that it sould uninitialize COM */
|
|
if (vdec->comInitialized) {
|
|
g_mutex_lock (&vdec->com_deinit_lock);
|
|
g_cond_signal (&vdec->com_uninitialize);
|
|
g_cond_wait (&vdec->com_uninitialized, &vdec->com_deinit_lock);
|
|
g_mutex_unlock (&vdec->com_deinit_lock);
|
|
}
|
|
|
|
g_mutex_clear (&vdec->com_init_lock);
|
|
g_mutex_clear (&vdec->com_deinit_lock);
|
|
g_cond_clear (&vdec->com_initialized);
|
|
g_cond_clear (&vdec->com_uninitialize);
|
|
g_cond_clear (&vdec->com_uninitialized);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_dshowvideodec_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
if (!gst_dshowvideodec_create_graph_and_filters (vdec))
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
break;
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
break;
|
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
|
break;
|
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
|
break;
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
break;
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
if (!gst_dshowvideodec_destroy_graph_and_filters (vdec))
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_sink_setcaps (GstPad * pad, GstCaps * caps)
|
|
{
|
|
gboolean ret = FALSE;
|
|
HRESULT hres;
|
|
GstStructure *s = gst_caps_get_structure (caps, 0);
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
|
|
GstDshowVideoDecClass *klass =
|
|
(GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (vdec);
|
|
GstBuffer *extradata = NULL;
|
|
gsize extra_size;
|
|
const GValue *v = NULL;
|
|
guint size = 0;
|
|
GstCaps *caps_out = NULL;
|
|
AM_MEDIA_TYPE output_mediatype, input_mediatype;
|
|
VIDEOINFOHEADER *input_vheader = NULL, *output_vheader = NULL;
|
|
IPinPtr output_pin;
|
|
IPinPtr input_pin;
|
|
IBaseFilter *srcfilter = NULL;
|
|
IBaseFilter *sinkfilter = NULL;
|
|
const GValue *fps, *par;
|
|
GstQuery *query = NULL;
|
|
GstBufferPool *pool = NULL;
|
|
GstStructure *pool_config = NULL;
|
|
guint pool_size, pool_min, pool_max;
|
|
GstVideoInfo video_info;
|
|
|
|
/* read data */
|
|
if (!gst_structure_get_int (s, "width", &vdec->width) ||
|
|
!gst_structure_get_int (s, "height", &vdec->height)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("error getting video width or height from caps"), (NULL));
|
|
goto end;
|
|
}
|
|
fps = gst_structure_get_value (s, "framerate");
|
|
if (fps) {
|
|
vdec->fps_n = gst_value_get_fraction_numerator (fps);
|
|
vdec->fps_d = gst_value_get_fraction_denominator (fps);
|
|
}
|
|
else {
|
|
/* Invent a sane default framerate; the timestamps matter
|
|
* more anyway. */
|
|
vdec->fps_n = 25;
|
|
vdec->fps_d = 1;
|
|
}
|
|
|
|
par = gst_structure_get_value (s, "pixel-aspect-ratio");
|
|
if (par) {
|
|
vdec->par_n = gst_value_get_fraction_numerator (par);
|
|
vdec->par_d = gst_value_get_fraction_denominator (par);
|
|
}
|
|
else {
|
|
vdec->par_n = vdec->par_d = 1;
|
|
}
|
|
|
|
if ((v = gst_structure_get_value (s, "codec_data"))) {
|
|
extradata = gst_value_get_buffer (v);
|
|
extra_size = gst_buffer_get_size(extradata);
|
|
}
|
|
|
|
/* define the input type format */
|
|
memset (&input_mediatype, 0, sizeof (AM_MEDIA_TYPE));
|
|
input_mediatype.majortype = klass->entry->input_majortype;
|
|
input_mediatype.subtype = klass->entry->input_subtype;
|
|
input_mediatype.bFixedSizeSamples = FALSE;
|
|
input_mediatype.bTemporalCompression = TRUE;
|
|
|
|
if (strstr (klass->entry->sinkcaps, "video/mpeg, mpegversion= (int) 1")) {
|
|
size =
|
|
sizeof (MPEG1VIDEOINFO) + (extradata ? extra_size - 1 : 0);
|
|
input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size);
|
|
|
|
input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
|
|
if (extradata) {
|
|
MPEG1VIDEOINFO *mpeg_info = (MPEG1VIDEOINFO *) input_vheader;
|
|
|
|
gst_buffer_extract(extradata, 0, mpeg_info->bSequenceHeader, extra_size);
|
|
mpeg_info->cbSequenceHeader = extra_size;
|
|
}
|
|
input_mediatype.formattype = FORMAT_MPEGVideo;
|
|
} else {
|
|
size =
|
|
sizeof (VIDEOINFOHEADER) + (extradata ? extra_size : 0);
|
|
input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size);
|
|
input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
|
|
|
|
if (extradata) { /* Codec data is appended after our header */
|
|
gst_buffer_extract(extradata, 0,
|
|
((guchar *) input_vheader) + sizeof (VIDEOINFOHEADER), extra_size);
|
|
input_vheader->bmiHeader.biSize += extra_size;
|
|
}
|
|
input_mediatype.formattype = FORMAT_VideoInfo;
|
|
}
|
|
|
|
input_vheader->rcSource.top = input_vheader->rcSource.left = 0;
|
|
input_vheader->rcSource.right = vdec->width;
|
|
input_vheader->rcSource.bottom = vdec->height;
|
|
input_vheader->rcTarget = input_vheader->rcSource;
|
|
input_vheader->bmiHeader.biWidth = vdec->width;
|
|
input_vheader->bmiHeader.biHeight = vdec->height;
|
|
input_vheader->bmiHeader.biPlanes = 1;
|
|
input_vheader->bmiHeader.biBitCount = 16;
|
|
input_vheader->bmiHeader.biCompression = klass->entry->format;
|
|
input_vheader->bmiHeader.biSizeImage =
|
|
(vdec->width * vdec->height) * (input_vheader->bmiHeader.biBitCount / 8);
|
|
|
|
input_mediatype.cbFormat = size;
|
|
input_mediatype.pbFormat = (BYTE *) input_vheader;
|
|
input_mediatype.lSampleSize = input_vheader->bmiHeader.biSizeImage;
|
|
|
|
vdec->fakesrc->GetOutputPin()->SetMediaType(&input_mediatype);
|
|
|
|
/* set the sample size for fakesrc filter to the output buffer size */
|
|
vdec->fakesrc->GetOutputPin()->SetSampleSize(input_mediatype.lSampleSize);
|
|
|
|
/* connect our fake src to decoder */
|
|
hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
|
|
(void **) &srcfilter);
|
|
if (FAILED (hres)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't QT fakesrc to IBaseFilter: %x", hres), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
output_pin = gst_dshow_get_pin_from_filter (srcfilter, PINDIR_OUTPUT);
|
|
if (!output_pin) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't get output pin from our directshow fakesrc filter"), (NULL));
|
|
goto end;
|
|
}
|
|
input_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_INPUT);
|
|
if (!input_pin) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't get input pin from decoder filter"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
hres = vdec->filtergraph->ConnectDirect (output_pin, input_pin, NULL);
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't connect fakesrc with decoder (error=%x)", hres), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
/* get decoder output video format */
|
|
if (!gst_dshowvideodec_get_filter_output_format (vdec,
|
|
klass->entry->output_subtype, &output_vheader, &size)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't get decoder output video format"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
memset (&output_mediatype, 0, sizeof (AM_MEDIA_TYPE));
|
|
output_mediatype.majortype = klass->entry->output_majortype;
|
|
output_mediatype.subtype = klass->entry->output_subtype;
|
|
output_mediatype.bFixedSizeSamples = TRUE;
|
|
output_mediatype.bTemporalCompression = FALSE;
|
|
output_mediatype.lSampleSize = output_vheader->bmiHeader.biSizeImage;
|
|
output_mediatype.formattype = FORMAT_VideoInfo;
|
|
output_mediatype.cbFormat = size;
|
|
output_mediatype.pbFormat = (BYTE *) output_vheader;
|
|
|
|
vdec->fakesink->SetMediaType (&output_mediatype);
|
|
|
|
/* connect decoder to our fake sink */
|
|
output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
|
|
if (!output_pin) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't get output pin from our decoder filter"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
|
|
(void **) &sinkfilter);
|
|
if (FAILED (hres)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't QT fakesink to IBaseFilter: %x", hres), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
input_pin = gst_dshow_get_pin_from_filter (sinkfilter, PINDIR_INPUT);
|
|
if (!input_pin) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't get input pin from our directshow fakesink filter"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
hres = vdec->filtergraph->ConnectDirect(output_pin, input_pin,
|
|
&output_mediatype);
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't connect decoder with fakesink (error=%x)", hres), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
/* negotiate output */
|
|
caps_out = gst_caps_from_string (klass->entry->srccaps);
|
|
gst_caps_set_simple (caps_out,
|
|
"width", G_TYPE_INT, vdec->width,
|
|
"height", G_TYPE_INT, vdec->height, NULL);
|
|
|
|
if (vdec->fps_n && vdec->fps_d) {
|
|
gst_caps_set_simple (caps_out,
|
|
"framerate", GST_TYPE_FRACTION, vdec->fps_n, vdec->fps_d, NULL);
|
|
}
|
|
|
|
gst_caps_set_simple (caps_out,
|
|
"pixel-aspect-ratio", GST_TYPE_FRACTION, vdec->par_n, vdec->par_d, NULL);
|
|
|
|
if (!gst_pad_set_caps (vdec->srcpad, caps_out)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Failed to negotiate output"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
/* request or create a buffer pool */
|
|
if (vdec->buffer_pool) {
|
|
gst_object_unref (vdec->buffer_pool);
|
|
}
|
|
|
|
query = gst_query_new_allocation(caps_out, TRUE);
|
|
gst_pad_peer_query(vdec->srcpad, query);
|
|
|
|
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &pool_size, &pool_min,
|
|
&pool_max);
|
|
}
|
|
else {
|
|
pool = NULL;
|
|
pool_size = output_mediatype.lSampleSize;
|
|
pool_min = 1;
|
|
pool_max = 0;
|
|
}
|
|
|
|
if (pool == NULL) {
|
|
pool = gst_video_buffer_pool_new ();
|
|
}
|
|
|
|
if (!pool) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Could not create buffer bool"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
pool_config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (pool_config, caps_out, pool_size,
|
|
pool_min, pool_max);
|
|
gst_buffer_pool_set_config (pool, pool_config);
|
|
|
|
if (!gst_buffer_pool_set_active (pool, TRUE)) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Failed set buffer pool active"), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
vdec->buffer_pool = pool;
|
|
|
|
hres = vdec->mediafilter->Run (-1);
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("Can't run the directshow graph (error=%d)", hres), (NULL));
|
|
goto end;
|
|
}
|
|
|
|
ret = TRUE;
|
|
end:
|
|
if (caps_out)
|
|
gst_caps_unref (caps_out);
|
|
gst_object_unref (vdec);
|
|
g_free (input_vheader);
|
|
if (srcfilter)
|
|
srcfilter->Release();
|
|
if (sinkfilter)
|
|
sinkfilter->Release();
|
|
if (query)
|
|
gst_query_unref(query);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
{
|
|
gboolean ret = TRUE;
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_CAPS:
|
|
GstCaps *caps;
|
|
gst_event_parse_caps(event, &caps);
|
|
ret = gst_dshowvideodec_sink_setcaps(pad, caps);
|
|
break;
|
|
|
|
case GST_EVENT_FLUSH_STOP:
|
|
gst_dshowvideodec_flush (vdec);
|
|
ret = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
case GST_EVENT_SEGMENT:
|
|
{
|
|
const GstSegment *segment;
|
|
|
|
gst_event_parse_segment (event, &segment);
|
|
|
|
/* save the new segment in our local current segment */
|
|
gst_segment_copy_into(segment, vdec->segment);
|
|
|
|
GST_CAT_DEBUG_OBJECT (dshowvideodec_debug, vdec,
|
|
"new segment received => start=%" GST_TIME_FORMAT " stop=%"
|
|
GST_TIME_FORMAT, GST_TIME_ARGS (vdec->segment->start),
|
|
GST_TIME_ARGS (vdec->segment->stop));
|
|
|
|
ret = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
}
|
|
default:
|
|
ret = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
}
|
|
|
|
gst_object_unref (vdec);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_dshowvideodec_chain (GstPad * pad, GstObject *parent, GstBuffer * buffer)
|
|
{
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
|
|
bool discont = FALSE;
|
|
GstClockTime stop;
|
|
GstMapInfo map;
|
|
|
|
if (!vdec->setup) {
|
|
/* we are not setup */
|
|
GST_WARNING_OBJECT (vdec, "Decoder not set up, failing");
|
|
vdec->last_ret = GST_FLOW_FLUSHING;
|
|
goto beach;
|
|
}
|
|
|
|
if (vdec->last_ret != GST_FLOW_OK) {
|
|
GST_DEBUG_OBJECT (vdec, "last decoding iteration generated a fatal error "
|
|
"%s", gst_flow_get_name (vdec->last_ret));
|
|
goto beach;
|
|
}
|
|
|
|
/* check if duration is valid and use duration only when it's valid
|
|
/* because dshow is not decoding frames having stop smaller than start */
|
|
if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
|
|
stop = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
|
|
} else {
|
|
stop = GST_BUFFER_TIMESTAMP (buffer);
|
|
}
|
|
|
|
GST_CAT_LOG_OBJECT (dshowvideodec_debug, vdec,
|
|
"chain (size %d)=> pts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT,
|
|
gst_buffer_get_size (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
|
|
GST_TIME_ARGS (stop));
|
|
|
|
/* if the incoming buffer has discont flag set => flush decoder data */
|
|
if (buffer && GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
|
|
GST_CAT_DEBUG_OBJECT (dshowvideodec_debug, vdec,
|
|
"this buffer has a DISCONT flag (%" GST_TIME_FORMAT "), flushing",
|
|
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
|
|
gst_dshowvideodec_flush (vdec);
|
|
discont = TRUE;
|
|
}
|
|
|
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
|
/* push the buffer to the directshow decoder */
|
|
vdec->fakesrc->GetOutputPin()->PushBuffer(
|
|
map.data, GST_BUFFER_TIMESTAMP (buffer), stop,
|
|
map.size, discont);
|
|
gst_buffer_unmap(buffer, &map);
|
|
|
|
beach:
|
|
gst_buffer_unref (buffer);
|
|
gst_object_unref (vdec);
|
|
|
|
return vdec->last_ret;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_dshowvideodec_src_getcaps (GstPad * pad)
|
|
{
|
|
GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad);
|
|
GstCaps *caps = NULL;
|
|
|
|
if (!vdec->srccaps)
|
|
vdec->srccaps = gst_caps_new_empty ();
|
|
|
|
if (vdec->decfilter) {
|
|
IPinPtr output_pin;
|
|
IEnumMediaTypesPtr enum_mediatypes;
|
|
HRESULT hres;
|
|
ULONG fetched;
|
|
|
|
output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
|
|
if (!output_pin) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED,
|
|
("failed getting output pin from the decoder"), (NULL));
|
|
goto beach;
|
|
}
|
|
|
|
hres = output_pin->EnumMediaTypes (&enum_mediatypes);
|
|
if (hres == S_OK && enum_mediatypes) {
|
|
AM_MEDIA_TYPE *mediatype = NULL;
|
|
|
|
enum_mediatypes->Reset();
|
|
while (hres =
|
|
enum_mediatypes->Next(1, &mediatype, &fetched),
|
|
hres == S_OK)
|
|
{
|
|
VIDEOINFOHEADER *video_info;
|
|
GstCaps *mediacaps = NULL;
|
|
|
|
/* RGB24 */
|
|
if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_RGB24) &&
|
|
IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo))
|
|
{
|
|
video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
|
|
|
|
/* ffmpegcolorspace handles RGB24 in BIG_ENDIAN */
|
|
mediacaps = gst_caps_new_simple ("video/x-raw-rgb",
|
|
"bpp", G_TYPE_INT, 24,
|
|
"depth", G_TYPE_INT, 24,
|
|
"width", G_TYPE_INT, video_info->bmiHeader.biWidth,
|
|
"height", G_TYPE_INT, video_info->bmiHeader.biHeight,
|
|
"framerate", GST_TYPE_FRACTION,
|
|
(int) (10000000 / video_info->AvgTimePerFrame), 1, "endianness",
|
|
G_TYPE_INT, G_BIG_ENDIAN, "red_mask", G_TYPE_INT, 255,
|
|
"green_mask", G_TYPE_INT, 65280, "blue_mask", G_TYPE_INT,
|
|
16711680, NULL);
|
|
|
|
if (mediacaps) {
|
|
vdec->mediatypes = g_list_append (vdec->mediatypes, mediatype);
|
|
gst_caps_append (vdec->srccaps, mediacaps);
|
|
} else {
|
|
DeleteMediaType (mediatype);
|
|
}
|
|
} else {
|
|
DeleteMediaType (mediatype);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vdec->srccaps)
|
|
caps = gst_caps_ref (vdec->srccaps);
|
|
|
|
beach:
|
|
gst_object_unref (vdec);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_src_setcaps (GstPad * pad, GstCaps * caps)
|
|
{
|
|
gboolean ret = FALSE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_flush (GstDshowVideoDec * vdec)
|
|
{
|
|
if (!vdec->fakesrc)
|
|
return FALSE;
|
|
|
|
/* flush dshow decoder and reset timestamp */
|
|
vdec->fakesrc->GetOutputPin()->Flush();
|
|
vdec->last_ret = GST_FLOW_OK;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_get_filter_output_format (GstDshowVideoDec * vdec,
|
|
const GUID subtype, VIDEOINFOHEADER ** format, guint * size)
|
|
{
|
|
IPinPtr output_pin;
|
|
IEnumMediaTypesPtr enum_mediatypes;
|
|
HRESULT hres;
|
|
ULONG fetched;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!vdec->decfilter)
|
|
return FALSE;
|
|
|
|
output_pin = gst_dshow_get_pin_from_filter (vdec->decfilter, PINDIR_OUTPUT);
|
|
if (!output_pin) {
|
|
GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION,
|
|
("failed getting output pin from the decoder"), (NULL));
|
|
return FALSE;
|
|
}
|
|
|
|
hres = output_pin->EnumMediaTypes (&enum_mediatypes);
|
|
if (hres == S_OK && enum_mediatypes) {
|
|
AM_MEDIA_TYPE *mediatype = NULL;
|
|
|
|
enum_mediatypes->Reset();
|
|
while (hres =
|
|
enum_mediatypes->Next(1, &mediatype, &fetched),
|
|
hres == S_OK)
|
|
{
|
|
if (IsEqualGUID (mediatype->subtype, subtype) &&
|
|
IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo))
|
|
{
|
|
*size = mediatype->cbFormat;
|
|
*format = (VIDEOINFOHEADER *)g_malloc0 (*size);
|
|
memcpy (*format, mediatype->pbFormat, *size);
|
|
ret = TRUE;
|
|
}
|
|
DeleteMediaType (mediatype);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_create_graph_and_filters (GstDshowVideoDec * vdec)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
GstDshowVideoDecClass *klass =
|
|
(GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (vdec);
|
|
IBaseFilter *srcfilter = NULL;
|
|
IBaseFilter *sinkfilter = NULL;
|
|
gboolean ret = FALSE;
|
|
|
|
/* create the filter graph manager object */
|
|
hres = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IFilterGraph, (LPVOID *) & vdec->filtergraph);
|
|
if (hres != S_OK || !vdec->filtergraph) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't create an instance "
|
|
"of the directshow graph manager (error=%d)", hres), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
hres = vdec->filtergraph->QueryInterface(IID_IMediaFilter,
|
|
(void **) &vdec->mediafilter);
|
|
if (hres != S_OK || !vdec->mediafilter) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED,
|
|
("Can't get IMediacontrol interface "
|
|
"from the graph manager (error=%d)", hres), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
/* create fake src filter */
|
|
vdec->fakesrc = new FakeSrc();
|
|
/* Created with a refcount of zero, so increment that */
|
|
vdec->fakesrc->AddRef();
|
|
|
|
hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
|
|
(void **) &srcfilter);
|
|
if (FAILED (hres)) {
|
|
GST_WARNING_OBJECT (vdec, "Failed to QI fakesrc to IBaseFilter");
|
|
goto error;
|
|
}
|
|
|
|
/* search a decoder filter and create it */
|
|
vdec->decfilter = gst_dshow_find_filter (
|
|
klass->entry->input_majortype,
|
|
klass->entry->input_subtype,
|
|
klass->entry->output_majortype,
|
|
klass->entry->output_subtype,
|
|
klass->entry->preferred_filters);
|
|
if (vdec->decfilter == NULL) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't create an instance "
|
|
"of the decoder filter"), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
/* create fake sink filter */
|
|
vdec->fakesink = new VideoFakeSink(vdec);
|
|
/* Created with a refcount of zero, so increment that */
|
|
vdec->fakesink->AddRef();
|
|
|
|
hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
|
|
(void **) &sinkfilter);
|
|
if (FAILED (hres)) {
|
|
GST_WARNING_OBJECT (vdec, "Failed to QI fakesink to IBaseFilter");
|
|
goto error;
|
|
}
|
|
|
|
/* add filters to the graph */
|
|
hres = vdec->filtergraph->AddFilter (srcfilter, L"src");
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add fakesrc filter "
|
|
"to the graph (error=%d)", hres), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
hres = vdec->filtergraph->AddFilter(vdec->decfilter, L"decoder");
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add decoder filter "
|
|
"to the graph (error=%d)", hres), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
hres = vdec->filtergraph->AddFilter(sinkfilter, L"sink");
|
|
if (hres != S_OK) {
|
|
GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Can't add fakesink filter "
|
|
"to the graph (error=%d)", hres), (NULL));
|
|
goto error;
|
|
}
|
|
|
|
vdec->setup = TRUE;
|
|
|
|
ret = TRUE;
|
|
|
|
done:
|
|
if (srcfilter)
|
|
srcfilter->Release();
|
|
if (sinkfilter)
|
|
sinkfilter->Release();
|
|
return ret;
|
|
|
|
error:
|
|
if (vdec->fakesrc) {
|
|
vdec->fakesrc->Release();
|
|
vdec->fakesrc = NULL;
|
|
}
|
|
if (vdec->decfilter) {
|
|
vdec->decfilter->Release();
|
|
vdec->decfilter = NULL;
|
|
}
|
|
if (vdec->fakesink) {
|
|
vdec->fakesink->Release();
|
|
vdec->fakesink = NULL;
|
|
}
|
|
if (vdec->mediafilter) {
|
|
vdec->mediafilter->Release();
|
|
vdec->mediafilter = NULL;
|
|
}
|
|
if (vdec->filtergraph) {
|
|
vdec->filtergraph->Release();
|
|
vdec->filtergraph = NULL;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
static gboolean
|
|
gst_dshowvideodec_destroy_graph_and_filters (GstDshowVideoDec * vdec)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if (vdec->mediafilter) {
|
|
vdec->mediafilter->Stop();
|
|
}
|
|
|
|
if (vdec->fakesrc) {
|
|
if (vdec->filtergraph) {
|
|
IBaseFilter *filter;
|
|
hres = vdec->fakesrc->QueryInterface(IID_IBaseFilter,
|
|
(void **) &filter);
|
|
if (SUCCEEDED (hres)) {
|
|
vdec->filtergraph->RemoveFilter(filter);
|
|
filter->Release();
|
|
}
|
|
}
|
|
|
|
vdec->fakesrc->Release();
|
|
vdec->fakesrc = NULL;
|
|
}
|
|
if (vdec->decfilter) {
|
|
if (vdec->filtergraph)
|
|
vdec->filtergraph->RemoveFilter(vdec->decfilter);
|
|
vdec->decfilter->Release();
|
|
vdec->decfilter = NULL;
|
|
}
|
|
if (vdec->fakesink) {
|
|
if (vdec->filtergraph) {
|
|
IBaseFilter *filter;
|
|
hres = vdec->fakesink->QueryInterface(IID_IBaseFilter,
|
|
(void **) &filter);
|
|
if (SUCCEEDED (hres)) {
|
|
vdec->filtergraph->RemoveFilter(filter);
|
|
filter->Release();
|
|
}
|
|
}
|
|
|
|
vdec->fakesink->Release();
|
|
vdec->fakesink = NULL;
|
|
}
|
|
if (vdec->mediafilter) {
|
|
vdec->mediafilter->Release();
|
|
vdec->mediafilter = NULL;
|
|
}
|
|
if (vdec->filtergraph) {
|
|
vdec->filtergraph->Release();
|
|
vdec->filtergraph = NULL;
|
|
}
|
|
|
|
vdec->setup = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
dshow_vdec_register (GstPlugin * plugin)
|
|
{
|
|
GTypeInfo info = {
|
|
sizeof (GstDshowVideoDecClass),
|
|
(GBaseInitFunc) gst_dshowvideodec_base_init,
|
|
NULL,
|
|
(GClassInitFunc) gst_dshowvideodec_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GstDshowVideoDec),
|
|
0,
|
|
(GInstanceInitFunc) gst_dshowvideodec_init,
|
|
};
|
|
gint i;
|
|
HRESULT hr;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (dshowvideodec_debug, "dshowvideodec", 0,
|
|
"Directshow filter video decoder");
|
|
|
|
hr = CoInitialize (0);
|
|
|
|
for (i = 0; i < sizeof (video_dec_codecs) / sizeof (VideoCodecEntry); i++) {
|
|
GType type;
|
|
IBaseFilterPtr filter;
|
|
guint rank = GST_RANK_MARGINAL;
|
|
|
|
filter = gst_dshow_find_filter (
|
|
video_dec_codecs[i].input_majortype,
|
|
video_dec_codecs[i].input_subtype,
|
|
video_dec_codecs[i].output_majortype,
|
|
video_dec_codecs[i].output_subtype,
|
|
video_dec_codecs[i].preferred_filters);
|
|
if (filter != NULL) {
|
|
|
|
if (video_dec_codecs[i].format == GST_MAKE_FOURCC ('W', 'V', 'C', '1')) {
|
|
/* FFMPEG WVC1 decoder sucks, get higher priority for ours */
|
|
rank = GST_RANK_MARGINAL + 2;
|
|
}
|
|
GST_DEBUG ("Registering %s with rank %u", video_dec_codecs[i].element_name, rank);
|
|
|
|
type = g_type_register_static (GST_TYPE_ELEMENT,
|
|
video_dec_codecs[i].element_name, &info, (GTypeFlags)0);
|
|
g_type_set_qdata (type, DSHOW_CODEC_QDATA, (gpointer) (video_dec_codecs + i));
|
|
if (!gst_element_register (plugin, video_dec_codecs[i].element_name, rank, type)) {
|
|
return FALSE;
|
|
}
|
|
GST_DEBUG ("Registered %s", video_dec_codecs[i].element_name);
|
|
} else {
|
|
GST_DEBUG ("Element %s not registered "
|
|
"(the format is not supported by the system)",
|
|
video_dec_codecs[i].element_name);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
CoUninitialize ();
|
|
|
|
return TRUE;
|
|
}
|