diff options
author | Michael Smith <msmith@xiph.org> | 2008-09-10 23:15:11 +0000 |
---|---|---|
committer | Michael Smith <msmith@xiph.org> | 2008-09-10 23:15:11 +0000 |
commit | 007478f09c8c10a24b473d6ac8eef1929ce25ca0 (patch) | |
tree | 7cd3ed7569f937c69df398d48e72eefe7a7753a2 | |
parent | 61dee512910cd2c9e407cc0679a492cc57333194 (diff) | |
download | gst-plugins-bad-007478f09c8c10a24b473d6ac8eef1929ce25ca0.tar.gz gst-plugins-bad-007478f09c8c10a24b473d6ac8eef1929ce25ca0.tar.bz2 gst-plugins-bad-007478f09c8c10a24b473d6ac8eef1929ce25ca0.zip |
sys/dshowdecwrapper/: Major rewrite of dshowdecwrapper. Converts code to
Original commit message from CVS:
* sys/dshowdecwrapper/Makefile.am:
* sys/dshowdecwrapper/gstdshowaudiodec.c:
* sys/dshowdecwrapper/gstdshowaudiodec.cpp:
* sys/dshowdecwrapper/gstdshowaudiodec.h:
* sys/dshowdecwrapper/gstdshowdecwrapper.c:
* sys/dshowdecwrapper/gstdshowdecwrapper.cpp:
* sys/dshowdecwrapper/gstdshowdecwrapper.h:
* sys/dshowdecwrapper/gstdshowfakesrc.cpp:
* sys/dshowdecwrapper/gstdshowfakesrc.h:
* sys/dshowdecwrapper/gstdshowutil.cpp:
* sys/dshowdecwrapper/gstdshowutil.h:
* sys/dshowdecwrapper/gstdshowvideodec.c:
* sys/dshowdecwrapper/gstdshowvideodec.cpp:
* sys/dshowdecwrapper/gstdshowvideodec.h:
Major rewrite of dshowdecwrapper. Converts code to
C++, moves to direct use of DirectShow base classes,
make a lot of code clearer, simplify, etc.
Fix decode of MP3 on Vista by working around an apparent
bug in the decoder.
-rw-r--r-- | ChangeLog | 22 | ||||
-rw-r--r-- | sys/dshowdecwrapper/Makefile.am | 11 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowaudiodec.cpp | 1074 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowaudiodec.h | 56 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowdecwrapper.cpp | 73 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowfakesrc.cpp | 153 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowfakesrc.h | 65 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowutil.cpp | 156 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowutil.h | 42 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowvideodec.cpp | 1170 | ||||
-rw-r--r-- | sys/dshowdecwrapper/gstdshowvideodec.h | 56 |
11 files changed, 2862 insertions, 16 deletions
@@ -1,3 +1,25 @@ +2008-09-10 Michael Smith <msmith@songbirdnest.com> + + * sys/dshowdecwrapper/Makefile.am: + * sys/dshowdecwrapper/gstdshowaudiodec.c: + * sys/dshowdecwrapper/gstdshowaudiodec.cpp: + * sys/dshowdecwrapper/gstdshowaudiodec.h: + * sys/dshowdecwrapper/gstdshowdecwrapper.c: + * sys/dshowdecwrapper/gstdshowdecwrapper.cpp: + * sys/dshowdecwrapper/gstdshowdecwrapper.h: + * sys/dshowdecwrapper/gstdshowfakesrc.cpp: + * sys/dshowdecwrapper/gstdshowfakesrc.h: + * sys/dshowdecwrapper/gstdshowutil.cpp: + * sys/dshowdecwrapper/gstdshowutil.h: + * sys/dshowdecwrapper/gstdshowvideodec.c: + * sys/dshowdecwrapper/gstdshowvideodec.cpp: + * sys/dshowdecwrapper/gstdshowvideodec.h: + Major rewrite of dshowdecwrapper. Converts code to + C++, moves to direct use of DirectShow base classes, + make a lot of code clearer, simplify, etc. + Fix decode of MP3 on Vista by working around an apparent + bug in the decoder. + 2008-09-10 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> * sys/winks/gstksclock.c (gst_ks_clock_worker_thread_func, diff --git a/sys/dshowdecwrapper/Makefile.am b/sys/dshowdecwrapper/Makefile.am index 7e9782bf..8b48452a 100644 --- a/sys/dshowdecwrapper/Makefile.am +++ b/sys/dshowdecwrapper/Makefile.am @@ -1,9 +1,14 @@ plugin_LTLIBRARIES = libgstdshowdecwrapper.la -libgstdshowdecwrapper_la_SOURCES = gstdshowaudiodec.c gstdshowdecwrapper.c gstdshowvideodec.c +libgstdshowdecwrapper_la_SOURCES = gstdshowaudiodec.cpp gstdshowdecwrapper.cpp \ + gstdshowvideodec.cpp gstdshowutil.cpp \ + gstdshowfakesrc.cpp libgstdshowdecwrapper_la_CFLAGS = $(GST_CFLAGS) -I$(top_srcdir)/gst-libs/gst -DLIBGSTDSHOWDECWRAPPER_EXPORTS -libgstdshowdecwrapper_la_LIBADD = $(GST_LIBS) $(top_builddir)/gst-libs/gst/dshow/libgstdshow-@GST_MAJORMINOR@.la +libgstdshowdecwrapper_la_LIBADD = $(top_builddir)/gst-libs/gst/dshow/libgstdshow-@GST_MAJORMINOR@.la \ + $(GST_BASE_LIBS) $(GST_LIBS) libgstdshowdecwrapper_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstdshowaudiodec.h gstdshowdecwrapper.h gstdshowvideodec.h +noinst_HEADERS = gstdshowaudiodec.h gstdshowvideodec.h gstdshowfakesrc.h \ + gstdshowutil.h + diff --git a/sys/dshowdecwrapper/gstdshowaudiodec.cpp b/sys/dshowdecwrapper/gstdshowaudiodec.cpp new file mode 100644 index 00000000..dd2d98d7 --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowaudiodec.cpp @@ -0,0 +1,1074 @@ +/* + * GStreamer DirectShow codecs wrapper + * Copyright <2006, 2007, 2008> Fluendo <gstreamer@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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdshowaudiodec.h" +#include <mmreg.h> + +GST_DEBUG_CATEGORY_STATIC (dshowaudiodec_debug); +#define GST_CAT_DEFAULT dshowaudiodec_debug + +GST_BOILERPLATE (GstDshowAudioDec, gst_dshowaudiodec, GstElement, + GST_TYPE_ELEMENT); + +static const AudioCodecEntry *tmp; + +static void gst_dshowaudiodec_dispose (GObject * object); +static GstStateChangeReturn gst_dshowaudiodec_change_state + (GstElement * element, GstStateChange transition); + +/* sink pad overwrites */ +static gboolean gst_dshowaudiodec_sink_setcaps (GstPad * pad, GstCaps * caps); +static GstFlowReturn gst_dshowaudiodec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_dshowaudiodec_sink_event (GstPad * pad, GstEvent * event); + +/* utils */ +static gboolean gst_dshowaudiodec_create_graph_and_filters (GstDshowAudioDec * + adec); +static gboolean gst_dshowaudiodec_destroy_graph_and_filters (GstDshowAudioDec * + adec); +static gboolean gst_dshowaudiodec_flush (GstDshowAudioDec * adec); +static gboolean gst_dshowaudiodec_get_filter_settings (GstDshowAudioDec * adec); +static gboolean gst_dshowaudiodec_setup_graph (GstDshowAudioDec * adec, GstCaps *caps); + +/* All the GUIDs we want are generated from the FOURCC like this */ +#define GUID_MEDIASUBTYPE_FROM_FOURCC(fourcc) \ + { fourcc , 0x0000, 0x0010, \ + { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} + +static const AudioCodecEntry audio_dec_codecs[] = { + {"dshowadec_wma1", + "Windows Media Audio 7", + WAVE_FORMAT_MSAUDIO1, + "audio/x-wma, wmaversion = (int) 1"}, + {"dshowadec_wma2", + "Windows Media Audio 8", + WAVE_FORMAT_WMAUDIO2, + "audio/x-wma, wmaversion = (int) 2"}, + {"dshowadec_wma3", + "Windows Media Audio 9 Professional", + WAVE_FORMAT_WMAUDIO3, + "audio/x-wma, wmaversion = (int) 3"}, + {"dshowadec_wma4", + "Windows Media Audio 9 Lossless", + WAVE_FORMAT_WMAUDIO_LOSSLESS, + "audio/x-wma, wmaversion = (int) 4"}, + {"dshowadec_wms", + "Windows Media Audio Voice v9", + WAVE_FORMAT_WMAVOICE9, + "audio/x-wms"}, + {"dshowadec_mp3", + "MPEG Layer 3 Audio", + WAVE_FORMAT_MPEGLAYER3, + "audio/mpeg, " + "mpegversion = (int) 1, " + "layer = (int)3, " + "rate = (int) [ 8000, 48000 ], " + "channels = (int) [ 1, 2 ], " + "parsed= (boolean) true"}, + {"dshowadec_mpeg_1_2", + "MPEG Layer 1,2 Audio", + WAVE_FORMAT_MPEG, + "audio/mpeg, " + "mpegversion = (int) 1, " + "layer = (int) [ 1, 2 ], " + "rate = (int) [ 8000, 48000 ], " + "channels = (int) [ 1, 2 ], " + "parsed= (boolean) true"}, +}; + +HRESULT AudioFakeSink::DoRenderSample(IMediaSample *pMediaSample) +{ + GstBuffer *out_buf = NULL; + gboolean in_seg = FALSE; + GstClockTime buf_start, buf_stop; + gint64 clip_start = 0, clip_stop = 0; + guint start_offset = 0, stop_offset; + GstClockTime duration; + + if(pMediaSample) + { + BYTE *pBuffer = NULL; + LONGLONG lStart = 0, lStop = 0; + long size = pMediaSample->GetActualDataLength(); + + pMediaSample->GetPointer(&pBuffer); + pMediaSample->GetTime(&lStart, &lStop); + + if (!GST_CLOCK_TIME_IS_VALID (mDec->timestamp)) { + // Convert REFERENCE_TIME to GST_CLOCK_TIME + mDec->timestamp = (GstClockTime)lStart * 100; + } + duration = (lStop - lStart) * 100; + + buf_start = mDec->timestamp; + buf_stop = mDec->timestamp + duration; + + /* save stop position to start next buffer with it */ + mDec->timestamp = buf_stop; + + /* check if this buffer is in our current segment */ + in_seg = gst_segment_clip (mDec->segment, GST_FORMAT_TIME, + buf_start, buf_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 (buf_start), GST_TIME_ARGS (buf_stop)); + goto done; + } + + /* buffer is entirely or partially in-segment, so allocate a + * GstBuffer for output, and clip if required */ + + /* allocate a new buffer for raw audio */ + mDec->last_ret = gst_pad_alloc_buffer (mDec->srcpad, + GST_BUFFER_OFFSET_NONE, + size, + GST_PAD_CAPS (mDec->srcpad), &out_buf); + if (!out_buf) { + GST_WARNING_OBJECT (mDec, "cannot allocate a new GstBuffer"); + goto done; + } + + /* set buffer properties */ + GST_BUFFER_TIMESTAMP (out_buf) = buf_start; + GST_BUFFER_DURATION (out_buf) = duration; + memcpy (GST_BUFFER_DATA (out_buf), pBuffer, + MIN ((unsigned int)size, GST_BUFFER_SIZE (out_buf))); + + /* we have to remove some heading samples */ + if (clip_start > buf_start) { + start_offset = (guint)gst_util_uint64_scale_int (clip_start - buf_start, + mDec->rate, GST_SECOND) * mDec->depth / 8 * mDec->channels; + } + else + start_offset = 0; + /* we have to remove some trailing samples */ + if (clip_stop < buf_stop) { + stop_offset = (guint)gst_util_uint64_scale_int (buf_stop - clip_stop, + mDec->rate, GST_SECOND) * mDec->depth / 8 * mDec->channels; + } + else + stop_offset = size; + + /* truncating */ + if ((start_offset != 0) || (stop_offset != (size_t) size)) { + GstBuffer *subbuf = gst_buffer_create_sub (out_buf, start_offset, + stop_offset - start_offset); + + if (subbuf) { + gst_buffer_set_caps (subbuf, GST_PAD_CAPS (mDec->srcpad)); + gst_buffer_unref (out_buf); + out_buf = subbuf; + } + } + + GST_BUFFER_TIMESTAMP (out_buf) = clip_start; + GST_BUFFER_DURATION (out_buf) = clip_stop - clip_start; + + /* replace the saved stop position by the clipped one */ + mDec->timestamp = clip_stop; + + GST_DEBUG_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 (out_buf)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out_buf) + + GST_BUFFER_DURATION (out_buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (out_buf))); + + mDec->last_ret = gst_pad_push (mDec->srcpad, out_buf); + } + +done: + return S_OK; +} + +HRESULT AudioFakeSink::CheckMediaType(const CMediaType *pmt) +{ + if(pmt != NULL) + { + /* The Vista MP3 decoder (and possibly others?) outputs an + * AM_MEDIA_TYPE with the wrong cbFormat. So, rather than using + * CMediaType.operator==, we implement a sufficient check ourselves. + * I think this is a bug in the MP3 decoder. + */ + if (IsEqualGUID (pmt->majortype, m_MediaType.majortype) && + IsEqualGUID (pmt->subtype, m_MediaType.subtype) && + IsEqualGUID (pmt->formattype, m_MediaType.formattype)) + { + /* Types are the same at the top-level. Now, we need to compare + * the format blocks. + * We special case WAVEFORMATEX to not check that + * pmt->cbFormat == m_MediaType.cbFormat, though the actual format + * blocks must still be the same. + */ + if (pmt->formattype == FORMAT_WaveFormatEx) { + if (pmt->cbFormat >= sizeof (WAVEFORMATEX) && + m_MediaType.cbFormat >= sizeof (WAVEFORMATEX)) + { + WAVEFORMATEX *wf1 = (WAVEFORMATEX *)pmt->pbFormat; + WAVEFORMATEX *wf2 = (WAVEFORMATEX *)m_MediaType.pbFormat; + if (wf1->cbSize == wf2->cbSize && + memcmp (wf1, wf2, sizeof(WAVEFORMATEX) + wf1->cbSize) == 0) + return S_OK; + } + } + else { + if (pmt->cbFormat == m_MediaType.cbFormat && + pmt->cbFormat == 0 || + (pmt->pbFormat != NULL && m_MediaType.pbFormat != NULL && + memcmp (pmt->pbFormat, m_MediaType.pbFormat, pmt->cbFormat) == 0)) + return S_OK; + } + } + } + + return S_FALSE; +} + +static void +gst_dshowaudiodec_base_init (gpointer klass) +{ + GstDshowAudioDecClass *audiodec_class = (GstDshowAudioDecClass *)klass; + GstPadTemplate *src, *sink; + GstCaps *srccaps, *sinkcaps; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstElementDetails details; + + audiodec_class->entry = tmp; + details.longname = g_strdup_printf ("DirectShow %s Decoder Wrapper", + tmp->element_longname); + details.klass = g_strdup ("Codec/Decoder/Audio"); + details.description = g_strdup_printf ("DirectShow %s Decoder Wrapper", + tmp->element_longname); + details.author = "Sebastien Moutte <sebastien@moutte.net>"; + gst_element_class_set_details (element_class, &details); + g_free (details.longname); + g_free (details.klass); + g_free (details.description); + + sinkcaps = gst_caps_from_string (tmp->sinkcaps); + + srccaps = gst_caps_from_string ( + "audio/x-raw-int," + "width = (int)[1, 32]," + "depth = (int)[1, 32]," + "rate = (int)[1, MAX]," + "channels = (int)[1, MAX]," + "signed = (boolean)true," + "endianness = (int)" G_STRINGIFY(G_LITTLE_ENDIAN)); + + 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); + + /* register */ + gst_element_class_add_pad_template (element_class, src); + gst_element_class_add_pad_template (element_class, sink); +} + +static void +gst_dshowaudiodec_class_init (GstDshowAudioDecClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dshowaudiodec_dispose); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_dshowaudiodec_change_state); + + if (!parent_class) + parent_class = (GstElementClass *)g_type_class_ref (GST_TYPE_ELEMENT); + + if (!dshowaudiodec_debug) { + GST_DEBUG_CATEGORY_INIT (dshowaudiodec_debug, "dshowaudiodec", 0, + "Directshow filter audio decoder"); + } +} + +static void +gst_dshowaudiodec_init (GstDshowAudioDec * adec, + GstDshowAudioDecClass * adec_class) +{ + GstElementClass *element_class = GST_ELEMENT_GET_CLASS (adec); + HRESULT hr; + + /* setup pads */ + adec->sinkpad = + gst_pad_new_from_template (gst_element_class_get_pad_template + (element_class, "sink"), "sink"); + + gst_pad_set_setcaps_function (adec->sinkpad, gst_dshowaudiodec_sink_setcaps); + gst_pad_set_event_function (adec->sinkpad, gst_dshowaudiodec_sink_event); + gst_pad_set_chain_function (adec->sinkpad, gst_dshowaudiodec_chain); + gst_element_add_pad (GST_ELEMENT (adec), adec->sinkpad); + + adec->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template + (element_class, "src"), "src"); + gst_element_add_pad (GST_ELEMENT (adec), adec->srcpad); + + adec->fakesrc = NULL; + adec->fakesink = NULL; + + adec->decfilter = 0; + adec->filtergraph = 0; + adec->mediafilter = 0; + + adec->timestamp = GST_CLOCK_TIME_NONE; + adec->segment = gst_segment_new (); + adec->setup = FALSE; + adec->depth = 0; + adec->bitrate = 0; + adec->block_align = 0; + adec->channels = 0; + adec->rate = 0; + adec->layer = 0; + adec->codec_data = NULL; + + adec->last_ret = GST_FLOW_OK; + + hr = CoInitialize (0); + if (SUCCEEDED(hr)) { + adec->comInitialized = TRUE; + } +} + +static void +gst_dshowaudiodec_dispose (GObject * object) +{ + GstDshowAudioDec *adec = (GstDshowAudioDec *) (object); + + if (adec->segment) { + gst_segment_free (adec->segment); + adec->segment = NULL; + } + + if (adec->codec_data) { + gst_buffer_unref (adec->codec_data); + adec->codec_data = NULL; + } + + if (adec->comInitialized) { + CoUninitialize (); + adec->comInitialized = FALSE; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + + +static GstStateChangeReturn +gst_dshowaudiodec_change_state (GstElement * element, GstStateChange transition) +{ + GstDshowAudioDec *adec = (GstDshowAudioDec *) (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!gst_dshowaudiodec_create_graph_and_filters (adec)) + 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: + adec->depth = 0; + adec->bitrate = 0; + adec->block_align = 0; + adec->channels = 0; + adec->rate = 0; + adec->layer = 0; + if (adec->codec_data) { + gst_buffer_unref (adec->codec_data); + adec->codec_data = NULL; + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_dshowaudiodec_destroy_graph_and_filters (adec)) + return GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +} + +static gboolean +gst_dshowaudiodec_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + gboolean ret = FALSE; + GstDshowAudioDec *adec = (GstDshowAudioDec *) gst_pad_get_parent (pad); + GstStructure *s = gst_caps_get_structure (caps, 0); + const GValue *v = NULL; + + adec->timestamp = GST_CLOCK_TIME_NONE; + + /* read data, only rate and channels are needed */ + if (!gst_structure_get_int (s, "rate", &adec->rate) || + !gst_structure_get_int (s, "channels", &adec->channels)) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("error getting audio specs from caps"), (NULL)); + goto end; + } + + gst_structure_get_int (s, "depth", &adec->depth); + gst_structure_get_int (s, "bitrate", &adec->bitrate); + gst_structure_get_int (s, "block_align", &adec->block_align); + gst_structure_get_int (s, "layer", &adec->layer); + + if (adec->codec_data) { + gst_buffer_unref (adec->codec_data); + adec->codec_data = NULL; + } + + if ((v = gst_structure_get_value (s, "codec_data"))) + adec->codec_data = gst_buffer_ref (gst_value_get_buffer (v)); + + ret = gst_dshowaudiodec_setup_graph (adec, caps); +end: + gst_object_unref (adec); + + return ret; +} + +static GstFlowReturn +gst_dshowaudiodec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstDshowAudioDec *adec = (GstDshowAudioDec *) gst_pad_get_parent (pad); + bool discont = FALSE; + + if (!adec->setup) { + /* we are not set up */ + GST_WARNING_OBJECT (adec, "Decoder not set up, failing"); + adec->last_ret = GST_FLOW_WRONG_STATE; + goto beach; + } + + if (GST_FLOW_IS_FATAL (adec->last_ret)) { + GST_DEBUG_OBJECT (adec, "last decoding iteration generated a fatal error " + "%s", gst_flow_get_name (adec->last_ret)); + goto beach; + } + + GST_CAT_DEBUG_OBJECT (dshowaudiodec_debug, adec, "chain (size %d)=> pts %" + GST_TIME_FORMAT " stop %" GST_TIME_FORMAT, + GST_BUFFER_SIZE (buffer), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer))); + + /* 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 (dshowaudiodec_debug, adec, + "this buffer has a DISCONT flag (%" GST_TIME_FORMAT "), flushing", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + gst_dshowaudiodec_flush (adec); + discont = TRUE; + } + + /* push the buffer to the directshow decoder */ + adec->fakesrc->GetOutputPin()->PushBuffer ( + GST_BUFFER_DATA (buffer), GST_BUFFER_TIMESTAMP (buffer), + GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer), + GST_BUFFER_SIZE (buffer), (bool)discont); + +beach: + gst_buffer_unref (buffer); + gst_object_unref (adec); + return adec->last_ret; +} + +static gboolean +gst_dshowaudiodec_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean ret = TRUE; + GstDshowAudioDec *adec = (GstDshowAudioDec *) gst_pad_get_parent (pad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP:{ + gst_dshowaudiodec_flush (adec); + ret = gst_pad_event_default (pad, event); + break; + } + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &time); + + GST_CAT_DEBUG_OBJECT (dshowaudiodec_debug, adec, + "received new segment from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + + if (update) { + GST_CAT_DEBUG_OBJECT (dshowaudiodec_debug, adec, + "closing current segment flushing.."); + gst_dshowaudiodec_flush (adec); + } + + /* save the new segment in our local current segment */ + gst_segment_set_newsegment (adec->segment, update, rate, format, start, + stop, time); + + ret = gst_pad_event_default (pad, event); + break; + } + default: + ret = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (adec); + + return ret; +} + +static gboolean +gst_dshowaudiodec_flush (GstDshowAudioDec * adec) +{ + if (!adec->fakesrc) + return FALSE; + + /* flush dshow decoder and reset timestamp */ + adec->fakesrc->GetOutputPin()->Flush(); + adec->timestamp = GST_CLOCK_TIME_NONE; + + return TRUE; +} + +static AM_MEDIA_TYPE * +dshowaudiodec_set_input_format (GstDshowAudioDec *adec, GstCaps *caps) +{ + AM_MEDIA_TYPE *mediatype; + WAVEFORMATEX *format; + GstDshowAudioDecClass *klass = + (GstDshowAudioDecClass *) G_OBJECT_GET_CLASS (adec); + const AudioCodecEntry *codec_entry = klass->entry; + int size; + + mediatype = (AM_MEDIA_TYPE *)g_malloc0 (sizeof(AM_MEDIA_TYPE)); + mediatype->majortype = MEDIATYPE_Audio; + GUID subtype = GUID_MEDIASUBTYPE_FROM_FOURCC (0x00000000); + subtype.Data1 = codec_entry->format; + mediatype->subtype = subtype; + mediatype->bFixedSizeSamples = TRUE; + mediatype->bTemporalCompression = FALSE; + if (adec->block_align) + mediatype->lSampleSize = adec->block_align; + else + mediatype->lSampleSize = 8192; /* need to evaluate it dynamically */ + mediatype->formattype = FORMAT_WaveFormatEx; + + /* We need this special behaviour for layers 1 and 2 (layer 3 uses a different + * decoder which doesn't need this */ + if (adec->layer == 1 || adec->layer == 2) { + MPEG1WAVEFORMAT *mpeg1_format; + int version, samples; + GstStructure *structure = gst_caps_get_structure (caps, 0); + + size = sizeof (MPEG1WAVEFORMAT); + format = (WAVEFORMATEX *)g_malloc0 (size); + format->cbSize = sizeof (MPEG1WAVEFORMAT) - sizeof (WAVEFORMATEX); + format->wFormatTag = WAVE_FORMAT_MPEG; + + mpeg1_format = (MPEG1WAVEFORMAT *) format; + + mpeg1_format->wfx.nChannels = adec->channels; + if (adec->channels == 2) + mpeg1_format->fwHeadMode = ACM_MPEG_STEREO; + else + mpeg1_format->fwHeadMode = ACM_MPEG_SINGLECHANNEL; + + mpeg1_format->fwHeadModeExt = 0; + mpeg1_format->wHeadEmphasis = 0; + mpeg1_format->fwHeadFlags = 0; + + switch (adec->layer) { + case 1: + mpeg1_format->fwHeadLayer = ACM_MPEG_LAYER3; + break; + case 2: + mpeg1_format->fwHeadLayer = ACM_MPEG_LAYER2; + break; + case 3: + mpeg1_format->fwHeadLayer = ACM_MPEG_LAYER1; + break; + }; + + gst_structure_get_int (structure, "mpegaudioversion", &version); + if (adec->layer == 1) { + samples = 384; + } else { + if (version == 1) { + samples = 576; + } else { + samples = 1152; + } + } + mpeg1_format->wfx.nBlockAlign = (WORD) samples; + mpeg1_format->wfx.nSamplesPerSec = adec->rate; + mpeg1_format->dwHeadBitrate = 128000; /* This doesn't seem to matter */ + mpeg1_format->wfx.nAvgBytesPerSec = mpeg1_format->dwHeadBitrate / 8; + } + else + { + size = sizeof (WAVEFORMATEX) + + (adec->codec_data ? GST_BUFFER_SIZE (adec->codec_data) : 0); + format = (WAVEFORMATEX *)g_malloc0 (size); + if (adec->codec_data) { /* Codec data is appended after our header */ + memcpy (((guchar *) format) + sizeof (WAVEFORMATEX), + GST_BUFFER_DATA (adec->codec_data), + GST_BUFFER_SIZE (adec->codec_data)); + format->cbSize = GST_BUFFER_SIZE (adec->codec_data); + } + + format->wFormatTag = codec_entry->format; + format->nChannels = adec->channels; + format->nSamplesPerSec = adec->rate; + format->nAvgBytesPerSec = adec->bitrate / 8; + format->nBlockAlign = adec->block_align; + format->wBitsPerSample = adec->depth; + } + + mediatype->cbFormat = size; + mediatype->pbFormat = (BYTE *) format; + + return mediatype; +} + +static AM_MEDIA_TYPE * +dshowaudiodec_set_output_format (GstDshowAudioDec *adec) +{ + AM_MEDIA_TYPE *mediatype; + WAVEFORMATEX *format; + GstDshowAudioDecClass *klass = + (GstDshowAudioDecClass *) G_OBJECT_GET_CLASS (adec); + const AudioCodecEntry *codec_entry = klass->entry; + + if (!gst_dshowaudiodec_get_filter_settings (adec)) { + return NULL; + } + + format = (WAVEFORMATEX *)g_malloc0(sizeof (WAVEFORMATEX)); + format->wFormatTag = WAVE_FORMAT_PCM; + format->wBitsPerSample = adec->depth; + format->nChannels = adec->channels; + format->nBlockAlign = adec->channels * (adec->depth / 8); + format->nSamplesPerSec = adec->rate; + format->nAvgBytesPerSec = format->nBlockAlign * adec->rate; + + mediatype = (AM_MEDIA_TYPE *)g_malloc0(sizeof (AM_MEDIA_TYPE)); + mediatype->majortype = MEDIATYPE_Audio; + GUID subtype = GUID_MEDIASUBTYPE_FROM_FOURCC (WAVE_FORMAT_PCM); + mediatype->subtype = subtype; + mediatype->bFixedSizeSamples = TRUE; + mediatype->bTemporalCompression = FALSE; + mediatype->lSampleSize = format->nBlockAlign; + mediatype->formattype = FORMAT_WaveFormatEx; + mediatype->cbFormat = sizeof (WAVEFORMATEX); + mediatype->pbFormat = (BYTE *)format; + + return mediatype; +} + +static void +dshowadec_free_mediatype (AM_MEDIA_TYPE *mediatype) +{ + if (mediatype->pbFormat) + g_free (mediatype->pbFormat); + g_free (mediatype); +} + +static gboolean +gst_dshowaudiodec_setup_graph (GstDshowAudioDec * adec, GstCaps *caps) +{ + gboolean ret = FALSE; + GstDshowAudioDecClass *klass = + (GstDshowAudioDecClass *) G_OBJECT_GET_CLASS (adec); + HRESULT hres; + GstCaps *outcaps; + AM_MEDIA_TYPE *output_mediatype = NULL; + AM_MEDIA_TYPE *input_mediatype = NULL; + CComPtr<IPin> output_pin; + CComPtr<IPin> input_pin; + const AudioCodecEntry *codec_entry = klass->entry; + CComQIPtr<IBaseFilter> srcfilter; + CComQIPtr<IBaseFilter> sinkfilter; + + input_mediatype = dshowaudiodec_set_input_format (adec, caps); + + adec->fakesrc->GetOutputPin()->SetMediaType (input_mediatype); + + srcfilter = adec->fakesrc; + + /* connect our fake source to decoder */ + output_pin = gst_dshow_get_pin_from_filter (srcfilter, PINDIR_OUTPUT); + if (!output_pin) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't get output pin from our directshow fakesrc filter"), (NULL)); + goto end; + } + input_pin = gst_dshow_get_pin_from_filter (adec->decfilter, PINDIR_INPUT); + if (!input_pin) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't get input pin from decoder filter"), (NULL)); + goto end; + } + + hres = adec->filtergraph->ConnectDirect (output_pin, input_pin, + NULL); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't connect fakesrc with decoder (error=%x)", hres), (NULL)); + goto end; + } + + output_mediatype = dshowaudiodec_set_output_format (adec); + if (!output_mediatype) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't get audio output format from decoder"), (NULL)); + goto end; + } + + adec->fakesink->SetMediaType(output_mediatype); + + outcaps = gst_caps_new_simple ("audio/x-raw-int", + "width", G_TYPE_INT, adec->depth, + "depth", G_TYPE_INT, adec->depth, + "rate", G_TYPE_INT, adec->rate, + "channels", G_TYPE_INT, adec->channels, + "signed", G_TYPE_BOOLEAN, TRUE, + "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, + NULL); + + if (!gst_pad_set_caps (adec->srcpad, outcaps)) { + gst_caps_unref (outcaps); + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Failed to negotiate output"), (NULL)); + goto end; + } + gst_caps_unref (outcaps); + + /* connect the decoder to our fake sink */ + output_pin = gst_dshow_get_pin_from_filter (adec->decfilter, PINDIR_OUTPUT); + if (!output_pin) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't get output pin from our decoder filter"), (NULL)); + goto end; + } + + sinkfilter = adec->fakesink; + input_pin = gst_dshow_get_pin_from_filter (sinkfilter, PINDIR_INPUT); + if (!input_pin) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't get input pin from our directshow fakesink filter"), (NULL)); + goto end; + } + + hres = adec->filtergraph->ConnectDirect(output_pin, input_pin, NULL); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't connect decoder with fakesink (error=%x)", hres), (NULL)); + goto end; + } + + hres = adec->mediafilter->Run (-1); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("Can't run the directshow graph (error=%x)", hres), (NULL)); + goto end; + } + + ret = TRUE; + adec->setup = TRUE; +end: + if (input_mediatype) + dshowadec_free_mediatype (input_mediatype); + if (output_mediatype) + dshowadec_free_mediatype (output_mediatype); + + return ret; +} + +static gboolean +gst_dshowaudiodec_get_filter_settings (GstDshowAudioDec * adec) +{ + CComPtr<IPin> output_pin; + CComPtr<IEnumMediaTypes> enum_mediatypes; + HRESULT hres; + ULONG fetched; + BOOL ret = FALSE; + + if (adec->decfilter == 0) + return FALSE; + + output_pin = gst_dshow_get_pin_from_filter (adec->decfilter, PINDIR_OUTPUT); + if (!output_pin) { + GST_ELEMENT_ERROR (adec, CORE, NEGOTIATION, + ("failed getting ouput 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 (!ret && enum_mediatypes->Next(1, &mediatype, &fetched) == S_OK) + { + if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_PCM) && + IsEqualGUID (mediatype->formattype, FORMAT_WaveFormatEx)) + { + WAVEFORMATEX *audio_info = (WAVEFORMATEX *) mediatype->pbFormat; + + adec->channels = audio_info->nChannels; + adec->depth = audio_info->wBitsPerSample; + adec->rate = audio_info->nSamplesPerSec; + ret = TRUE; + } + DeleteMediaType (mediatype); + } + } + + return ret; +} + +static gboolean +gst_dshowaudiodec_create_graph_and_filters (GstDshowAudioDec * adec) +{ + HRESULT hres; + GstDshowAudioDecClass *klass = + (GstDshowAudioDecClass *) G_OBJECT_GET_CLASS (adec); + CComQIPtr<IBaseFilter> srcfilter; + CComQIPtr<IBaseFilter> sinkfilter; + GUID insubtype = GUID_MEDIASUBTYPE_FROM_FOURCC (klass->entry->format); + GUID outsubtype = GUID_MEDIASUBTYPE_FROM_FOURCC (WAVE_FORMAT_PCM); + + /* create the filter graph manager object */ + hres = adec->filtergraph.CoCreateInstance ( + CLSID_FilterGraph, NULL, CLSCTX_INPROC); + if (FAILED (hres)) { + GST_ELEMENT_ERROR (adec, STREAM, FAILED, + ("Can't create an instance of the directshow graph manager (error=%d)", + hres), (NULL)); + goto error; + } + + hres = adec->filtergraph->QueryInterface (&adec->mediafilter); + if (FAILED (hres)) { + GST_WARNING_OBJECT (adec, "Can't QI filtergraph to mediafilter"); + goto error; + } + + /* create fake src filter */ + adec->fakesrc = new FakeSrc(); + /* Created with a refcount of zero, so increment that */ + adec->fakesrc->AddRef(); + + /* create decoder filter */ + if (!gst_dshow_find_filter (MEDIATYPE_Audio, + insubtype, + MEDIATYPE_Audio, + outsubtype, + NULL, &adec->decfilter)) { + GST_ELEMENT_ERROR (adec, STREAM, FAILED, + ("Can't create an instance of the decoder filter"), (NULL)); + goto error; + } + + /* create fake sink filter */ + adec->fakesink = new AudioFakeSink(adec); + /* Created with a refcount of zero, so increment that */ + adec->fakesink->AddRef(); + + /* add filters to the graph */ + srcfilter = adec->fakesrc; + hres = adec->filtergraph->AddFilter (srcfilter, L"src"); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, STREAM, FAILED, + ("Can't add fakesrc filter to the graph (error=%d)", hres), (NULL)); + goto error; + } + + hres = adec->filtergraph->AddFilter(adec->decfilter, L"decoder"); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, STREAM, FAILED, + ("Can't add decoder filter to the graph (error=%d)", hres), (NULL)); + goto error; + } + + sinkfilter = adec->fakesink; + hres = adec->filtergraph->AddFilter(sinkfilter, L"sink"); + if (hres != S_OK) { + GST_ELEMENT_ERROR (adec, STREAM, FAILED, + ("Can't add fakesink filter to the graph (error=%d)", hres), (NULL)); + goto error; + } + + return TRUE; + +error: + if (adec->fakesrc) { + adec->fakesrc->Release(); + adec->fakesrc = NULL; + } + if (adec->fakesink) { + adec->fakesink->Release(); + adec->fakesink = NULL; + } + adec->decfilter = 0; + adec->mediafilter = 0; + adec->filtergraph = 0; + + return FALSE; +} + +static gboolean +gst_dshowaudiodec_destroy_graph_and_filters (GstDshowAudioDec * adec) +{ + if (adec->mediafilter) { + adec->mediafilter->Stop(); + } + + if (adec->fakesrc) { + if (adec->filtergraph) { + CComQIPtr<IBaseFilter> filter = adec->fakesrc; + adec->filtergraph->RemoveFilter(filter); + } + adec->fakesrc->Release(); + adec->fakesrc = NULL; + } + if (adec->decfilter) { + if (adec->filtergraph) + adec->filtergraph->RemoveFilter(adec->decfilter); + adec->decfilter = 0; + } + if (adec->fakesink) { + if (adec->filtergraph) { + CComQIPtr<IBaseFilter> filter = adec->fakesink; + adec->filtergraph->RemoveFilter(filter); + } + + adec->fakesink->Release(); + adec->fakesink = NULL; + } + adec->mediafilter = 0; + adec->filtergraph = 0; + + adec->setup = FALSE; + + return TRUE; +} + +gboolean +dshow_adec_register (GstPlugin * plugin) +{ + GTypeInfo info = { + sizeof (GstDshowAudioDecClass), + (GBaseInitFunc) gst_dshowaudiodec_base_init, + NULL, + (GClassInitFunc) gst_dshowaudiodec_class_init, + NULL, + NULL, + sizeof (GstDshowAudioDec), + 0, + (GInstanceInitFunc) gst_dshowaudiodec_init, + }; + gint i; + HRESULT hr; + + GST_DEBUG_CATEGORY_INIT (dshowaudiodec_debug, "dshowaudiodec", 0, + "Directshow filter audio decoder"); + + hr = CoInitialize(0); + for (i = 0; i < sizeof (audio_dec_codecs) / sizeof (AudioCodecEntry); i++) { + GType type; + + GUID insubtype = GUID_MEDIASUBTYPE_FROM_FOURCC (audio_dec_codecs[i].format); + GUID outsubtype = GUID_MEDIASUBTYPE_FROM_FOURCC (WAVE_FORMAT_PCM); + if (gst_dshow_find_filter (MEDIATYPE_Audio, + insubtype, + MEDIATYPE_Audio, + outsubtype, + NULL, NULL)) { + + GST_CAT_DEBUG (dshowaudiodec_debug, "Registering %s", + audio_dec_codecs[i].element_name); + + tmp = &audio_dec_codecs[i]; + type = + g_type_register_static (GST_TYPE_ELEMENT, + audio_dec_codecs[i].element_name, &info, (GTypeFlags)0); + if (!gst_element_register (plugin, audio_dec_codecs[i].element_name, + GST_RANK_PRIMARY, type)) { + return FALSE; + } + GST_CAT_DEBUG (dshowaudiodec_debug, "Registered %s", + audio_dec_codecs[i].element_name); + } else { + GST_CAT_DEBUG (dshowaudiodec_debug, + "Element %s not registered (the format is not supported by the system)", + audio_dec_codecs[i].element_name); + } + } + + if (SUCCEEDED(hr)) + CoUninitialize (); + + return TRUE; +} diff --git a/sys/dshowdecwrapper/gstdshowaudiodec.h b/sys/dshowdecwrapper/gstdshowaudiodec.h index 732e636d..6301b31f 100644 --- a/sys/dshowdecwrapper/gstdshowaudiodec.h +++ b/sys/dshowdecwrapper/gstdshowaudiodec.h @@ -47,11 +47,21 @@ #ifndef __GST_DSHOWAUDIODEC_H__ #define __GST_DSHOWAUDIODEC_H__ +#include <atlbase.h> + #include <gst/gst.h> -#include "gstdshowdecwrapper.h" +#include "gstdshowutil.h" +#include "gstdshowfakesrc.h" G_BEGIN_DECLS +typedef struct { + gchar *element_name; /* The gst element factory name */ + gchar *element_longname; /* Description string for element */ + gint32 format; /* WAVEFORMATEX format */ + gchar *sinkcaps; /* GStreamer caps of input format */ +} AudioCodecEntry; + #define GST_TYPE_DSHOWAUDIODEC (gst_dshowaudiodec_get_type()) #define GST_DSHOWAUDIODEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DSHOWAUDIODEC,GstDshowAudioDec)) #define GST_DSHOWAUDIODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DSHOWAUDIODEC,GstDshowAudioDecClass)) @@ -61,6 +71,8 @@ G_BEGIN_DECLS typedef struct _GstDshowAudioDec GstDshowAudioDec; typedef struct _GstDshowAudioDecClass GstDshowAudioDecClass; +class AudioFakeSink; + struct _GstDshowAudioDec { GstElement element; @@ -72,14 +84,14 @@ struct _GstDshowAudioDec GstFlowReturn last_ret; /* filters interfaces*/ - IBaseFilter *srcfilter; - IGstDshowInterface *gstdshowsrcfilter; - IBaseFilter *decfilter; - IBaseFilter *sinkfilter; + FakeSrc *fakesrc; + AudioFakeSink *fakesink; + + CComPtr<IBaseFilter> decfilter; /* graph manager interfaces */ - IMediaFilter *mediafilter; - IFilterGraph *filtergraph; + CComPtr<IMediaFilter> mediafilter; + CComPtr<IFilterGraph> filtergraph; /* true when dshow graph is setup */ gboolean setup; @@ -105,11 +117,39 @@ struct _GstDshowAudioDec struct _GstDshowAudioDecClass { GstElementClass parent_class; - const CodecEntry *entry; + const AudioCodecEntry *entry; }; gboolean dshow_adec_register (GstPlugin * plugin); +const GUID CLSID_AudioFakeSink = +{ 0x3867f537, 0x3e3d, 0x44da, + { 0xbb, 0xf2, 0x02, 0x48, 0x7b, 0xb0, 0xbc, 0xc4} }; + +class AudioFakeSink : public CBaseRenderer +{ +public: + AudioFakeSink(GstDshowAudioDec *dec) : + m_hres(S_OK), + CBaseRenderer(CLSID_AudioFakeSink, L"AudioFakeSink", NULL, &m_hres), + mDec(dec) + {}; + virtual ~AudioFakeSink() {}; + + HRESULT DoRenderSample(IMediaSample *pMediaSample); + HRESULT CheckMediaType(const CMediaType *pmt); + HRESULT SetMediaType (AM_MEDIA_TYPE *pmt) + { + m_MediaType.Set (*pmt); + return S_OK; + } + +protected: + HRESULT m_hres; + CMediaType m_MediaType; + GstDshowAudioDec *mDec; +}; + G_END_DECLS #endif /* __GST_DSHOWAUDIODEC_H__ */ diff --git a/sys/dshowdecwrapper/gstdshowdecwrapper.cpp b/sys/dshowdecwrapper/gstdshowdecwrapper.cpp new file mode 100644 index 00000000..c64257c8 --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowdecwrapper.cpp @@ -0,0 +1,73 @@ +/* + * GStreamer DirectShow codecs wrapper + * Copyright <2006, 2007, 2008> Fluendo <gstreamer@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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstdshowaudiodec.h" +#include "gstdshowvideodec.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!dshow_adec_register (plugin)) + return FALSE; + + if (!dshow_vdec_register (plugin)) + return FALSE; + + return TRUE; +} + +extern "C" { + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "dshowdecwrapper", + "DirectShow decoder wrapper plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) + +} diff --git a/sys/dshowdecwrapper/gstdshowfakesrc.cpp b/sys/dshowdecwrapper/gstdshowfakesrc.cpp new file mode 100644 index 00000000..8095c36e --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowfakesrc.cpp @@ -0,0 +1,153 @@ +/* GStreamer + * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net> + * + * gstdshowfakesrc.cpp: + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstdshowfakesrc.h" + +const GUID CLSID_DecodeFakeSrc = +{ 0x039527db, 0x6b48, 0x45a7, { 0xab, 0xcf, 0x21, 0xab, 0xc5, 0x44, 0xbb, 0xb6} }; + +static CCritSec g_pCriticSec; + +/* output pin*/ +FakeOutputPin::FakeOutputPin (CBaseFilter *pFilter, CCritSec *sec): + CBaseOutputPin("FakeOutputPin", pFilter, sec, &m_hres, L"output") +{ +} + +FakeOutputPin::~FakeOutputPin() +{ +} + +HRESULT FakeOutputPin::GetMediaType(int iPosition, + CMediaType *pMediaType) +{ + if(iPosition == 0) { + *pMediaType = m_MediaType; + return S_OK; + } + + return VFW_S_NO_MORE_ITEMS; +} + +HRESULT FakeOutputPin::CheckMediaType(const CMediaType *pmt) +{ + if (m_MediaType == *pmt) { + return S_OK; + } + + return S_FALSE; +} + +HRESULT FakeOutputPin::DecideBufferSize (IMemAllocator *pAlloc, + ALLOCATOR_PROPERTIES *ppropInputRequest) +{ + ALLOCATOR_PROPERTIES properties; + ppropInputRequest->cbBuffer = m_SampleSize; + ppropInputRequest->cBuffers = 1; + HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties); + pAlloc->Commit(); + + return S_OK; +} + +STDMETHODIMP FakeOutputPin::SetMediaType (AM_MEDIA_TYPE *pmt) +{ + m_MediaType.Set (*pmt); + m_SampleSize = m_MediaType.GetSampleSize(); + return S_OK; +} + +STDMETHODIMP FakeOutputPin::PushBuffer(byte *buffer, + __int64 start, __int64 stop, + unsigned int size, bool discont) +{ + IMediaSample *pSample = NULL; + + if (start != -1) { + start /= 100; + stop /= 100; + } + + HRESULT hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0); + if (hres == S_OK && pSample) + { + BYTE *sample_buffer; + pSample->GetPointer(&sample_buffer); + if(sample_buffer) + { + memcpy (sample_buffer, buffer, size); + pSample->SetActualDataLength(size); + } + pSample->SetDiscontinuity(discont); + + pSample->SetSyncPoint(TRUE); + pSample->SetPreroll(FALSE); + + if (start != -1) + pSample->SetTime(&start, &stop); + + hres = Deliver(pSample); + pSample->Release(); + } + + return S_OK; +} + +STDMETHODIMP FakeOutputPin::Flush () +{ + DeliverBeginFlush(); + DeliverEndFlush(); + return S_OK; +} + +STDMETHODIMP FakeOutputPin::SetSampleSize (unsigned int size) +{ + m_SampleSize = size; + return S_OK; +} + +/* filter */ +FakeSrc::FakeSrc() : + CBaseFilter("DshowFakeSink", NULL, &g_pCriticSec, CLSID_DecodeFakeSrc) +{ + m_pOutputPin = new FakeOutputPin((CSource *)this, m_pLock); +} + +FakeSrc::~FakeSrc() +{ + if (m_pOutputPin) + delete m_pOutputPin; +} + +int FakeSrc::GetPinCount() +{ + return 1; +} + +CBasePin *FakeSrc::GetPin(int n) +{ + return (CBasePin *)m_pOutputPin; +} + +FakeOutputPin *FakeSrc::GetOutputPin() +{ + return m_pOutputPin; +}
\ No newline at end of file diff --git a/sys/dshowdecwrapper/gstdshowfakesrc.h b/sys/dshowdecwrapper/gstdshowfakesrc.h new file mode 100644 index 00000000..03e01346 --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowfakesrc.h @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net> + * + * gstdshowfakesrc.h: + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _DSHOWDECWRAPPER_FAKESRC_H_ +#define _DSHOWDECWRAPPER_FAKESRC_H_ + +#include <gst/gst.h> +#include "gstdshowutil.h" + +class FakeOutputPin : public CBaseOutputPin +{ +protected: +/* members */ + HRESULT m_hres; + CMediaType m_MediaType; + unsigned int m_SampleSize; + +public: +/* methods */ + FakeOutputPin (CBaseFilter *pFilter, CCritSec *sec); + ~FakeOutputPin (); + + virtual HRESULT CheckMediaType(const CMediaType *pmt); + HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); + virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest); + STDMETHOD (SetMediaType) (AM_MEDIA_TYPE *pmt); + STDMETHOD (PushBuffer) (byte *buffer, __int64 start, __int64 stop, unsigned int size, bool discont); + STDMETHOD (Flush) (); + STDMETHOD (SetSampleSize) (unsigned int size); +}; + +class FakeSrc : public CBaseFilter +{ +public: +/* members */ + FakeOutputPin *m_pOutputPin; + +/* methods */ + FakeSrc (); + virtual ~FakeSrc (); + + virtual int GetPinCount(); + virtual CBasePin *GetPin(int n); + + FakeOutputPin *GetOutputPin(); +}; + +#endif // _DSHOWDECWRAPPER_FAKESRC_H_
\ No newline at end of file diff --git a/sys/dshowdecwrapper/gstdshowutil.cpp b/sys/dshowdecwrapper/gstdshowutil.cpp new file mode 100644 index 00000000..df2f65d1 --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowutil.cpp @@ -0,0 +1,156 @@ +/* GStreamer + * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net> + * + * gstdshow.cpp: + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atlbase.h> + +#include "gstdshowutil.h" +#include "gstdshowfakesrc.h" + +IPin * +gst_dshow_get_pin_from_filter (IBaseFilter *filter, PIN_DIRECTION pindir) +{ + CComPtr<IEnumPins> enumpins; + CComPtr<IPin> pin; + HRESULT hres; + + hres = filter->EnumPins (&enumpins); + if (FAILED(hres)) { + return NULL; + } + + while (enumpins->Next (1, &pin, NULL) == S_OK) + { + PIN_DIRECTION pindirtmp; + hres = pin->QueryDirection (&pindirtmp); + if (hres == S_OK && pindir == pindirtmp) { + return pin; + } + pin.Release(); + } + + return NULL; +} + +gboolean gst_dshow_find_filter(CLSID input_majortype, CLSID input_subtype, + CLSID output_majortype, CLSID output_subtype, + gchar * prefered_filter_name, IBaseFilter **filter) +{ + gboolean ret = FALSE; + HRESULT hres; + GUID arrayInTypes[2]; + GUID arrayOutTypes[2]; + IFilterMapper2 *mapper = NULL; + IEnumMoniker *enum_moniker = NULL; + IMoniker *moniker = NULL; + ULONG fetched; + gchar *prefered_filter_upper = NULL; + gboolean exit = FALSE; + + /* initialize output parameter */ + if (filter) + *filter = NULL; + + /* create a private copy of prefered filter substring in upper case */ + if (prefered_filter_name) { + prefered_filter_upper = g_strdup (prefered_filter_name); + strupr (prefered_filter_upper); + } + + hres = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC, + IID_IFilterMapper2, (void **) &mapper); + if (FAILED(hres)) + goto clean; + + memcpy(&arrayInTypes[0], &input_majortype, sizeof (CLSID)); + memcpy(&arrayInTypes[1], &input_subtype, sizeof (CLSID)); + memcpy(&arrayOutTypes[0], &output_majortype, sizeof (CLSID)); + memcpy(&arrayOutTypes[1], &output_subtype, sizeof (CLSID)); + + hres = mapper->EnumMatchingFilters (&enum_moniker, 0, FALSE, MERIT_DO_NOT_USE+1, + TRUE, 1, arrayInTypes, NULL, NULL, FALSE, + TRUE, 1, arrayOutTypes, NULL, NULL); + if (FAILED(hres)) + goto clean; + + enum_moniker->Reset (); + + while(hres = enum_moniker->Next (1, &moniker, &fetched),hres == S_OK + && !exit) { + IBaseFilter *filter_temp = NULL; + IPropertyBag *property_bag = NULL; + gchar * friendly_name = NULL; + + hres = moniker->BindToStorage (NULL, NULL, IID_IPropertyBag, (void **)&property_bag); + if(SUCCEEDED(hres) && property_bag) { + VARIANT varFriendlyName; + VariantInit (&varFriendlyName); + + hres = property_bag->Read (L"FriendlyName", &varFriendlyName, NULL); + if(hres == S_OK && varFriendlyName.bstrVal) { + friendly_name = g_utf16_to_utf8((const gunichar2*)varFriendlyName.bstrVal, + wcslen(varFriendlyName.bstrVal), NULL, NULL, NULL); + if (friendly_name) + strupr (friendly_name); + SysFreeString (varFriendlyName.bstrVal); + } + property_bag->Release (); + } + + hres = moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&filter_temp); + if(SUCCEEDED(hres) && filter_temp) { + ret = TRUE; + if (filter) { + if (*filter) + (*filter)->Release (); + + *filter = filter_temp; + (*filter)->AddRef (); + + if (prefered_filter_upper && friendly_name && + strstr(friendly_name, prefered_filter_upper)) + exit = TRUE; + } + + /* if we just want to know if the formats are supported OR + if we don't care about what will be the filter used + => we can stop enumeration */ + if (!filter || !prefered_filter_upper) + exit = TRUE; + + filter_temp->Release (); + } + + if (friendly_name) + g_free (friendly_name); + moniker->Release (); + } + +clean: + if (prefered_filter_upper) + g_free (prefered_filter_upper); + if (enum_moniker) + enum_moniker->Release (); + if (mapper) + mapper->Release (); + + return ret; +} + diff --git a/sys/dshowdecwrapper/gstdshowutil.h b/sys/dshowdecwrapper/gstdshowutil.h new file mode 100644 index 00000000..0b5ca90e --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowutil.h @@ -0,0 +1,42 @@ +/* GStreamer + * Copyright (C) 2007 Sebastien Moutte <sebastien@moutte.net> + * + * gstdshow.h: + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_DSHOW_UTIL_H_ +#define _GST_DSHOW_UTIL_H_ + +#include <windows.h> +#include <objbase.h> +#include <dshow.h> +#include <Rpc.h> +#include <streams.h> +#include <strmif.h> + +#include <glib.h> + +/* get a pin from directshow filter */ +IPin *gst_dshow_get_pin_from_filter (IBaseFilter *filter, PIN_DIRECTION pindir); + +/* find and return a filter according to the input and output types */ +gboolean gst_dshow_find_filter(CLSID input_majortype, CLSID input_subtype, + CLSID output_majortype, CLSID output_subtype, + gchar * prefered_filter_name, IBaseFilter **filter); + +#endif /* _GST_DSHOW_UTIL_H_ */ diff --git a/sys/dshowdecwrapper/gstdshowvideodec.cpp b/sys/dshowdecwrapper/gstdshowvideodec.cpp new file mode 100644 index 00000000..80a1a193 --- /dev/null +++ b/sys/dshowdecwrapper/gstdshowvideodec.cpp @@ -0,0 +1,1170 @@ +/* + * GStreamer DirectShow codecs wrapper + * Copyright <2006, 2007, 2008> Fluendo <gstreamer@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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <atlbase.h> + +#include "gstdshowvideodec.h" + +GST_DEBUG_CATEGORY_STATIC (dshowvideodec_debug); +#define GST_CAT_DEFAULT dshowvideodec_debug + +GST_BOILERPLATE (GstDshowVideoDec, gst_dshowvideodec, GstElement, + GST_TYPE_ELEMENT); +static const VideoCodecEntry *tmp; + +static void gst_dshowvideodec_dispose (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, GstEvent * event); +static GstFlowReturn gst_dshowvideodec_chain (GstPad * pad, 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_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 }} + +/* video codecs array */ +static const VideoCodecEntry video_dec_codecs[] = { + {"dshowvdec_wmv1", + "Windows Media Video 7", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_wmv2", + "Windows Media Video 8", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_wmv3", + "Windows Media Video 9", + "DMO", + GST_MAKE_FOURCC ('W', 'M', 'V', '3'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVV3, + "video/x-wmv, wmvversion = (int) 3, " "format = (fourcc) WMV3", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_wmvp", + "Windows Media Video 9 Image", + "DMO", + GST_MAKE_FOURCC ('W', 'M', 'V', 'P'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVP, + "video/x-wmv, wmvversion = (int) 3, " "format = (fourcc) WMVP", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_wmva", + "Windows Media Video 9 Advanced", + "DMO", + GST_MAKE_FOURCC ('W', 'M', 'V', 'A'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_WMVA, + "video/x-wmv, wmvversion = (int) 3, " "format = (fourcc) WMVA", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_cinepak", + "Cinepack", + "AVI Decompressor", + 0x64697663, + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_CVID, + "video/x-cinepak", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_RGB32, + "video/x-raw-rgb, bpp=(int)32, depth=(int)24, " + "endianness=(int)4321, red_mask=(int)65280, " + "green_mask=(int)16711680, blue_mask=(int)-16777216"}, + {"dshowvdec_msmpeg41", + "Microsoft ISO MPEG-4 version 1", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_msmpeg42", + "Microsoft ISO MPEG-4 version 2", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_msmpeg43", + "Microsoft ISO MPEG-4 version 3", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_msmpeg4", + "Microsoft ISO MPEG-4 version 1.1", + "DMO", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_mpeg1", + "MPEG-1 Video", + "MPEG Video Decoder", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_xvid", + "XVID Video", + "ffdshow", + GST_MAKE_FOURCC ('X', 'V', 'I', 'D'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_XVID, + "video/x-xvid", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_divx5", + "DIVX 5.0 Video", + "ffdshow", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_divx4", + "DIVX 4.0 Video", + "ffdshow", + 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, + "video/x-raw-yuv, format=(fourcc)YUY2"}, + {"dshowvdec_divx3", + "DIVX 3.0 Video", + "ffdshow", + GST_MAKE_FOURCC ('D', 'I', 'V', '3'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_DIV3, + "video/x-divx, divxversion=(int)3", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2"} + /*, + { "dshowvdec_mpeg4", + "DMO", + GST_MAKE_FOURCC ('M', 'P', 'G', '4'), + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_MPG4, + "video/mpeg, msmpegversion=(int)4", + GUID_MEDIATYPE_VIDEO, GUID_MEDIASUBTYPE_YUY2, + "video/x-raw-yuv, format=(fourcc)YUY2" + } */ +}; + +HRESULT VideoFakeSink::DoRenderSample(IMediaSample *pMediaSample) +{ + gboolean in_seg = FALSE; + gint64 clip_start = 0, clip_stop = 0; + GstDshowVideoDecClass *klass = + (GstDshowVideoDecClass *) G_OBJECT_GET_CLASS (mDec); + GstBuffer *buf = NULL; + GstClockTime start, stop; + + 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 */ + mDec->last_ret = gst_pad_alloc_buffer (mDec->srcpad, + GST_BUFFER_OFFSET_NONE, + size, + GST_PAD_CAPS (mDec->srcpad), &buf); + 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; + + 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 (GST_BUFFER_DATA (buf) + (line * stride), + pBuffer + (size - ((line + 1) * (stride))), stride); + } + } else { + memcpy (GST_BUFFER_DATA (buf), pBuffer, MIN ((unsigned int)size, GST_BUFFER_SIZE (buf))); + } + + 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); + GstElementDetails details; + + videodec_class->entry = tmp; + + details.longname = g_strdup_printf ("DirectShow %s Decoder Wrapper", + tmp->element_longname); + details.klass = g_strdup ("Codec/Decoder/Video"); + details.description = g_strdup_printf ("DirectShow %s Decoder Wrapper", + tmp->element_longname); + details.author = "Sebastien Moutte <sebastien@moutte.net>"; + gst_element_class_set_details (element_class, &details); + g_free (details.longname); + g_free (details.klass); + g_free (details.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); +} + +static void +gst_dshowvideodec_class_init (GstDshowVideoDecClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dshowvideodec_dispose); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_dshowvideodec_change_state); + + if (!parent_class) + parent_class = (GstElementClass *)g_type_class_ref (GST_TYPE_ELEMENT); + + if (!dshowvideodec_debug) { + GST_DEBUG_CATEGORY_INIT (dshowvideodec_debug, "dshowvideodec", 0, + "Directshow filter video decoder"); + } +} + +static void +gst_dshowvideodec_init (GstDshowVideoDec * vdec, + GstDshowVideoDecClass * vdec_class) +{ + GstElementClass *element_class = GST_ELEMENT_GET_CLASS (vdec); + HRESULT hr; + + /* setup pads */ + vdec->sinkpad = + gst_pad_new_from_template (gst_element_class_get_pad_template + (element_class, "sink"), "sink"); + + gst_pad_set_setcaps_function (vdec->sinkpad, gst_dshowvideodec_sink_setcaps); + 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 negociation 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; + + hr = CoInitialize (0); + if (SUCCEEDED(hr)) { + vdec->comInitialized = TRUE; + } +} + +static void +gst_dshowvideodec_dispose (GObject * object) +{ + GstDshowVideoDec *vdec = (GstDshowVideoDec *) (object); + + if (vdec->segment) { + gst_segment_free (vdec->segment); + vdec->segment = NULL; + } + + if (vdec->comInitialized) { + CoUninitialize (); + vdec->comInitialized = FALSE; + } + + G_OBJECT_CLASS (parent_class)->dispose (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; + const GValue *v = NULL; + guint size = 0; + GstCaps *caps_out; + AM_MEDIA_TYPE output_mediatype, input_mediatype; + VIDEOINFOHEADER *input_vheader = NULL, *output_vheader = NULL; + CComPtr<IPin> output_pin; + CComPtr<IPin> input_pin; + IBaseFilter *srcfilter = NULL; + IBaseFilter *sinkfilter = NULL; + const GValue *fps; + + /* 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) { + GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION, + ("error getting video framerate from caps"), (NULL)); + goto end; + } + vdec->fps_n = gst_value_get_fraction_numerator (fps); + vdec->fps_d = gst_value_get_fraction_denominator (fps); + + if ((v = gst_structure_get_value (s, "codec_data"))) + extradata = gst_value_get_buffer (v); + + /* 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 ? GST_BUFFER_SIZE (extradata) - + 1 : 0); + input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size); + + input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + if (extradata) { + MPEG1VIDEOINFO *mpeg_info = (MPEG1VIDEOINFO *) input_vheader; + + memcpy (mpeg_info->bSequenceHeader, + GST_BUFFER_DATA (extradata), GST_BUFFER_SIZE (extradata)); + mpeg_info->cbSequenceHeader = GST_BUFFER_SIZE (extradata); + } + input_mediatype.formattype = FORMAT_MPEGVideo; + } else { + size = + sizeof (VIDEOINFOHEADER) + + (extradata ? GST_BUFFER_SIZE (extradata) : 0); + input_vheader = (VIDEOINFOHEADER *)g_malloc0 (size); + + input_vheader->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + if (extradata) { /* Codec data is appended after our header */ + memcpy (((guchar *) input_vheader) + sizeof (VIDEOINFOHEADER), + GST_BUFFER_DATA (extradata), GST_BUFFER_SIZE (extradata)); + input_vheader->bmiHeader.biSize += GST_BUFFER_SIZE (extradata); + } + 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, + "framerate", GST_TYPE_FRACTION, vdec->fps_n, vdec->fps_d, NULL); + if (!gst_pad_set_caps (vdec->srcpad, caps_out)) { + gst_caps_unref (caps_out); + GST_ELEMENT_ERROR (vdec, CORE, NEGOTIATION, + ("Failed to negotiate output"), (NULL)); + goto end; + } + gst_caps_unref (caps_out); + + 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: + gst_object_unref (vdec); + if (input_vheader) + g_free (input_vheader); + if (srcfilter) + srcfilter->Release(); + if (sinkfilter) + sinkfilter->Release(); + return ret; +} + +static gboolean +gst_dshowvideodec_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean ret = TRUE; + GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_dshowvideodec_flush (vdec); + ret = gst_pad_event_default (pad, event); + break; + case GST_EVENT_NEWSEGMENT: + { + GstFormat format; + gdouble rate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment (event, &update, &rate, &format, &start, + &stop, &time); + + /* save the new segment in our local current segment */ + gst_segment_set_newsegment (vdec->segment, update, rate, format, start, + stop, time); + + 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)); + + if (update) { + GST_CAT_DEBUG_OBJECT (dshowvideodec_debug, vdec, + "closing current segment flushing.."); + gst_dshowvideodec_flush (vdec); + } + + ret = gst_pad_event_default (pad, event); + break; + } + default: + ret = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (vdec); + + return ret; +} + +static GstFlowReturn +gst_dshowvideodec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstDshowVideoDec *vdec = (GstDshowVideoDec *) gst_pad_get_parent (pad); + bool discont = FALSE; + GstClockTime stop; + + if (!vdec->setup) { + /* we are not setup */ + GST_WARNING_OBJECT (vdec, "Decoder not set up, failing"); + vdec->last_ret = GST_FLOW_WRONG_STATE; + goto beach; + } + + if (GST_FLOW_IS_FATAL (vdec->last_ret)) { + 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_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; + } + + /* push the buffer to the directshow decoder */ + vdec->fakesrc->GetOutputPin()->PushBuffer( + GST_BUFFER_DATA (buffer), GST_BUFFER_TIMESTAMP (buffer), stop, + GST_BUFFER_SIZE (buffer), discont); + +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) { + CComPtr<IPin> output_pin; + CComPtr<IEnumMediaTypes> 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 ouput 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(); + + return TRUE; +} + +static gboolean +gst_dshowvideodec_get_filter_output_format (GstDshowVideoDec * vdec, + const GUID subtype, VIDEOINFOHEADER ** format, guint * size) +{ + CComPtr<IPin> output_pin; + CComPtr<IEnumMediaTypes> 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 ouput 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 */ + if (!gst_dshow_find_filter (klass->entry->input_majortype, + klass->entry->input_subtype, + klass->entry->output_majortype, + klass->entry->output_subtype, + klass->entry->preferred_filter_substring, &vdec->decfilter)) { + 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; + + if (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_filter_substring, NULL)) { + + GST_CAT_DEBUG (dshowvideodec_debug, "Registering %s", + video_dec_codecs[i].element_name); + + tmp = &video_dec_codecs[i]; + type = + g_type_register_static (GST_TYPE_ELEMENT, + video_dec_codecs[i].element_name, &info, (GTypeFlags)0); + if (!gst_element_register (plugin, video_dec_codecs[i].element_name, + GST_RANK_PRIMARY, type)) { + return FALSE; + } + GST_CAT_DEBUG (dshowvideodec_debug, "Registered %s", + video_dec_codecs[i].element_name); + } else { + GST_CAT_DEBUG (dshowvideodec_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; +} diff --git a/sys/dshowdecwrapper/gstdshowvideodec.h b/sys/dshowdecwrapper/gstdshowvideodec.h index 0ab7a5d7..29953d82 100644 --- a/sys/dshowdecwrapper/gstdshowvideodec.h +++ b/sys/dshowdecwrapper/gstdshowvideodec.h @@ -47,10 +47,24 @@ #define __GST_DSHOWVIDEODEC_H__ #include <gst/gst.h> -#include "gstdshowdecwrapper.h" +#include "gstdshowutil.h" +#include "gstdshowfakesrc.h" G_BEGIN_DECLS +typedef struct { + gchar *element_name; /* The gst element factory name */ + gchar *element_longname; /* Description string for element */ + gchar *preferred_filter_substring; /* TODO: Remove this? */ + gint32 format; /* ??? */ + GUID input_majortype; + GUID input_subtype; + gchar *sinkcaps; /* GStreamer caps of input format */ + GUID output_majortype; + GUID output_subtype; + gchar *srccaps; +} VideoCodecEntry; + #define GST_TYPE_DSHOWVIDEODEC (gst_dshowvideodec_get_type()) #define GST_DSHOWVIDEODEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DSHOWVIDEODEC,GstDshowVideoDec)) #define GST_DSHOWVIDEODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DSHOWVIDEODEC,GstDshowVideoDecClass)) @@ -60,6 +74,8 @@ G_BEGIN_DECLS typedef struct _GstDshowVideoDec GstDshowVideoDec; typedef struct _GstDshowVideoDecClass GstDshowVideoDecClass; +class VideoFakeSink; + struct _GstDshowVideoDec { GstElement element; @@ -77,10 +93,10 @@ struct _GstDshowVideoDec GList *mediatypes; /* filters interfaces */ - IBaseFilter *srcfilter; - IGstDshowInterface *gstdshowsrcfilter; + FakeSrc *fakesrc; + VideoFakeSink *fakesink; + IBaseFilter *decfilter; - IBaseFilter *sinkfilter; /* graph manager interfaces */ IMediaFilter *mediafilter; @@ -93,17 +109,47 @@ struct _GstDshowVideoDec /* current segment */ GstSegment *segment; + gboolean setup; + gboolean comInitialized; }; struct _GstDshowVideoDecClass { GstElementClass parent_class; - const CodecEntry *entry; + const VideoCodecEntry *entry; }; gboolean dshow_vdec_register (GstPlugin * plugin); +const GUID CLSID_VideoFakeSink = +{ 0xff8f0c8e, 0x64f9, 0x4471, + { 0x96, 0x0e, 0xd2, 0xd3, 0x18, 0x87, 0x78, 0x9a} }; + +class VideoFakeSink : public CBaseRenderer +{ +public: + VideoFakeSink(GstDshowVideoDec *dec) : + m_hres(S_OK), + CBaseRenderer(CLSID_VideoFakeSink, L"VideoFakeSink", NULL, &m_hres), + mDec(dec) + {}; + virtual ~VideoFakeSink() {}; + + HRESULT DoRenderSample(IMediaSample *pMediaSample); + HRESULT CheckMediaType(const CMediaType *pmt); + HRESULT SetMediaType (AM_MEDIA_TYPE *pmt) + { + m_MediaType.Set (*pmt); + return S_OK; + } + +protected: + HRESULT m_hres; + CMediaType m_MediaType; + GstDshowVideoDec *mDec; +}; + G_END_DECLS #endif /* __GST_DSHOWVIDEODEC_H__ */ |