summaryrefslogtreecommitdiffstats
path: root/stream/tvi_dshow.c
diff options
context:
space:
mode:
Diffstat (limited to 'stream/tvi_dshow.c')
-rw-r--r--stream/tvi_dshow.c3298
1 files changed, 3298 insertions, 0 deletions
diff --git a/stream/tvi_dshow.c b/stream/tvi_dshow.c
new file mode 100644
index 0000000000..43ab094c2f
--- /dev/null
+++ b/stream/tvi_dshow.c
@@ -0,0 +1,3298 @@
+/*
+ * TV support under Win32
+ *
+ * (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>.
+ *
+ * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c code .
+ *
+ * -------------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ * -------------------------------------------------------------------------------
+ *
+ *
+ * WARNING: This is alpha code!
+ *
+ * Abilities:
+ * * Watching TV under Win32 using WDM Capture driver and DirectShow
+ * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow)
+ * * If device driver provides IAMStreamConfig interface, user can choose width/height with "-tv height=<h>:width=<w>"
+ * * Adjusting BRIGHTNESS,HUE,SATURATION,CONTRAST if supported by device
+ * * Selecting Tuner,S-Video,... as media source
+ * * User can select used video capture device, passing -tv device=<dev#>
+ * * User can select used audio input, passing -tv audioid=<input#>
+ *
+ * options which will not be implemented (probably sometime in future, if possible):
+ * * alsa
+ * * mjpeg
+ * * decimation=<1|2|4>
+ * * quality=<0\-100>
+ * * forceaudio
+ * * forcechan=<1\-2>
+ * * [volume|bass|treble|balance]
+ *
+ * Works with:
+ * - LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov
+ * Partially works with:
+ * - ATI 9200 VIVO based card
+ * - ATI AIW 7500
+ * - nVidia Ti-4400
+ *
+ * Known bugs:
+ * * stream goes with 24.93 FPS (NTSC), while reporting 25 FPS (PAL) ?!
+ * * direct set frequency call does not work ('Insufficient Buffer' error)
+ * * audio stream goes with about 1 sample/sec rate when capturing sound from audio card
+ *
+ * TODO:
+ * * check audio with small buffer on vivo !!!
+ * * norm for IAMVideoDecoder and for IAMTVtuner - differs !!
+ * * check how to change TVFormat on VIVO card without tuner
+ * * Flip image upside-down for RGB formats.
+ * *
+ * * remove debug sleep()
+ * * Add some notes to methods' parameters
+ * * refactor console messages
+ * * check using header files and keep only needed
+ * * add additional comments to methods' bodies
+ *
+ */
+
+
+/// \ingroup tvi_dshow
+
+#include "config.h"
+
+#include <stdio.h>
+#include "libmpcodecs/img_format.h"
+#include "libaf/af_format.h"
+#include "help_mp.h"
+#include "osdep/timer.h"
+
+
+#include "tv.h"
+#include "mp_msg.h"
+#include "frequencies.h"
+
+
+#include "tvi_dshow.h"
+
+static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param);
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Data structures
+*
+*---------------------------------------------------------------------------------------
+*/
+/**
+ information about this file
+*/
+tvi_info_t tvi_info_dshow = {
+ tvi_init_dshow,
+ "DirectShow TV",
+ "dshow",
+ "Vladimir Voroshilov",
+ "Very experimental!! Use with caution"
+};
+
+
+/**
+ringbuffer related info
+*/
+typedef struct {
+ CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex)
+ char **ringbuffer; ///< ringbuffer array
+ double*dpts; ///< samples' timestamps
+
+ int buffersize; ///< size of buffer in blocks
+ int blocksize; ///< size of individual block
+ int head; ///< index of first valid sample
+ int tail; ///< index of last valid sample
+ int count; ///< count of valid samples in ringbuffer
+ double tStart; ///< pts of first sample (first sample should have pts 0)
+} grabber_ringbuffer_t;
+
+/**
+ CSampleGrabberCD definition
+*/
+typedef struct CSampleGrabberCB {
+ ISampleGrabberCBVtbl *lpVtbl;
+ int refcount;
+ GUID interfaces[2];
+ grabber_ringbuffer_t *pbuf;
+} CSampleGrabberCB;
+
+typedef struct {
+ int dev_index; ///< capture device index in device list (defaul: 0, first available device)
+ int adev_index; ///< audio capture device index in device list (default: -1, not used)
+ int immediate_mode; ///< immediate mode (no sound capture)
+ int state; ///< state: 1-filter graph running, 0-filter graph stopped
+ int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency
+ int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency
+
+ int fcc; ///< used video format code (FourCC)
+ int width; ///< picture width (default: auto)
+ int height; ///< picture height (default: auto)
+
+ int channels; ///< number of audio channels (default: auto)
+ int samplerate; ///< audio samplerate (default: auto)
+
+ long *freq_table; ///< frequency table (in Hz)
+ int freq_table_len; ///< length of freq table
+ int first_channel; ///< channel number of first entry in freq table
+ int input; ///< used input
+
+ // video related stuff
+ int nVideoFormatUsed; ///< index of used video format
+ AM_MEDIA_TYPE **arpmtVideo; ///< available video formats
+ VIDEO_STREAM_CONFIG_CAPS **arVideoCaps; ///< capabilities of output video
+ AM_MEDIA_TYPE *pmtVideo; ///< video stream properties
+ grabber_ringbuffer_t *v_buf;
+ IBaseFilter *pVideoFilter; ///< interface for capture device
+ IAMStreamConfig *pVideoStreamConfig; ///< for configuring video stream
+
+ // audio related stuff
+ int nAudioFormatUsed; ///< index of used audio format
+ AM_MEDIA_TYPE **arpmtAudio; ///< available audio formats
+ AUDIO_STREAM_CONFIG_CAPS **arAudioCaps; ///< capabilities of output audio
+ AM_MEDIA_TYPE *pmtAudio; ///< audio stream properties.
+ grabber_ringbuffer_t *a_buf;
+ IBaseFilter *pAudioFilter; ///< interface for audio capture device (if adevice passed)
+ IAMStreamConfig *pAudioStreamConfig; ///< for configuring audio stream
+
+ AM_MEDIA_TYPE *pmtVBI; ///< available audio formats
+ grabber_ringbuffer_t *vbi_buf;
+
+ IAMTVTuner *pTVTuner; ///< interface for tuner device
+ IGraphBuilder *pGraph; ///< filter graph
+ ICaptureGraphBuilder2 *pBuilder; ///< graph builder
+ IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...)
+ IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc
+ IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...)
+ DWORD dwRegister; ///< allow graphedit to connect to our graph
+ CSampleGrabberCB* pCSGCB; ///< callback object
+ void *priv_vbi; ///< private VBI data structure
+ tt_stream_props tsp; ///< data for VBI initialization
+
+ tv_param_t* tv_param; ///< TV parameters
+} priv_t;
+
+#include "tvi_def.h"
+
+/**
+ country table entry structure (for loading freq table stored in kstvtuner.ax
+
+ \note
+ structure have to be 2-byte aligned and have 10-byte length!!
+*/
+typedef struct __attribute__((__packed__)) {
+ WORD CountryCode; ///< Country code
+ WORD CableFreqTable; ///< index of resource with frequencies for cable channels
+ WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels
+ DWORD VideoStandard; ///< used video standard
+} TRCCountryList;
+/**
+ information about image formats
+*/
+typedef struct {
+ uint32_t fmt; ///< FourCC
+ const GUID *subtype; ///< DirectShow's subtype
+ int nBits; ///< number of bits
+ int nCompression; ///< complression
+ int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure
+} img_fmt;
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Methods forward declaration
+*
+*---------------------------------------------------------------------------------------
+*/
+static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
+ int blocksize);
+static HRESULT show_filter_info(IBaseFilter * pFilter);
+#if 0
+//defined in current MinGW release
+HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **);
+HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **);
+#endif
+static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
+ pbuf);
+static int set_crossbar_input(priv_t * priv, int input);
+static int subtype2imgfmt(const GUID * subtype);
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Global constants and variables
+*
+*---------------------------------------------------------------------------------------
+*/
+/**
+ lookup tables for physical connector types
+ */
+static const struct {
+ long type;
+ char *name;
+} tv_physcon_types[]={
+ {PhysConn_Video_Tuner, "Tuner" },
+ {PhysConn_Video_Composite, "Composite" },
+ {PhysConn_Video_SVideo, "S-Video" },
+ {PhysConn_Video_RGB, "RGB" },
+ {PhysConn_Video_YRYBY, "YRYBY" },
+ {PhysConn_Video_SerialDigital, "SerialDigital" },
+ {PhysConn_Video_ParallelDigital, "ParallelDigital"},
+ {PhysConn_Video_VideoDecoder, "VideoDecoder" },
+ {PhysConn_Video_VideoEncoder, "VideoEncoder" },
+ {PhysConn_Video_SCART, "SCART" },
+ {PhysConn_Video_Black, "Blaack" },
+ {PhysConn_Audio_Tuner, "Tuner" },
+ {PhysConn_Audio_Line, "Line" },
+ {PhysConn_Audio_Mic, "Mic" },
+ {PhysConn_Audio_AESDigital, "AESDiital" },
+ {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" },
+ {PhysConn_Audio_AudioDecoder, "AudioDecoder" },
+ {PhysConn_Audio_SCSI, "SCSI" },
+ {PhysConn_Video_SCSI, "SCSI" },
+ {PhysConn_Audio_AUX, "AUX" },
+ {PhysConn_Video_AUX, "AUX" },
+ {PhysConn_Audio_1394, "1394" },
+ {PhysConn_Video_1394, "1394" },
+ {PhysConn_Audio_USB, "USB" },
+ {PhysConn_Video_USB, "USB" },
+ {-1, NULL }
+};
+
+static const struct {
+ char *chanlist_name;
+ int country_code;
+} tv_chanlist2country[]={
+ {"us-bcast", 1},
+ {"russia", 7},
+ {"argentina", 54},
+ {"japan-bcast", 81},
+ {"china-bcast", 86},
+ {"southafrica", 27},
+ {"australia", 61},
+ {"ireland", 353},
+ {"france", 33},
+ {"italy", 39},
+ {"newzealand", 64},
+ //directshow table uses eastern europe freq table for russia
+ {"europe-east", 7},
+ //directshow table uses western europe freq table for germany
+ {"europe-west", 49},
+ /* cable channels */
+ {"us-cable", 1},
+ {"us-cable-hrc", 1},
+ {"japan-cable", 81},
+ //default is USA
+ {NULL, 1}
+};
+
+/**
+ array, contains information about various supported (i hope) image formats
+*/
+static const img_fmt img_fmt_list[] = {
+ {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 0, IMGFMT_YUY2, 0},
+ {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 0, IMGFMT_YV12, 0},
+ {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 0, IMGFMT_IYUV, 0},
+ {IMGFMT_I420, &MEDIASUBTYPE_I420, 0, IMGFMT_I420, 0},
+ {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 0, IMGFMT_UYVY, 0},
+ {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 0, IMGFMT_YVYU, 0},
+ {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 0, IMGFMT_YVU9, 0},
+ {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0},
+ {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0},
+ {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12},
+ {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12},
+ {0, &GUID_NULL, 0, 0, 0}
+};
+
+#define TV_NORMS_COUNT 19
+static const struct {
+ long index;
+ char *name;
+} tv_norms[TV_NORMS_COUNT] = {
+ {
+ AnalogVideo_NTSC_M, "ntsc-m"}, {
+ AnalogVideo_NTSC_M_J, "ntsc-mj"}, {
+ AnalogVideo_NTSC_433, "ntsc-433"}, {
+ AnalogVideo_PAL_B, "pal-b"}, {
+ AnalogVideo_PAL_D, "pal-d"}, {
+ AnalogVideo_PAL_G, "pal-g"}, {
+ AnalogVideo_PAL_H, "pal-h"}, {
+ AnalogVideo_PAL_I, "pal-i"}, {
+ AnalogVideo_PAL_M, "pal-m"}, {
+ AnalogVideo_PAL_N, "pal-n"}, {
+ AnalogVideo_PAL_60, "pal-60"}, {
+ AnalogVideo_SECAM_B, "secam-b"}, {
+ AnalogVideo_SECAM_D, "secam-d"}, {
+ AnalogVideo_SECAM_G, "secam-g"}, {
+ AnalogVideo_SECAM_H, "secam-h"}, {
+ AnalogVideo_SECAM_K, "secam-k"}, {
+ AnalogVideo_SECAM_K1, "secam-k1"}, {
+ AnalogVideo_SECAM_L, "secam-l"}, {
+ AnalogVideo_SECAM_L1, "secam-l1"}
+};
+static long tv_available_norms[TV_NORMS_COUNT];
+static int tv_available_norms_count = 0;
+
+
+static long *tv_available_inputs;
+static int tv_available_inputs_count = 0;
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Various GUID definitions
+*
+*---------------------------------------------------------------------------------------
+*/
+/// CLSID definitions (used for CoCreateInstance call)
+DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B,
+ 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
+DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B,
+ 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
+DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B,
+ 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
+DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3,
+ 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
+DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0,
+ 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
+DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0,
+ 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
+DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53,
+ 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53,
+ 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+#ifdef NOT_USED
+DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3,
+ 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
+DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a,
+ 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2);
+DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20,
+ 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3,
+ 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5);
+DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
+ 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
+ 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
+#endif
+
+/// IID definitions (used for QueryInterface call)
+DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a,
+ 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0,
+ 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
+DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa,
+ 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
+DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD,
+ 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F);
+DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0,
+ 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85);
+DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,
+ 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d);
+DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b,
+ 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
+DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a,
+ 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C,
+ 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
+DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
+ 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a,
+ 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
+DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00,
+ 0xAA, 0x00, 0xBD, 0x83, 0x39);
+DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00,
+ 0xa0, 0xc9, 0x11, 0x89, 0x56);
+DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c,
+ 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
+DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0,
+ 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
+DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00,
+ 0xA0, 0xC9, 0x56, 0x02, 0x66);
+DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1,
+ 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
+DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00,
+ 0xaa, 0x00, 0x4b, 0xb8, 0x51);
+DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f,
+ 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
+DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f,
+ 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
+DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
+ 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
+DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f,
+ 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
+DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00,
+ 0xa0, 0xc9, 0x11, 0x89, 0x56);
+DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00,
+ 0x00, 0xc0, 0xcc, 0x16, 0xba);
+
+#define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
+#define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
+
+#define DEVICE_NAME_MAX_LEN 2000
+
+/*---------------------------------------------------------------------------------------
+* Methods, called only from this file
+*---------------------------------------------------------------------------------------*/
+
+void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){
+ ALLOCATOR_PROPERTIES prop;
+ IAMBufferNegotiation* pBN;
+ HRESULT hr;
+
+ prop.cbAlign = -1;
+ prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
+ if (!prop.cbBuffer)
+ prop.cbBuffer = 1;
+ prop.cbBuffer += pWF->nBlockAlign - 1;
+ prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
+ prop.cbPrefix = -1;
+ prop.cBuffers = -1;
+
+ hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
+ if(FAILED(hr))
+ mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
+ else{
+ hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
+ if(FAILED(hr))
+ mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
+ OLE_RELEASE_SAFE(pBN);
+ }
+ hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
+ if(FAILED(hr))
+ mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
+ else{
+ hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
+ if(FAILED(hr))
+ mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
+ OLE_RELEASE_SAFE(pBN);
+ }
+}
+/*
+*---------------------------------------------------------------------------------------
+*
+* CSampleGrabberCD class. Used for receiving samples from DirectShow.
+*
+*---------------------------------------------------------------------------------------
+*/
+/// CSampleGrabberCD destructor
+static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
+{
+ free(This->lpVtbl);
+ free(This);
+}
+
+/// CSampleGrabberCD IUnknown interface methods implementation
+static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
+ This,
+ const GUID * riid,
+ void **ppvObject)
+{
+ CSampleGrabberCB *me = (CSampleGrabberCB *) This;
+ GUID *r;
+ unsigned int i = 0;
+ Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
+ if (!ppvObject)
+ return E_POINTER;
+ for (r = me->interfaces;
+ i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
+ if (!memcmp(r, riid, sizeof(*r))) {
+ OLE_CALL(This, AddRef);
+ *ppvObject = This;
+ return 0;
+ }
+ Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
+ return E_NOINTERFACE;
+}
+
+static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
+{
+ CSampleGrabberCB *me = (CSampleGrabberCB *) This;
+ Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
+ me->refcount);
+ return ++(me->refcount);
+}
+
+static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
+{
+ CSampleGrabberCB *me = (CSampleGrabberCB *) This;
+ Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
+ This, me->refcount - 1);
+ if (--(me->refcount) == 0)
+ CSampleGrabberCB_Destroy(me);
+ return 0;
+}
+
+
+HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This,
+ double SampleTime,
+ BYTE * pBuffer, long lBufferLen)
+{
+ CSampleGrabberCB *this = (CSampleGrabberCB *) This;
+ grabber_ringbuffer_t *rb = this->pbuf;
+
+ if (!lBufferLen)
+ return E_FAIL;
+
+ if (!rb->ringbuffer) {
+ rb->buffersize /= lBufferLen;
+ if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
+ return E_FAIL;
+ }
+ mp_msg(MSGT_TV, MSGL_DBG4,
+ "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
+ EnterCriticalSection(rb->pMutex);
+ if (rb->count >= rb->buffersize) {
+ rb->head = (rb->head + 1) % rb->buffersize;
+ rb->count--;
+ }
+
+ memcpy(rb->ringbuffer[rb->tail], pBuffer,
+ lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
+ rb->dpts[rb->tail] = SampleTime;
+ rb->tail = (rb->tail + 1) % rb->buffersize;
+ rb->count++;
+ LeaveCriticalSection(rb->pMutex);
+
+ return S_OK;
+}
+
+/// wrapper. directshow does the same when BufferCB callback is requested
+HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This,
+ double SampleTime,
+ LPMEDIASAMPLE pSample)
+{
+ char* buf;
+ long len;
+ long long tStart,tEnd;
+ HRESULT hr;
+ grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
+
+ len=OLE_CALL(pSample,GetSize);
+ tStart=tEnd=0;
+ hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
+ if(FAILED(hr)){
+ return hr;
+ }
+ mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
+ hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
+ if(FAILED(hr)){
+ return hr;
+ }
+ hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
+ return hr;
+
+}
+
+/// main grabbing routine
+static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
+ pbuf)
+{
+ CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
+ if (!This)
+ return NULL;
+
+ This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
+ if (!This->lpVtbl) {
+ CSampleGrabberCB_Destroy(This);
+ return NULL;
+ }
+ This->refcount = 1;
+ This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
+ This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
+ This->lpVtbl->Release = CSampleGrabberCB_Release;
+ This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
+ This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
+
+ This->interfaces[0] = IID_IUnknown;
+ This->interfaces[1] = IID_ISampleGrabberCB;
+
+ This->pbuf = pbuf;
+
+ return This;
+}
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* ROT related methods (register, unregister)
+*
+*---------------------------------------------------------------------------------------
+*/
+/**
+Registering graph in ROT. User will be able to connect to graph from GraphEdit.
+*/
+static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
+{
+ IMoniker *pMoniker;
+ IRunningObjectTable *pROT;
+ WCHAR wsz[256];
+ HRESULT hr;
+
+ if (FAILED(GetRunningObjectTable(0, &pROT))) {
+ return E_FAIL;
+ }
+ wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
+ GetCurrentProcessId());
+ hr = CreateItemMoniker(L"!", wsz, &pMoniker);
+ if (SUCCEEDED(hr)) {
+ hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
+ pUnkGraph, pMoniker, pdwRegister);
+ OLE_RELEASE_SAFE(pMoniker);
+ }
+ OLE_RELEASE_SAFE(pROT);
+ return hr;
+}
+
+/// Unregistering graph in ROT
+static void RemoveFromRot(DWORD dwRegister)
+{
+ IRunningObjectTable *pROT;
+ if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
+ OLE_CALL_ARGS(pROT, Revoke, dwRegister);
+ OLE_RELEASE_SAFE(pROT);
+ }
+}
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* ringbuffer related methods (init, destroy)
+*
+*---------------------------------------------------------------------------------------
+*/
+/**
+ * \brief ringbuffer destroying routine
+ *
+ * \param rb pointer to empty (just allocated) ringbuffer structure
+ *
+ * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
+ */
+static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
+{
+ int i;
+
+ if (!rb)
+ return;
+
+ if (rb->ringbuffer) {
+ for (i = 0; i < rb->buffersize; i++)
+ if (rb->ringbuffer[i])
+ free(rb->ringbuffer[i]);
+ free(rb->ringbuffer);
+ rb->ringbuffer = NULL;
+ }
+ if (rb->dpts) {
+ free(rb->dpts);
+ rb->dpts = NULL;
+ }
+ if (rb->pMutex) {
+ DeleteCriticalSection(rb->pMutex);
+ free(rb->pMutex);
+ rb->pMutex = NULL;
+ }
+
+ rb->blocksize = 0;
+ rb->buffersize = 0;
+ rb->head = 0;
+ rb->tail = 0;
+ rb->count = 0;
+}
+
+/**
+ * \brief ringbuffer initialization
+ *
+ * \param rb pointer to empty (just allocated) ringbuffer structure
+ * \param buffersize size of buffer in blocks
+ * \param blocksize size of buffer's block
+ *
+ * \return S_OK if success
+ * \return E_OUTOFMEMORY not enough memory
+ *
+ * \note routine does not allocates memory for grabber_rinbuffer_s structure
+ */
+static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
+ int blocksize)
+{
+ int i;
+
+ if (!rb)
+ return E_OUTOFMEMORY;
+
+ rb->buffersize = buffersize < 2 ? 2 : buffersize;
+ rb->blocksize = blocksize;
+
+ mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
+ rb->buffersize, rb->blocksize);
+
+ rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *));
+ if (!rb)
+ return E_POINTER;
+ memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
+
+ for (i = 0; i < rb->buffersize; i++) {
+ rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char));
+ if (!rb->ringbuffer[i]) {
+ destroy_ringbuffer(rb);
+ return E_OUTOFMEMORY;
+ }
+ }
+ rb->dpts = (double*) malloc(rb->buffersize * sizeof(double));
+ if (!rb->dpts) {
+ destroy_ringbuffer(rb);
+ return E_OUTOFMEMORY;
+ }
+ rb->head = 0;
+ rb->tail = 0;
+ rb->count = 0;
+ rb->tStart = -1;
+ rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
+ if (!rb->pMutex) {
+ destroy_ringbuffer(rb);
+ return E_OUTOFMEMORY;
+ }
+ InitializeCriticalSection(rb->pMutex);
+ return S_OK;
+}
+
+/*
+*---------------------------------------------------------------------------------------
+*
+* Tuner related methods (frequency, capabilities, etc
+*
+*---------------------------------------------------------------------------------------
+*/
+/**
+ * \brief returns string with name for givend PsysCon_* constant
+ *
+ * \param lPhysicalType constant from PhysicalConnectorType enumeration
+ *
+ * \return pointer to string with apropriate name
+ *
+ * \note
+ * Caller should not free returned pointer
+ */
+static char *physcon2str(const long lPhysicalType)
+{
+ int i;
+ for(i=0; tv_physcon_types[i].name; i++)
+ if(tv_physcon_types[i].type==lPhysicalType)
+ return tv_physcon_types[i].name;
+ return "Unknown";
+};
+
+/**
+ * \brief converts MPlayer's chanlist to system country code.
+ *
+ * \param chanlist MPlayer's chanlist name
+ *
+ * \return system country code
+ *
+ * \remarks
+ * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
+ * country (which is usually larger then MPlayer's one, so workaround will work fine).
+ *
+ * \todo
+ * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
+ */
+static int chanlist2country(char *chanlist)
+{
+ int i;
+ for(i=0; tv_chanlist2country[i].chanlist_name; i++)
+ if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
+ break;
+ return tv_chanlist2country[i].country_code;
+}
+
+/**
+ * \brief loads specified resource from module and return pointer to it
+ *
+ * \param hDLL valid module desriptor
+ * \param index index of resource. resource with name "#<index>" will be loaded
+ *
+ * \return pointer to loader resource or NULL if error occured
+ */
+static void *GetRC(HMODULE hDLL, int index)
+{
+ char szRCDATA[10];
+ char szName[10];
+ HRSRC hRes;
+ HGLOBAL hTable;
+
+ snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
+ snprintf(szName, 10, "#%d", index);
+
+ hRes = FindResource(hDLL, szName, szRCDATA);
+ if (!hRes) {
+ return NULL;
+ }
+ hTable = LoadResource(hDLL, hRes);
+ if (!hTable) {
+ return NULL;
+ }
+ return LockResource(hTable);
+}
+
+/**
+ * \brief loads frequency table for given country from kstvtune.ax
+ *
+ * \param[in] nCountry - country code
+ * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
+ * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
+ * \param[out] pnLen length of array
+ * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
+ *
+ * \return S_OK if success
+ * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
+ * \return E_FAIL error occured during load
+ *
+ * \remarks
+ * - array must be freed by caller
+ * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
+ */
+static HRESULT load_freq_table(int nCountry, int nInputType,
+ long **pplFreqTable, int *pnLen,
+ int *pnFirst)
+{
+ HMODULE hDLL;
+ long *plFreqTable;
+ TRCCountryList *pCountryList;
+ int i, index;
+
+ mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%d)\n",nCountry,nInputType);
+ /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
+
+ if (!pplFreqTable || !pnFirst || !pnLen)
+ return E_POINTER;
+ if (!nCountry)
+ return E_FAIL;
+
+ hDLL = LoadLibrary("kstvtune.ax");
+ if (!hDLL) {
+ return E_FAIL;
+ }
+ pCountryList = GetRC(hDLL, 9999);
+ if (!pCountryList) {
+ FreeLibrary(hDLL);
+ return E_FAIL;
+ }
+ for (i = 0; pCountryList[i].CountryCode != 0; i++)
+ if (pCountryList[i].CountryCode == nCountry)
+ break;
+ if (pCountryList[i].CountryCode == 0) {
+ FreeLibrary(hDLL);
+ return E_FAIL;
+ }
+ if (nInputType == TunerInputCable)
+ index = pCountryList[i].CableFreqTable;
+ else
+ index = pCountryList[i].BroadcastFreqTable;
+
+ plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
+ if (!plFreqTable) {
+ FreeLibrary(hDLL);
+ return E_FAIL;
+ }
+ *pnFirst = plFreqTable[0];
+ *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
+ *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long));
+ if (!*pplFreqTable) {
+ FreeLibrary(hDLL);
+ return E_FAIL;
+ }
+ for (i = 0; i < *pnLen; i++) {
+ (*pplFreqTable)[i] = plFreqTable[i + 2];
+ }
+ FreeLibrary(hDLL);
+ return S_OK;
+}
+
+/**
+ * \brief tunes to given frequency through IKsPropertySet call
+ *
+ * \param pTVTuner IAMTVTuner interface of capture device
+ * \param lFreq frequency to tune (in Hz)
+ *
+ * \return S_OK success
+ * \return apropriate error code otherwise
+ *
+ * \note
+ * Due to either bug in driver or error in following code calll to IKsProperty::Set
+ * in this methods always fail with error 0x8007007a.
+ *
+ * \todo test code on other machines and an error
+ */
+static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
+{
+ HRESULT hr;
+ DWORD dwSupported = 0;
+ DWORD cbBytes = 0;
+ KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
+ KSPROPERTY_TUNER_FREQUENCY_S frequency;
+ IKsPropertySet *pKSProp;
+
+ mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
+
+ memset(&mode_caps, 0, sizeof(mode_caps));
+ memset(&frequency, 0, sizeof(frequency));
+
+ hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
+ if (FAILED(hr))
+ return hr; //no IKsPropertySet interface
+
+ mode_caps.Mode = AMTUNER_MODE_TV;
+ hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
+ KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
+ if (FAILED(hr)) {
+ OLE_RELEASE_SAFE(pKSProp);
+ return hr;
+ }
+
+ if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
+ OLE_RELEASE_SAFE(pKSProp);
+ return E_FAIL; //PROPSETID_TINER not supported
+ }
+
+ hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
+ KSPROPERTY_TUNER_MODE_CAPS,
+ INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
+ INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
+ &mode_caps, sizeof(mode_caps), &cbBytes);
+
+ frequency.Frequency = lFreq;
+
+ if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
+ frequency.TuningFlags = KS_TUNER_TUNING_FINE;
+ else
+ frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
+
+ if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
+ OLE_RELEASE_SAFE(pKSProp);
+ return E_FAIL;
+ }
+
+ hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
+ KSPROPERTY_TUNER_FREQUENCY,
+ INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
+ INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
+ &frequency, sizeof(frequency));
+ if (FAILED(hr)) {
+ OLE_RELEASE_SAFE(pKSProp);
+ return hr;
+ }
+
+ OLE_R