vo_glamo: sub.h was moved to sub directory in c9026cb3210205b07e2e068467a18ee40f9259a3
[mplayer/glamo.git] / stream / tvi_dshow.c
blob3b4535ac8c36ea49abffb77ea18981465decf5d6
1 /*
2 * TV support under Win32
4 * (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>
6 * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c code.
8 * This file is part of MPlayer.
10 * MPlayer is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * MPlayer is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * WARNING: This is alpha code!
28 * Abilities:
29 * * Watching TV under Win32 using WDM Capture driver and DirectShow
30 * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow)
31 * * If device driver provides IAMStreamConfig interface, user can choose width/height with "-tv height=<h>:width=<w>"
32 * * Adjusting BRIGHTNESS,HUE,SATURATION,CONTRAST if supported by device
33 * * Selecting Tuner,S-Video,... as media source
34 * * User can select used video capture device, passing -tv device=<dev#>
35 * * User can select used audio input, passing -tv audioid=<input#>
37 * options which will not be implemented (probably sometime in future, if possible):
38 * * alsa
39 * * mjpeg
40 * * decimation=<1|2|4>
41 * * quality=<0\-100>
42 * * forceaudio
43 * * forcechan=<1\-2>
44 * * [volume|bass|treble|balance]
46 * Works with:
47 * - LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov
48 * Partially works with:
49 * - ATI 9200 VIVO based card
50 * - ATI AIW 7500
51 * - nVidia Ti-4400
53 * Known bugs:
54 * * stream goes with 24.93 FPS (NTSC), while reporting 25 FPS (PAL) ?!
55 * * direct set frequency call does not work ('Insufficient Buffer' error)
56 * * audio stream goes with about 1 sample/sec rate when capturing sound from audio card
58 * TODO:
59 * * check audio with small buffer on vivo !!!
60 * * norm for IAMVideoDecoder and for IAMTVtuner - differs !!
61 * * check how to change TVFormat on VIVO card without tuner
62 * * Flip image upside-down for RGB formats.
63 * *
64 * * remove debug sleep()
65 * * Add some notes to methods' parameters
66 * * refactor console messages
67 * * check using header files and keep only needed
68 * * add additional comments to methods' bodies
73 /// \ingroup tvi_dshow
75 #include "config.h"
77 #include <stdio.h>
78 #include "libmpcodecs/img_format.h"
79 #include "libmpcodecs/dec_teletext.h"
80 #include "libaf/af_format.h"
81 #include "osdep/timer.h"
84 #include "tv.h"
85 #include "mp_msg.h"
86 #include "frequencies.h"
89 #include "tvi_dshow.h"
91 #ifndef STDCALL
92 // mingw64 needs this
93 #define STDCALL __stdcall
94 #endif
96 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param);
99 *---------------------------------------------------------------------------------------
101 * Data structures
103 *---------------------------------------------------------------------------------------
106 information about this file
108 const tvi_info_t tvi_info_dshow = {
109 tvi_init_dshow,
110 "DirectShow TV",
111 "dshow",
112 "Vladimir Voroshilov",
113 "Very experimental!! Use with caution"
118 ringbuffer related info
120 typedef struct {
121 CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex)
122 char **ringbuffer; ///< ringbuffer array
123 double*dpts; ///< samples' timestamps
125 int buffersize; ///< size of buffer in blocks
126 int blocksize; ///< size of individual block
127 int head; ///< index of first valid sample
128 int tail; ///< index of last valid sample
129 int count; ///< count of valid samples in ringbuffer
130 double tStart; ///< pts of first sample (first sample should have pts 0)
131 } grabber_ringbuffer_t;
133 typedef enum { unknown, video, audio, vbi } stream_type;
136 CSampleGrabberCD definition
138 typedef struct CSampleGrabberCB {
139 ISampleGrabberCBVtbl *lpVtbl;
140 int refcount;
141 GUID interfaces[2];
142 grabber_ringbuffer_t *pbuf;
143 } CSampleGrabberCB;
146 Chain related structure
148 typedef struct {
149 stream_type type; ///< stream type
150 const GUID* majortype; ///< GUID of major mediatype (video/audio/vbi)
151 const GUID* pin_category; ///< pin category (pointer to one of PIN_CATEGORY_*)
153 IBaseFilter *pCaptureFilter; ///< capture device filter
154 IAMStreamConfig *pStreamConfig; ///< for configuring stream
155 ISampleGrabber *pSG; ///< ISampleGrabber interface of SampleGrabber filter
156 IBaseFilter *pSGF; ///< IBaseFilter interface of SampleGrabber filter
157 IPin *pCapturePin; ///< output capture pin
158 IPin *pSGIn; ///< input pin of SampleGrabber filter
160 grabber_ringbuffer_t *rbuf; ///< sample frabber data
161 CSampleGrabberCB* pCSGCB; ///< callback object
163 AM_MEDIA_TYPE *pmt; ///< stream properties.
164 int nFormatUsed; ///< index of used format
165 AM_MEDIA_TYPE **arpmt; ///< available formats
166 void** arStreamCaps; ///< VIDEO_STREAM_CONFIG_CAPS or AUDIO_STREAM_CONFIG_CAPS
167 } chain_t;
169 typedef struct priv {
170 int dev_index; ///< capture device index in device list (defaul: 0, first available device)
171 int adev_index; ///< audio capture device index in device list (default: -1, not used)
172 int immediate_mode; ///< immediate mode (no sound capture)
173 int state; ///< state: 1-filter graph running, 0-filter graph stopped
174 int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency
175 int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency
177 int fcc; ///< used video format code (FourCC)
178 int width; ///< picture width (default: auto)
179 int height; ///< picture height (default: auto)
181 int channels; ///< number of audio channels (default: auto)
182 int samplerate; ///< audio samplerate (default: auto)
184 long *freq_table; ///< frequency table (in Hz)
185 int freq_table_len; ///< length of freq table
186 int first_channel; ///< channel number of first entry in freq table
187 int input; ///< used input
189 chain_t* chains[3]; ///< chains' data (0-video, 1-audio, 2-vbi)
191 IAMTVTuner *pTVTuner; ///< interface for tuner device
192 IGraphBuilder *pGraph; ///< filter graph
193 ICaptureGraphBuilder2 *pBuilder; ///< graph builder
194 IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...)
195 IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc
196 IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...)
197 DWORD dwRegister; ///< allow graphedit to connect to our graph
198 void *priv_vbi; ///< private VBI data structure
199 tt_stream_props tsp; ///< data for VBI initialization
201 tv_param_t* tv_param; ///< TV parameters
202 } priv_t;
204 #include "tvi_def.h"
207 country table entry structure (for loading freq table stored in kstvtuner.ax
209 \note
210 structure have to be 2-byte aligned and have 10-byte length!!
212 typedef struct __attribute__((__packed__)) {
213 WORD CountryCode; ///< Country code
214 WORD CableFreqTable; ///< index of resource with frequencies for cable channels
215 WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels
216 DWORD VideoStandard; ///< used video standard
217 } TRCCountryList;
219 information about image formats
221 typedef struct {
222 uint32_t fmt; ///< FourCC
223 const GUID *subtype; ///< DirectShow's subtype
224 int nBits; ///< number of bits
225 int nCompression; ///< complression
226 int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure
227 } img_fmt;
230 *---------------------------------------------------------------------------------------
232 * Methods forward declaration
234 *---------------------------------------------------------------------------------------
236 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
237 int blocksize);
238 static HRESULT show_filter_info(IBaseFilter * pFilter);
239 #if 0
240 //defined in current MinGW release
241 HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **);
242 HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **);
243 #endif
244 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
245 pbuf);
246 static int set_crossbar_input(priv_t * priv, int input);
247 static int subtype2imgfmt(const GUID * subtype);
250 *---------------------------------------------------------------------------------------
252 * Global constants and variables
254 *---------------------------------------------------------------------------------------
257 lookup tables for physical connector types
259 static const struct {
260 long type;
261 char *name;
262 } tv_physcon_types[]={
263 {PhysConn_Video_Tuner, "Tuner" },
264 {PhysConn_Video_Composite, "Composite" },
265 {PhysConn_Video_SVideo, "S-Video" },
266 {PhysConn_Video_RGB, "RGB" },
267 {PhysConn_Video_YRYBY, "YRYBY" },
268 {PhysConn_Video_SerialDigital, "SerialDigital" },
269 {PhysConn_Video_ParallelDigital, "ParallelDigital"},
270 {PhysConn_Video_VideoDecoder, "VideoDecoder" },
271 {PhysConn_Video_VideoEncoder, "VideoEncoder" },
272 {PhysConn_Video_SCART, "SCART" },
273 {PhysConn_Video_Black, "Blaack" },
274 {PhysConn_Audio_Tuner, "Tuner" },
275 {PhysConn_Audio_Line, "Line" },
276 {PhysConn_Audio_Mic, "Mic" },
277 {PhysConn_Audio_AESDigital, "AESDiital" },
278 {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" },
279 {PhysConn_Audio_AudioDecoder, "AudioDecoder" },
280 {PhysConn_Audio_SCSI, "SCSI" },
281 {PhysConn_Video_SCSI, "SCSI" },
282 {PhysConn_Audio_AUX, "AUX" },
283 {PhysConn_Video_AUX, "AUX" },
284 {PhysConn_Audio_1394, "1394" },
285 {PhysConn_Video_1394, "1394" },
286 {PhysConn_Audio_USB, "USB" },
287 {PhysConn_Video_USB, "USB" },
288 {-1, NULL }
291 static const struct {
292 char *chanlist_name;
293 int country_code;
294 } tv_chanlist2country[]={
295 {"us-bcast", 1},
296 {"russia", 7},
297 {"argentina", 54},
298 {"japan-bcast", 81},
299 {"china-bcast", 86},
300 {"southafrica", 27},
301 {"australia", 61},
302 {"ireland", 353},
303 {"france", 33},
304 {"italy", 39},
305 {"newzealand", 64},
306 //directshow table uses eastern europe freq table for russia
307 {"europe-east", 7},
308 //directshow table uses western europe freq table for germany
309 {"europe-west", 49},
310 /* cable channels */
311 {"us-cable", 1},
312 {"us-cable-hrc", 1},
313 {"japan-cable", 81},
314 //default is USA
315 {NULL, 1}
319 array, contains information about various supported (i hope) image formats
321 static const img_fmt img_fmt_list[] = {
322 {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 16, IMGFMT_YUY2, 0},
323 {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 12, IMGFMT_YV12, 0},
324 {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 12, IMGFMT_IYUV, 0},
325 {IMGFMT_I420, &MEDIASUBTYPE_I420, 12, IMGFMT_I420, 0},
326 {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 16, IMGFMT_UYVY, 0},
327 {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 16, IMGFMT_YVYU, 0},
328 {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 9, IMGFMT_YVU9, 0},
329 {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0},
330 {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0},
331 {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12},
332 {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12},
333 {0, &GUID_NULL, 0, 0, 0}
336 #define TV_NORMS_COUNT 19
337 static const struct {
338 long index;
339 char *name;
340 } tv_norms[TV_NORMS_COUNT] = {
342 AnalogVideo_NTSC_M, "ntsc-m"}, {
343 AnalogVideo_NTSC_M_J, "ntsc-mj"}, {
344 AnalogVideo_NTSC_433, "ntsc-433"}, {
345 AnalogVideo_PAL_B, "pal-b"}, {
346 AnalogVideo_PAL_D, "pal-d"}, {
347 AnalogVideo_PAL_G, "pal-g"}, {
348 AnalogVideo_PAL_H, "pal-h"}, {
349 AnalogVideo_PAL_I, "pal-i"}, {
350 AnalogVideo_PAL_M, "pal-m"}, {
351 AnalogVideo_PAL_N, "pal-n"}, {
352 AnalogVideo_PAL_60, "pal-60"}, {
353 AnalogVideo_SECAM_B, "secam-b"}, {
354 AnalogVideo_SECAM_D, "secam-d"}, {
355 AnalogVideo_SECAM_G, "secam-g"}, {
356 AnalogVideo_SECAM_H, "secam-h"}, {
357 AnalogVideo_SECAM_K, "secam-k"}, {
358 AnalogVideo_SECAM_K1, "secam-k1"}, {
359 AnalogVideo_SECAM_L, "secam-l"}, {
360 AnalogVideo_SECAM_L1, "secam-l1"}
362 static long tv_available_norms[TV_NORMS_COUNT];
363 static int tv_available_norms_count = 0;
366 static long *tv_available_inputs;
367 static int tv_available_inputs_count = 0;
370 *---------------------------------------------------------------------------------------
372 * Various GUID definitions
374 *---------------------------------------------------------------------------------------
376 // selectany can not be used with "static", fixes compilation with mingw-w64
377 #undef DECLSPEC_SELECTANY
378 #define DECLSPEC_SELECTANY
379 /// CLSID definitions (used for CoCreateInstance call)
380 #define CLSID_SampleGrabber MP_CLSID_SampleGrabber
381 static DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B,
382 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
383 #define CLSID_NullRenderer MP_CLSID_NullRenderer
384 static DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B,
385 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
386 #define CLSID_SystemDeviceEnum MP_CLSID_SystemDeviceEnum
387 static DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B,
388 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
389 #define CLSID_CaptureGraphBuilder2 MP_CLSID_CaptureGraphBuilder2
390 static DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3,
391 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
392 #define CLSID_VideoInputDeviceCategory MP_CLSID_VideoInputDeviceCategory
393 static DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0,
394 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
395 #define CLSID_AudioInputDeviceCategory MP_CLSID_AudioInputDeviceCategory
396 static DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0,
397 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
398 #define CLSID_FilterGraph MP_CLSID_FilterGraph
399 static DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53,
400 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
401 #define CLSID_SystemClock MP_CLSID_SystemClock
402 static DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53,
403 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
404 #ifdef NOT_USED
405 #define CLSID_CaptureGraphBuilder MP_CLSID_CaptureGraphBuilder
406 static DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3,
407 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
408 #define CLSID_VideoPortManager MP_CLSID_VideoPortManager
409 static DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a,
410 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2);
411 #define IID_IPin MP_IID_IPin
412 static DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20,
413 0xaf, 0x0b, 0xa7, 0x70);
414 #define IID_ICaptureGraphBuilder MP_IID_ICaptureGraphBuilder
415 static DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3,
416 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5);
417 #define IID_IFilterGraph MP_IID_IFilterGraph
418 static DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
419 0x20, 0xaf, 0x0b, 0xa7, 0x70);
420 #define PIN_CATEGORY_PREVIEW MP_PIN_CATEGORY_PREVIEW
421 static DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
422 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
423 #endif
425 /// IID definitions (used for QueryInterface call)
426 #define IID_IReferenceClock MP_IID_IReferenceClock
427 static DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a,
428 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
429 #define IID_IAMBufferNegotiation MP_IID_IAMBufferNegotiation
430 static DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0,
431 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
432 #define IID_IKsPropertySet MP_IID_IKsPropertySet
433 static DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa,
434 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
435 #define IID_ISampleGrabber MP_IID_ISampleGrabber
436 static DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD,
437 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F);
438 #define IID_ISampleGrabberCB MP_IID_ISampleGrabberCB
439 static DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0,
440 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85);
441 #define IID_ICaptureGraphBuilder2 MP_IID_ICaptureGraphBuilder2
442 static DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,
443 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d);
444 #define IID_ICreateDevEnum MP_IID_ICreateDevEnum
445 static DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b,
446 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
447 #define IID_IGraphBuilder MP_IID_IGraphBuilder
448 static DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a,
449 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
450 #define IID_IAMVideoProcAmp MP_IID_IAMVideoProcAmp
451 static DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C,
452 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
453 #define IID_IVideoWindow MP_IID_IVideoWindow
454 static DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
455 0x20, 0xaf, 0x0b, 0xa7, 0x70);
456 #define IID_IMediaControl MP_IID_IMediaControl
457 static DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a,
458 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
459 #define IID_IAMTVTuner MP_IID_IAMTVTuner
460 static DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00,
461 0xAA, 0x00, 0xBD, 0x83, 0x39);
462 #define IID_IAMCrossbar MP_IID_IAMCrossbar
463 static DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00,
464 0xa0, 0xc9, 0x11, 0x89, 0x56);
465 #define IID_IAMStreamConfig MP_IID_IAMStreamConfig
466 static DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c,
467 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
468 #define IID_IAMAudioInputMixer MP_IID_IAMAudioInputMixer
469 static DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0,
470 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
471 #define IID_IAMTVAudio MP_IID_IAMTVAudio
472 static DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00,
473 0xA0, 0xC9, 0x56, 0x02, 0x66);
474 #define IID_IAMAnalogVideoDecoder MP_IID_IAMAnalogVideoDecoder
475 static DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1,
476 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
477 #define IID_IPropertyBag MP_IID_IPropertyBag
478 static DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00,
479 0xaa, 0x00, 0x4b, 0xb8, 0x51);
480 #define PIN_CATEGORY_CAPTURE MP_PIN_CATEGORY_CAPTURE
481 static DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f,
482 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
483 #define PIN_CATEGORY_VIDEOPORT MP_PIN_CATEGORY_VIDEOPORT
484 static DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f,
485 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
486 #define PIN_CATEGORY_PREVIEW MP_PIN_CATEGORY_PREVIEW
487 static DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
488 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
489 #define PIN_CATEGORY_VBI MP_PIN_CATEGORY_VBI
490 static DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f,
491 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
492 #define PROPSETID_TUNER MP_PROPSETID_TUNER
493 static DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00,
494 0xa0, 0xc9, 0x11, 0x89, 0x56);
495 #define MEDIATYPE_VBI MP_MEDIATYPE_VBI
496 static DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00,
497 0x00, 0xc0, 0xcc, 0x16, 0xba);
499 #define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
500 #define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
502 #define DEVICE_NAME_MAX_LEN 2000
504 /*---------------------------------------------------------------------------------------
505 * Methods, called only from this file
506 *---------------------------------------------------------------------------------------*/
508 void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){
509 ALLOCATOR_PROPERTIES prop;
510 IAMBufferNegotiation* pBN;
511 HRESULT hr;
513 prop.cbAlign = -1;
514 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
515 if (!prop.cbBuffer)
516 prop.cbBuffer = 1;
517 prop.cbBuffer += pWF->nBlockAlign - 1;
518 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
519 prop.cbPrefix = -1;
520 prop.cBuffers = -1;
522 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
523 if(FAILED(hr))
524 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
525 else{
526 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
527 if(FAILED(hr))
528 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
529 OLE_RELEASE_SAFE(pBN);
531 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
532 if(FAILED(hr))
533 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
534 else{
535 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
536 if(FAILED(hr))
537 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
538 OLE_RELEASE_SAFE(pBN);
542 *---------------------------------------------------------------------------------------
544 * CSampleGrabberCD class. Used for receiving samples from DirectShow.
546 *---------------------------------------------------------------------------------------
548 /// CSampleGrabberCD destructor
549 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
551 free(This->lpVtbl);
552 free(This);
555 /// CSampleGrabberCD IUnknown interface methods implementation
556 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
557 This,
558 const GUID * riid,
559 void **ppvObject)
561 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
562 GUID *r;
563 unsigned int i = 0;
564 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
565 if (!ppvObject)
566 return E_POINTER;
567 for (r = me->interfaces;
568 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
569 if (!memcmp(r, riid, sizeof(*r))) {
570 OLE_CALL(This, AddRef);
571 *ppvObject = This;
572 return 0;
574 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
575 return E_NOINTERFACE;
578 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
580 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
581 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
582 me->refcount);
583 return ++(me->refcount);
586 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
588 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
589 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
590 This, me->refcount - 1);
591 if (--(me->refcount) == 0)
592 CSampleGrabberCB_Destroy(me);
593 return 0;
597 HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This,
598 double SampleTime,
599 BYTE * pBuffer, long lBufferLen)
601 CSampleGrabberCB *this = (CSampleGrabberCB *) This;
602 grabber_ringbuffer_t *rb = this->pbuf;
604 if (!lBufferLen)
605 return E_FAIL;
607 if (!rb->ringbuffer) {
608 rb->buffersize /= lBufferLen;
609 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
610 return E_FAIL;
612 mp_msg(MSGT_TV, MSGL_DBG4,
613 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
614 EnterCriticalSection(rb->pMutex);
615 if (rb->count >= rb->buffersize) {
616 rb->head = (rb->head + 1) % rb->buffersize;
617 rb->count--;
620 memcpy(rb->ringbuffer[rb->tail], pBuffer,
621 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
622 rb->dpts[rb->tail] = SampleTime;
623 rb->tail = (rb->tail + 1) % rb->buffersize;
624 rb->count++;
625 LeaveCriticalSection(rb->pMutex);
627 return S_OK;
630 /// wrapper. directshow does the same when BufferCB callback is requested
631 HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This,
632 double SampleTime,
633 LPMEDIASAMPLE pSample)
635 char* buf;
636 long len;
637 long long tStart,tEnd;
638 HRESULT hr;
639 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
641 len=OLE_CALL(pSample,GetSize);
642 tStart=tEnd=0;
643 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
644 if(FAILED(hr)){
645 return hr;
647 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
648 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
649 if(FAILED(hr)){
650 return hr;
652 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
653 return hr;
657 /// main grabbing routine
658 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
659 pbuf)
661 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
662 if (!This)
663 return NULL;
665 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
666 if (!This->lpVtbl) {
667 CSampleGrabberCB_Destroy(This);
668 return NULL;
670 This->refcount = 1;
671 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
672 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
673 This->lpVtbl->Release = CSampleGrabberCB_Release;
674 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
675 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
677 This->interfaces[0] = IID_IUnknown;
678 This->interfaces[1] = IID_ISampleGrabberCB;
680 This->pbuf = pbuf;
682 return This;
686 *---------------------------------------------------------------------------------------
688 * ROT related methods (register, unregister)
690 *---------------------------------------------------------------------------------------
693 Registering graph in ROT. User will be able to connect to graph from GraphEdit.
695 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
697 IMoniker *pMoniker;
698 IRunningObjectTable *pROT;
699 WCHAR wsz[256];
700 HRESULT hr;
702 if (FAILED(GetRunningObjectTable(0, &pROT))) {
703 return E_FAIL;
705 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
706 GetCurrentProcessId());
707 hr = CreateItemMoniker(L"!", wsz, &pMoniker);
708 if (SUCCEEDED(hr)) {
709 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
710 pUnkGraph, pMoniker, pdwRegister);
711 OLE_RELEASE_SAFE(pMoniker);
713 OLE_RELEASE_SAFE(pROT);
714 return hr;
717 /// Unregistering graph in ROT
718 static void RemoveFromRot(DWORD dwRegister)
720 IRunningObjectTable *pROT;
721 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
722 OLE_CALL_ARGS(pROT, Revoke, dwRegister);
723 OLE_RELEASE_SAFE(pROT);
728 *---------------------------------------------------------------------------------------
730 * ringbuffer related methods (init, destroy)
732 *---------------------------------------------------------------------------------------
735 * \brief ringbuffer destroying routine
737 * \param rb pointer to empty (just allocated) ringbuffer structure
739 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
741 static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
743 int i;
745 if (!rb)
746 return;
748 if (rb->ringbuffer) {
749 for (i = 0; i < rb->buffersize; i++)
750 free(rb->ringbuffer[i]);
751 free(rb->ringbuffer);
752 rb->ringbuffer = NULL;
754 free(rb->dpts);
755 rb->dpts = NULL;
756 if (rb->pMutex) {
757 DeleteCriticalSection(rb->pMutex);
758 free(rb->pMutex);
759 rb->pMutex = NULL;
762 rb->blocksize = 0;
763 rb->buffersize = 0;
764 rb->head = 0;
765 rb->tail = 0;
766 rb->count = 0;
770 * \brief ringbuffer initialization
772 * \param rb pointer to empty (just allocated) ringbuffer structure
773 * \param buffersize size of buffer in blocks
774 * \param blocksize size of buffer's block
776 * \return S_OK if success
777 * \return E_OUTOFMEMORY not enough memory
779 * \note routine does not allocates memory for grabber_rinbuffer_s structure
781 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
782 int blocksize)
784 int i;
786 if (!rb)
787 return E_OUTOFMEMORY;
789 rb->buffersize = buffersize < 2 ? 2 : buffersize;
790 rb->blocksize = blocksize;
792 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
793 rb->buffersize, rb->blocksize);
795 rb->ringbuffer = malloc(rb->buffersize * sizeof(char *));
796 if (!rb)
797 return E_POINTER;
798 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
800 for (i = 0; i < rb->buffersize; i++) {
801 rb->ringbuffer[i] = malloc(rb->blocksize * sizeof(char));
802 if (!rb->ringbuffer[i]) {
803 destroy_ringbuffer(rb);
804 return E_OUTOFMEMORY;
807 rb->dpts = malloc(rb->buffersize * sizeof(double));
808 if (!rb->dpts) {
809 destroy_ringbuffer(rb);
810 return E_OUTOFMEMORY;
812 rb->head = 0;
813 rb->tail = 0;
814 rb->count = 0;
815 rb->tStart = -1;
816 rb->pMutex = malloc(sizeof(CRITICAL_SECTION));
817 if (!rb->pMutex) {
818 destroy_ringbuffer(rb);
819 return E_OUTOFMEMORY;
821 InitializeCriticalSection(rb->pMutex);
822 return S_OK;
826 *---------------------------------------------------------------------------------------
828 * Tuner related methods (frequency, capabilities, etc
830 *---------------------------------------------------------------------------------------
833 * \brief returns string with name for givend PsysCon_* constant
835 * \param lPhysicalType constant from PhysicalConnectorType enumeration
837 * \return pointer to string with apropriate name
839 * \note
840 * Caller should not free returned pointer
842 static char *physcon2str(const long lPhysicalType)
844 int i;
845 for(i=0; tv_physcon_types[i].name; i++)
846 if(tv_physcon_types[i].type==lPhysicalType)
847 return tv_physcon_types[i].name;
848 return "Unknown";
852 * \brief converts MPlayer's chanlist to system country code.
854 * \param chanlist MPlayer's chanlist name
856 * \return system country code
858 * \remarks
859 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
860 * country (which is usually larger then MPlayer's one, so workaround will work fine).
862 * \todo
863 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
865 static int chanlist2country(char *chanlist)
867 int i;
868 for(i=0; tv_chanlist2country[i].chanlist_name; i++)
869 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
870 break;
871 return tv_chanlist2country[i].country_code;
875 * \brief loads specified resource from module and return pointer to it
877 * \param hDLL valid module desriptor
878 * \param index index of resource. resource with name "#<index>" will be loaded
880 * \return pointer to loader resource or NULL if error occured
882 static void *GetRC(HMODULE hDLL, int index)
884 char szRCDATA[10];
885 char szName[10];
886 HRSRC hRes;
887 HGLOBAL hTable;
889 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
890 snprintf(szName, 10, "#%d", index);
892 hRes = FindResource(hDLL, szName, szRCDATA);
893 if (!hRes) {
894 return NULL;
896 hTable = LoadResource(hDLL, hRes);
897 if (!hTable) {
898 return NULL;
900 return LockResource(hTable);
904 * \brief loads frequency table for given country from kstvtune.ax
906 * \param[in] nCountry - country code
907 * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
908 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
909 * \param[out] pnLen length of array
910 * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
912 * \return S_OK if success
913 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
914 * \return E_FAIL error occured during load
916 * \remarks
917 * - array must be freed by caller
918 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
920 static HRESULT load_freq_table(int nCountry, int nInputType,
921 long **pplFreqTable, int *pnLen,
922 int *pnFirst)
924 HMODULE hDLL;
925 long *plFreqTable;
926 TRCCountryList *pCountryList;
927 int i, index;
929 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%s)\n",nCountry,nInputType == TunerInputAntenna ? "broadcast" : "cable");
930 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
932 if (!pplFreqTable || !pnFirst || !pnLen)
933 return E_POINTER;
934 if (!nCountry)
935 return E_FAIL;
937 hDLL = LoadLibrary("kstvtune.ax");
938 if (!hDLL) {
939 return E_FAIL;
941 pCountryList = GetRC(hDLL, 9999);
942 if (!pCountryList) {
943 FreeLibrary(hDLL);
944 return E_FAIL;
946 for (i = 0; pCountryList[i].CountryCode != 0; i++)
947 if (pCountryList[i].CountryCode == nCountry)
948 break;
949 if (pCountryList[i].CountryCode == 0) {
950 FreeLibrary(hDLL);
951 return E_FAIL;
953 if (nInputType == TunerInputCable)
954 index = pCountryList[i].CableFreqTable;
955 else
956 index = pCountryList[i].BroadcastFreqTable;
958 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
959 if (!plFreqTable) {
960 FreeLibrary(hDLL);
961 return E_FAIL;
963 *pnFirst = plFreqTable[0];
964 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
965 *pplFreqTable = malloc((*pnLen) * sizeof(long));
966 if (!*pplFreqTable) {
967 FreeLibrary(hDLL);
968 return E_FAIL;
970 for (i = 0; i < *pnLen; i++) {
971 (*pplFreqTable)[i] = plFreqTable[i + 2];
972 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table #%d => (%ld)\n",i+*pnFirst,(*pplFreqTable)[i]);
974 FreeLibrary(hDLL);
975 return S_OK;
979 * \brief tunes to given frequency through IKsPropertySet call
981 * \param pTVTuner IAMTVTuner interface of capture device
982 * \param lFreq frequency to tune (in Hz)
984 * \return S_OK success
985 * \return apropriate error code otherwise
987 * \note
988 * Due to either bug in driver or error in following code calll to IKsProperty::Set
989 * in this methods always fail with error 0x8007007a.
991 * \todo test code on other machines and an error
993 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
995 HRESULT hr;
996 DWORD dwSupported = 0;
997 DWORD cbBytes = 0;
998 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
999 KSPROPERTY_TUNER_FREQUENCY_S frequency;
1000 IKsPropertySet *pKSProp;
1002 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
1004 memset(&mode_caps, 0, sizeof(mode_caps));
1005 memset(&frequency, 0, sizeof(frequency));
1007 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1008 if (FAILED(hr))
1009 return hr; //no IKsPropertySet interface
1011 mode_caps.Mode = AMTUNER_MODE_TV;
1012 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
1013 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
1014 if (FAILED(hr)) {
1015 OLE_RELEASE_SAFE(pKSProp);
1016 return hr;
1019 if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
1020 OLE_RELEASE_SAFE(pKSProp);
1021 return E_FAIL; //PROPSETID_TINER not supported
1024 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1025 KSPROPERTY_TUNER_MODE_CAPS,
1026 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
1027 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
1028 &mode_caps, sizeof(mode_caps), &cbBytes);
1030 frequency.Frequency = lFreq;
1032 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
1033 frequency.TuningFlags = KS_TUNER_TUNING_FINE;
1034 else
1035 frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
1037 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
1038 OLE_RELEASE_SAFE(pKSProp);
1039 return E_FAIL;
1042 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
1043 KSPROPERTY_TUNER_FREQUENCY,
1044 INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
1045 INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
1046 &frequency, sizeof(frequency));
1047 if (FAILED(hr)) {
1048 OLE_RELEASE_SAFE(pKSProp);
1049 return hr;
1052 OLE_RELEASE_SAFE(pKSProp);
1054 return S_OK;
1058 * \brief find channel with nearest frequency and set it
1060 * \param priv driver's private data
1061 * \param lFreq frequency in Hz
1063 * \return S_OK if success
1064 * \return E_FAIL if error occured
1066 static HRESULT set_nearest_freq(priv_t * priv, long lFreq)
1068 HRESULT hr;
1069 int i;
1070 long lFreqDiff=-1;
1071 int nChannel;
1072 TunerInputType tunerInput;
1073 long lInput;
1075 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called: %ld\n", lFreq);
1076 if(priv->freq_table_len == -1 && !priv->freq_table) {
1078 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
1079 if(FAILED(hr)){ //Falling back to 0
1080 lInput=0;
1083 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput);
1085 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME
1086 priv->freq_table_len=0;
1087 priv->freq_table=NULL;
1088 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to load frequency table from kstvtune.ax\n");
1089 return E_FAIL;
1091 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: loaded system (%s) frequency table for country id=%d (channels:%d).\n", tunerInput == TunerInputAntenna ? "broadcast" : "cable",
1092 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len);
1095 if (priv->freq_table_len <= 0)
1096 return E_FAIL;
1098 //FIXME: rewrite search algo
1099 nChannel = -1;
1100 for (i = 0; i < priv->freq_table_len; i++) {
1101 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) {
1102 nChannel = priv->first_channel + i;
1103 lFreqDiff = labs(lFreq - priv->freq_table[i]);
1105 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq #%d (%ld) => %d (%ld)\n",i+priv->first_channel,priv->freq_table[i], nChannel,lFreqDiff);
1107 if (nChannel == -1) {
1108 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find nearest channel in system frequency table\n");
1109 return E_FAIL;
1111 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: set_nearest_freq #%d (%ld)\n",nChannel,priv->freq_table[nChannel - priv->first_channel]);
1112 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel,
1113 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
1114 if (FAILED(hr)) {
1115 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to switch to nearest channel from system frequency table. Error:0x%x\n", (unsigned int)hr);
1116 return E_FAIL;
1118 return S_OK;
1122 * \brief setting frequency. decides whether use direct call/workaround
1124 * \param priv driver's private data
1125 * \param lFreq frequency in Hz
1127 * \return TVI_CONTROL_TRUE if success
1128 * \return TVI_CONTROL_FALSE if error occured
1130 * \todo check for freq boundary
1132 static int set_frequency(priv_t * priv, long lFreq)
1134 HRESULT hr;
1136 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called: %ld\n", lFreq);
1137 if (!priv->pTVTuner)
1138 return TVI_CONTROL_FALSE;
1139 if (priv->direct_setfreq_call) { //using direct call to set frequency
1140 hr = set_frequency_direct(priv->pTVTuner, lFreq);
1141 if (FAILED(hr)) {
1142 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to set frequency directly. OS built-in channels table will be used.\n");
1143 priv->direct_setfreq_call = 0;
1146 if (!priv->direct_setfreq_call) {
1147 hr = set_nearest_freq(priv, lFreq);
1149 if (FAILED(hr))
1150 return TVI_CONTROL_FALSE;
1151 #ifdef DEPRECATED
1152 priv->pGrabber->ClearBuffer(priv->pGrabber);
1153 #endif
1154 return TVI_CONTROL_TRUE;
1158 * \brief return current frequency from tuner (in Hz)
1160 * \param pTVTuner IAMTVTuner interface of tuner
1161 * \param plFreq address of variable that receives current frequency
1163 * \return S_OK success
1164 * \return E_POINTER pTVTuner==NULL || plFreq==NULL
1165 * \return apropriate error code otherwise
1167 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq)
1169 HRESULT hr;
1170 KSPROPERTY_TUNER_STATUS_S TunerStatus;
1171 DWORD cbBytes;
1172 IKsPropertySet *pKSProp;
1173 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n");
1175 if (!plFreq)
1176 return E_POINTER;
1178 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1179 if (FAILED(hr)) {
1180 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n");
1181 return hr;
1184 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1185 KSPROPERTY_TUNER_STATUS,
1186 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus),
1187 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus),
1188 &TunerStatus, sizeof(TunerStatus), &cbBytes);
1189 if (FAILED(hr)) {
1190 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n");
1191 return hr;
1193 *plFreq = TunerStatus.CurrentFrequency;
1194 return S_OK;
1198 * \brief gets current frequency
1200 * \param priv driver's private data structure
1201 * \param plFreq - pointer to long int to store frequency to (in Hz)
1203 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
1205 static int get_frequency(priv_t * priv, long *plFreq)
1207 HRESULT hr;
1209 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n");
1211 if (!plFreq || !priv->pTVTuner)
1212 return TVI_CONTROL_FALSE;
1214 if (priv->direct_getfreq_call) { //using direct call to get frequency
1215 hr = get_frequency_direct(priv->pTVTuner, plFreq);
1216 if (FAILED(hr)) {
1217 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Unable to get frequency directly. OS built-in channels table will be used.\n");
1218 priv->direct_getfreq_call = 0;
1221 if (!priv->direct_getfreq_call) {
1222 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq);
1223 if (FAILED(hr))
1224 return TVI_CONTROL_FALSE;
1227 return TVI_CONTROL_TRUE;
1231 * \brief get tuner capabilities
1233 * \param priv driver's private data
1235 static void get_capabilities(priv_t * priv)
1237 long lAvailableFormats;
1238 HRESULT hr;
1239 int i;
1240 long lInputPins, lOutputPins, lRelated, lPhysicalType;
1241 IEnumPins *pEnum;
1242 char tmp[200];
1243 IPin *pPin = 0;
1244 PIN_DIRECTION ThisPinDir;
1245 PIN_INFO pi;
1246 IAMAudioInputMixer *pIAMixer;
1248 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n");
1249 if (priv->pTVTuner) {
1251 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: supported norms:");
1252 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats,
1253 &lAvailableFormats);
1254 if (FAILED(hr))
1255 tv_available_norms_count = 0;
1256 else {
1257 for (i = 0; i < TV_NORMS_COUNT; i++) {
1258 if (lAvailableFormats & tv_norms[i].index) {
1259 tv_available_norms[tv_available_norms_count] = i;
1260 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1261 tv_available_norms_count + 1, tv_norms[i].name);
1262 tv_available_norms_count++;
1266 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1268 if (priv->pCrossbar) {
1269 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins,
1270 &lInputPins);
1272 tv_available_inputs = malloc(sizeof(long) * lInputPins);
1273 tv_available_inputs_count = 0;
1275 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available video inputs:");
1276 for (i = 0; i < lInputPins; i++) {
1277 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i,
1278 &lRelated, &lPhysicalType);
1280 if (lPhysicalType < 0x1000) {
1281 tv_available_inputs[tv_available_inputs_count++] = i;
1282 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1283 tv_available_inputs_count - 1,
1284 physcon2str(lPhysicalType));
1287 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1289 set_crossbar_input(priv, 0);
1292 if (priv->adev_index != -1) {
1293 hr = OLE_CALL_ARGS(priv->chains[1]->pCaptureFilter, EnumPins, &pEnum);
1294 if (FAILED(hr))
1295 return;
1296 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available audio inputs:");
1297 i = 0;
1298 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1299 memset(&pi, 0, sizeof(pi));
1300 memset(tmp, 0, 200);
1301 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1302 if (ThisPinDir == PINDIR_INPUT) {
1303 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1304 wtoa(pi.achName, tmp, 200);
1305 OLE_RELEASE_SAFE(pi.pFilter);
1306 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp);
1307 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1308 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer);
1309 if (SUCCEEDED(hr)) {
1310 if (i == priv->tv_param->audio_id) {
1311 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE);
1312 if(priv->tv_param->volume>0)
1313 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume);
1314 #if 0
1315 else
1316 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0);
1317 #endif
1318 mp_tmsg(MSGT_TV, MSGL_V, "(selected)");
1319 } else {
1320 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE);
1321 #if 0
1322 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0);
1323 #endif
1325 OLE_RELEASE_SAFE(pIAMixer);
1327 mp_msg(MSGT_TV, MSGL_V, ";");
1328 OLE_RELEASE_SAFE(pPin);
1329 i++;
1332 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1333 OLE_RELEASE_SAFE(pEnum);
1338 *---------------------------------------------------------------------------------------
1340 * Filter related methods
1342 *---------------------------------------------------------------------------------------
1345 * \brief building in graph audio/video capture chain
1347 * \param priv driver's private data
1348 * \param pCaptureFilter pointer to capture device's IBaseFilter interface
1349 * \param pbuf ringbuffer data structure
1350 * \param pmt media type for chain (AM_MEDIA_TYPE)
1352 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
1354 static HRESULT build_sub_graph(priv_t * priv, chain_t * chain, const GUID* ppin_category)
1356 HRESULT hr;
1357 int nFormatProbed = 0;
1359 IPin *pSGOut;
1360 IPin *pNRIn=NULL;
1362 IBaseFilter *pNR = NULL;
1364 hr=S_OK;
1366 //No supported formats
1367 if(!chain->arpmt[0])
1368 return E_FAIL;
1371 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
1372 (IUnknown *) chain->pCaptureFilter,
1373 PINDIR_OUTPUT, ppin_category,
1374 chain->majortype, FALSE, 0, &chain->pCapturePin);
1375 if(FAILED(hr)){
1376 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
1377 break;
1379 /* Addinf SampleGrabber filter for video stream */
1380 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &chain->pSGF);
1381 if(FAILED(hr)){
1382 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1383 break;
1385 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, chain->pSGF, L"Sample Grabber");
1386 if(FAILED(hr)){
1387 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1388 break;
1390 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &chain->pSGIn);
1391 if(FAILED(hr)){
1392 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr);
1393 break;
1395 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut);
1396 if(FAILED(hr)){
1397 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr);
1398 break;
1401 /* creating ringbuffer for video samples */
1402 chain->pCSGCB = CSampleGrabberCB_Create(chain->rbuf);
1403 if(!chain->pCSGCB){
1404 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY);
1405 break;
1408 /* initializing SampleGrabber filter */
1409 hr = OLE_QUERYINTERFACE(chain->pSGF, IID_ISampleGrabber, chain->pSG);
1410 if(FAILED(hr)){
1411 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1412 break;
1414 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data
1415 hr = OLE_CALL_ARGS(chain->pSG, SetCallback, (ISampleGrabberCB *) chain->pCSGCB, 0); //we want to receive sample
1417 if(FAILED(hr)){
1418 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1419 break;
1421 hr = OLE_CALL_ARGS(chain->pSG, SetOneShot, FALSE); //... for all frames
1422 if(FAILED(hr)){
1423 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1424 break;
1426 hr = OLE_CALL_ARGS(chain->pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber
1427 if(FAILED(hr)){
1428 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1429 break;
1432 if(priv->tv_param->normalize_audio_chunks && chain->type==audio){
1433 set_buffer_preference(20,(WAVEFORMATEX*)(chain->arpmt[nFormatProbed]->pbFormat),chain->pCapturePin,chain->pSGIn);
1436 for(nFormatProbed=0; chain->arpmt[nFormatProbed]; nFormatProbed++)
1438 DisplayMediaType("Probing format", chain->arpmt[nFormatProbed]);
1439 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, chain->arpmt[nFormatProbed]); //set desired mediatype
1440 if(FAILED(hr)){
1441 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1442 continue;
1444 /* connecting filters together: VideoCapture --> SampleGrabber */
1445 hr = OLE_CALL_ARGS(priv->pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1446 if(FAILED(hr)){
1447 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr);
1448 continue;
1450 break;
1453 if(!chain->arpmt[nFormatProbed])
1455 mp_msg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to negotiate media format\n");
1456 hr = E_FAIL;
1457 break;
1460 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, chain->pmt);
1461 if(FAILED(hr))
1463 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to get actual mediatype (Error:0x%x). Assuming equal to requested.\n", (unsigned int)hr);
1466 if(priv->tv_param->hidden_video_renderer){
1467 IEnumFilters* pEnum;
1468 IBaseFilter* pFilter;
1470 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)chain->pCapturePin,NULL,NULL);
1472 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
1473 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
1474 LPVIDEOWINDOW pVideoWindow;
1475 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
1476 if (SUCCEEDED(hr))
1478 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
1479 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
1480 OLE_RELEASE_SAFE(pVideoWindow);
1482 OLE_RELEASE_SAFE(pFilter);
1484 OLE_RELEASE_SAFE(pEnum);
1485 }else
1487 #if 0
1489 Code below is disabled, because terminating chain with NullRenderer leads to jerky video.
1490 Perhaps, this happens because NullRenderer filter discards each received
1491 frame while discarded frames causes live source filter to dramatically reduce frame rate.
1493 /* adding sink for video stream */
1494 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR);
1495 if(FAILED(hr)){
1496 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1497 break;
1499 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer");
1500 if(FAILED(hr)){
1501 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1502 break;
1504 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn);
1505 if(FAILED(hr)){
1506 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr);
1507 break;
1510 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection
1512 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){
1513 /* connecting filters together: SampleGrabber --> NullRenderer */
1514 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn);
1515 if(FAILED(hr)){
1516 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr);
1517 break;
1520 #endif
1523 hr = S_OK;
1524 } while(0);
1526 OLE_RELEASE_SAFE(pSGOut);
1527 OLE_RELEASE_SAFE(pNR);
1528 OLE_RELEASE_SAFE(pNRIn);
1530 return hr;
1534 * \brief configures crossbar for grabbing video stream from given input
1536 * \param priv driver's private data
1537 * \param input index of available video input to get data from
1539 * \return TVI_CONTROL_TRUE success
1540 * \return TVI_CONTROL_FALSE error
1542 static int set_crossbar_input(priv_t * priv, int input)
1544 HRESULT hr;
1545 int i, nVideoDecoder, nAudioDecoder;
1546 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins,
1547 lInputPins;
1549 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n");
1550 if (!priv->pCrossbar || input < 0
1551 || input >= tv_available_inputs_count)
1552 return TVI_CONTROL_FALSE;
1554 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins);
1556 lInput = tv_available_inputs[input];
1558 if (lInput < 0 || lInput >= lInputPins)
1559 return TVI_CONTROL_FALSE;
1561 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput,
1562 &lInputRelated, &lPhysicalType);
1564 nVideoDecoder = nAudioDecoder = -1;
1565 for (i = 0; i < lOutputPins; i++) {
1566 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i,
1567 &lRelated, &lPhysicalType);
1568 if (lPhysicalType == PhysConn_Video_VideoDecoder)
1569 nVideoDecoder = i;
1570 if (lPhysicalType == PhysConn_Audio_AudioDecoder)
1571 nAudioDecoder = i;
1573 if (nVideoDecoder >= 0) {
1574 //connecting given input with video decoder
1575 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput);
1576 if (hr != S_OK) {
1577 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to video decoder. Error:0x%x\n", (unsigned int)hr);
1578 return TVI_CONTROL_FALSE;
1581 if (nAudioDecoder >= 0 && lInputRelated >= 0) {
1582 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder,
1583 lInputRelated);
1584 if (hr != S_OK) {
1585 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to audio decoder. Error:0x%x\n", (unsigned int)hr);
1586 return TVI_CONTROL_FALSE;
1589 return TVI_CONTROL_TRUE;
1593 * \brief adjusts video control (hue,saturation,contrast,brightess)
1595 * \param priv driver's private data
1596 * \param control which control to adjust
1597 * \param value new value for control (0-100)
1599 * \return TVI_CONTROL_TRUE success
1600 * \return TVI_CONTROL_FALSE error
1602 static int set_control(priv_t * priv, int control, int value)
1604 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1605 HRESULT hr;
1607 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n");
1608 if (value < -100 || value > 100 || !priv->pVideoProcAmp)
1609 return TVI_CONTROL_FALSE;
1611 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1612 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1613 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual)
1614 return TVI_CONTROL_FALSE;
1616 lValue = lMin + (value + 100) * (lMax - lMin) / 200;
1618 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256
1620 if (lStepping > lMax) {
1621 mp_msg(MSGT_TV, MSGL_DBG3,
1622 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n",
1623 lStepping, lMax,control);
1624 lStepping = 1;
1626 lValue -= lValue % lStepping;
1627 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue,
1628 VideoProcAmp_Flags_Manual);
1629 if (FAILED(hr))
1630 return TVI_CONTROL_FALSE;
1632 return TVI_CONTROL_TRUE;
1636 * \brief get current value of video control (hue,saturation,contrast,brightess)
1638 * \param priv driver's private data
1639 * \param control which control to adjust
1640 * \param pvalue address of variable thar receives current value
1642 * \return TVI_CONTROL_TRUE success
1643 * \return TVI_CONTROL_FALSE error
1645 static int get_control(priv_t * priv, int control, int *pvalue)
1647 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1648 HRESULT hr;
1650 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n");
1651 if (!pvalue || !priv->pVideoProcAmp)
1652 return TVI_CONTROL_FALSE;
1654 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1655 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1656 if (FAILED(hr))
1657 return TVI_CONTROL_FALSE;
1658 if (lMin == lMax) {
1659 *pvalue = lMin;
1660 return TVI_CONTROL_TRUE;
1663 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags);
1664 if (FAILED(hr))
1665 return TVI_CONTROL_FALSE;
1667 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100;
1669 return TVI_CONTROL_TRUE;
1673 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps
1674 * \param fcc FourCC code for video format
1675 * \param width picture width
1676 * \param height pciture height
1677 * \param fps frames per second (required for bitrate calculation)
1679 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise
1681 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps)
1683 int i;
1684 AM_MEDIA_TYPE mt;
1685 VIDEOINFOHEADER vHdr;
1687 /* Check given fcc in lookup table*/
1688 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */;
1689 if(!img_fmt_list[i].fmt)
1690 return NULL;
1692 memset(&mt, 0, sizeof(AM_MEDIA_TYPE));
1693 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER));
1695 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader);
1696 vHdr.bmiHeader.biWidth = width;
1697 vHdr.bmiHeader.biHeight = height;
1698 //FIXME: is biPlanes required too?
1699 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes;
1700 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits;
1701 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression;
1702 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8;
1703 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps;
1705 mt.pbFormat = (char*)&vHdr;
1706 mt.cbFormat = sizeof(vHdr);
1708 mt.majortype = MEDIATYPE_Video;
1709 mt.subtype = *img_fmt_list[i].subtype;
1710 mt.formattype = FORMAT_VideoInfo;
1712 mt.bFixedSizeSamples = 1;
1713 mt.bTemporalCompression = 0;
1714 mt.lSampleSize = vHdr.bmiHeader.biSizeImage;
1716 return CreateMediaType(&mt);
1720 * \brief extracts fcc,width,height from AM_MEDIA_TYPE
1722 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1723 * \param pfcc address of variable that receives FourCC
1724 * \param pwidth address of variable that receives width
1725 * \param pheight address of variable that recevies height
1727 * \return 1 if data extracted successfully, 0 - otherwise
1729 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc,
1730 int *pwidth, int *pheight)
1732 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n");
1733 if (!pmt)
1734 return 0;
1735 if (!pmt->pbFormat)
1736 return 0;
1737 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0)
1738 return 0;
1739 if (pfcc)
1740 *pfcc = subtype2imgfmt(&(pmt->subtype));
1741 if (pwidth)
1742 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth;
1743 if (pheight)
1744 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight;
1745 return 1;
1749 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE
1751 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1752 * \param pfcc address of variable that receives samplerate
1753 * \param pwidth address of variable that receives number of bits per sample
1754 * \param pheight address of variable that recevies number of channels
1756 * \return 1 if data extracted successfully, 0 - otherwise
1758 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate,
1759 int *pbits, int *pchannels)
1761 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n");
1762 if (!pmt)
1763 return 0;
1764 if (!pmt->pbFormat)
1765 return 0;
1766 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1767 return 0;
1768 if (psamplerate)
1769 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec;
1770 if (pbits)
1771 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample;
1772 if (pchannels)
1773 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels;
1774 return 1;
1778 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels
1780 * \param pmt pointer to AM_MEDIA_TYPE for check
1781 * \param samplerate audio samplerate
1782 * \param bits bits per sample
1783 * \param channels number of audio channels
1785 * \return 1 if AM_MEDIA_TYPE compatible
1786 * \return 0 if not
1788 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate,
1789 int bits, int channels)
1791 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n");
1792 if (!pmt)
1793 return 0;
1794 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0)
1795 return 0;
1796 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0)
1797 return 0;
1798 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1799 return 0;
1800 if (!pmt->pbFormat)
1801 return 0;
1802 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate)
1803 return 0;
1804 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits)
1805 return 0;
1806 if (channels > 0
1807 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels)
1808 return 0;
1810 return 1;
1814 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height
1816 * \param pmt pointer to AM_MEDIA_TYPE for check
1817 * \param fcc FourCC (compression)
1818 * \param width width of picture
1819 * \param height height of picture
1821 * \return 1 if AM_MEDIA_TYPE compatible
1822 & \return 0 if not
1824 * \note
1825 * width and height are currently not used
1827 * \todo
1828 * add width/height check
1830 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width,
1831 int height)
1833 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n");
1834 if (!pmt)
1835 return 0;
1836 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0)
1837 return 0;
1838 if (subtype2imgfmt(&(pmt->subtype)) != fcc)
1839 return 0;
1840 return 1;
1844 * \brief converts DirectShow subtype to MPlayer's IMGFMT
1846 * \param subtype DirectShow subtype for video format
1848 * \return MPlayer's IMGFMT or 0 if error occured
1850 static int subtype2imgfmt(const GUID * subtype)
1852 int i;
1853 for (i = 0; img_fmt_list[i].fmt; i++) {
1854 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0)
1855 return img_fmt_list[i].fmt;
1857 return 0;
1861 * \brief prints filter name and it pins
1863 * \param pFilter - IBaseFilter to get data from
1865 * \return S_OK if success, error code otherwise
1867 static HRESULT show_filter_info(IBaseFilter * pFilter)
1869 char tmp[200];
1870 FILTER_INFO fi;
1871 LPENUMPINS pEnum = 0;
1872 IPin *pPin = 0;
1873 PIN_DIRECTION ThisPinDir;
1874 PIN_INFO pi;
1875 HRESULT hr;
1876 int i;
1878 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n");
1879 memset(&fi, 0, sizeof(fi));
1880 memset(tmp, 0, 200);
1882 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi);
1883 OLE_RELEASE_SAFE(fi.pGraph);
1884 wtoa(fi.achName, tmp, 200);
1885 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:",
1886 pFilter, tmp, fi.pGraph);
1887 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum);
1888 if (FAILED(hr))
1889 return hr;
1890 i = 0;
1891 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1892 memset(&pi, 0, sizeof(pi));
1893 memset(tmp, 0, 200);
1894 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1895 if (ThisPinDir == PINDIR_OUTPUT) {
1896 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1897 wtoa(pi.achName, tmp, 200);
1898 OLE_RELEASE_SAFE(pi.pFilter);
1899 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp);
1900 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1901 mp_msg(MSGT_TV, MSGL_DBG2, ";");
1902 OLE_RELEASE_SAFE(pPin);
1903 i++;
1906 mp_msg(MSGT_TV, MSGL_DBG2, "\n");
1907 OLE_RELEASE_SAFE(pEnum);
1908 return S_OK;
1912 * \brief gets device's frendly in ANSI encoding
1914 * \param pM IMoniker interface, got in enumeration process
1915 * \param category device category
1917 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise
1919 static int get_device_name(IMoniker * pM, char *pBuf, int nLen)
1921 HRESULT hr;
1922 VARIANT var;
1923 IPropertyBag *pPropBag;
1924 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag);
1925 if (FAILED(hr)) {
1926 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n");
1927 return TVI_CONTROL_FALSE;
1929 var.vt = VT_BSTR;
1930 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var,
1931 NULL);
1932 if (FAILED(hr)) {
1933 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var,
1934 NULL);
1936 OLE_RELEASE_SAFE(pPropBag);
1937 if (SUCCEEDED(hr)) {
1938 wtoa(var.bstrVal, pBuf, nLen);
1939 return TVI_CONTROL_TRUE;
1941 return TVI_CONTROL_FALSE;
1945 * \brief find capture device at given index
1947 * \param index device index to search for (-1 mean only print available)
1948 * \param category device category
1950 * \return IBaseFilter interface for capture device with given index
1952 * Sample values for category:
1953 * CLSID_VideoInputDeviceCategory - Video Capture Sources
1954 * CLSID_AudioInputDeviceCategory - Audio Capture Sources
1955 * See DirectShow SDK documentation for other possible values
1957 static IBaseFilter *find_capture_device(int index, REFCLSID category)
1959 IBaseFilter *pFilter = NULL;
1960 ICreateDevEnum *pDevEnum = NULL;
1961 IEnumMoniker *pClassEnum = NULL;
1962 IMoniker *pM;
1963 HRESULT hr;
1964 ULONG cFetched;
1965 int i;
1966 char tmp[DEVICE_NAME_MAX_LEN + 1];
1967 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL,
1968 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
1969 (void *) &pDevEnum);
1970 if (FAILED(hr)) {
1971 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n");
1972 return NULL;
1975 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0);
1976 OLE_RELEASE_SAFE(pDevEnum);
1977 if (FAILED(hr)) {
1978 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n");
1979 return NULL;
1981 if (hr == S_FALSE) {
1982 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n");
1983 return NULL;
1986 OLE_CALL(pClassEnum,Reset);
1987 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) {
1988 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE)
1989 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to get name for device #%d\n", i);
1990 else
1991 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Device #%d: %s\n", i, tmp);
1992 if (index != -1 && i == index) {
1993 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Using device #%d: %s\n", index, tmp);
1994 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter);
1995 if (FAILED(hr))
1996 pFilter = NULL;
1998 OLE_RELEASE_SAFE(pM);
2000 if (index != -1 && !pFilter) {
2001 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Device #%d not found\n",
2002 index);
2004 OLE_RELEASE_SAFE(pClassEnum);
2006 return pFilter;
2010 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps
2012 * \praram[in] chain chain data structure
2014 * \return S_OK success
2015 * \return E_POINTER one of parameters is NULL
2016 * \return E_FAIL required size of buffer is unknown for given media type
2017 * \return E_OUTOFMEMORY not enough memory
2018 * \return other error code from called methods
2020 * \remarks
2021 * last items of chain->arpmt and chain->arStreamCaps will be NULL
2023 static HRESULT get_available_formats_stream(chain_t *chain)
2025 AM_MEDIA_TYPE **arpmt;
2026 void **pBuf=NULL;
2028 HRESULT hr;
2029 int i, count, size;
2030 int done;
2032 mp_msg(MSGT_TV, MSGL_DBG4,
2033 "tvi_dshow: get_available_formats_stream called\n");
2035 if (!chain->pStreamConfig)
2036 return E_POINTER;
2038 hr=OLE_CALL_ARGS(chain->pStreamConfig, GetNumberOfCapabilities, &count, &size);
2039 if (FAILED(hr)) {
2040 mp_msg(MSGT_TV, MSGL_DBG4,
2041 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n");
2042 return hr;
2044 if (chain->type == video){
2045 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
2046 mp_msg(MSGT_TV, MSGL_DBG4,
2047 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2048 return E_FAIL;
2050 } else if (chain->type == audio){
2051 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) {
2052 mp_msg(MSGT_TV, MSGL_DBG4,
2053 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2054 return E_FAIL;
2056 } else {
2057 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_stream");
2058 return E_FAIL;
2060 done = 0;
2062 arpmt = malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2063 if (arpmt) {
2064 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2066 pBuf = malloc((count + 1) * sizeof(void *));
2067 if (pBuf) {
2068 memset(pBuf, 0, (count + 1) * sizeof(void *));
2070 for (i = 0; i < count; i++) {
2071 pBuf[i] = malloc(size);
2073 if (!pBuf[i])
2074 break;
2076 hr = OLE_CALL_ARGS(chain->pStreamConfig, GetStreamCaps, i,
2077 &(arpmt[i]), pBuf[i]);
2078 if (FAILED(hr))
2079 break;
2081 if (i == count) {
2082 chain->arpmt = arpmt;
2083 chain->arStreamCaps = pBuf;
2084 done = 1;
2088 if (!done) {
2089 for (i = 0; i < count; i++) {
2090 if (pBuf)
2091 free(pBuf[i]);
2092 if (arpmt && arpmt[i])
2093 DeleteMediaType(arpmt[i]);
2095 free(pBuf);
2096 free(arpmt);
2097 if (hr != S_OK) {
2098 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n");
2099 return hr;
2100 } else
2101 return E_OUTOFMEMORY;
2103 return S_OK;
2107 * \brief returns allocates an array and store available media formats for given pin type to it
2109 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder
2110 * \param chain chain data structure
2112 * \return S_OK success
2113 * \return E_POINTER one of given pointers is null
2114 * \return apropriate error code otherwise
2116 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder,
2117 chain_t *chain)
2119 IEnumMediaTypes *pEnum;
2120 int i, count, size;
2121 ULONG cFetched;
2122 AM_MEDIA_TYPE *pmt;
2123 HRESULT hr;
2124 void **pBuf;
2125 AM_MEDIA_TYPE **arpmt; //This will be real array
2126 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps;
2127 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps;
2128 int p1, p2, p3;
2130 mp_msg(MSGT_TV, MSGL_DBG4,
2131 "tvi_dshow: get_available_formats_pin called\n");
2132 if (!pBuilder || !chain->pCaptureFilter)
2133 return E_POINTER;
2135 if (!chain->pCapturePin)
2137 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2138 (IUnknown *) chain->pCaptureFilter,
2139 PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE,
2140 chain->majortype, FALSE, 0, &chain->pCapturePin);
2142 if (!chain->pCapturePin)
2143 return E_POINTER;
2145 if (chain->type == video) {
2146 size = sizeof(VIDEO_STREAM_CONFIG_CAPS);
2147 } else if (chain->type == audio) {
2148 size = sizeof(AUDIO_STREAM_CONFIG_CAPS);
2149 } else {
2150 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_pin");
2151 return E_FAIL;
2154 hr = OLE_CALL_ARGS(chain->pCapturePin, EnumMediaTypes, &pEnum);
2155 if (FAILED(hr)) {
2156 mp_msg(MSGT_TV, MSGL_DBG4,
2157 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n");
2158 return hr;
2160 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) {
2161 if (!pmt)
2162 break;
2164 OLE_CALL(pEnum,Reset);
2166 count = i;
2167 arpmt = malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2168 if (!arpmt)
2169 return E_OUTOFMEMORY;
2170 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2172 for (i = 0;
2173 i < count
2174 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK;
2175 i++);
2177 OLE_RELEASE_SAFE(pEnum);
2180 pBuf = malloc((count + 1) * sizeof(void *));
2181 if (!pBuf) {
2182 for (i = 0; i < count; i++)
2183 if (arpmt[i])
2184 DeleteMediaType(arpmt[i]);
2185 free(arpmt);
2186 return E_OUTOFMEMORY;
2188 memset(pBuf, 0, (count + 1) * sizeof(void *));
2190 for (i = 0; i < count; i++) {
2191 pBuf[i] = malloc(size);
2192 if (!pBuf[i])
2193 break;
2194 memset(pBuf[i], 0, size);
2196 if (chain->type == video) {
2197 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i];
2198 extract_video_format(arpmt[i], NULL, &p1, &p2);
2199 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx =
2201 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy =
2203 } else {
2204 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i];
2205 extract_audio_format(arpmt[i], &p1, &p2, &p3);
2206 pAudioCaps->MaximumSampleFrequency =
2207 pAudioCaps->MinimumSampleFrequency = p1;
2208 pAudioCaps->MaximumBitsPerSample =
2209 pAudioCaps->MinimumBitsPerSample = p2;
2210 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3;
2214 if (i != count) {
2215 for (i = 0; i < count; i++) {
2216 if (arpmt[i])
2217 DeleteMediaType(arpmt[i]);
2218 free(pBuf[i]);
2220 free(arpmt);
2221 free(pBuf);
2222 return E_OUTOFMEMORY;
2224 chain->arpmt = arpmt;
2225 chain->arStreamCaps = pBuf;
2227 return S_OK;
2231 *---------------------------------------------------------------------------------------
2233 * Public methods
2235 *---------------------------------------------------------------------------------------
2238 * \brief fills given buffer with audio data (usually one block)
2240 * \param priv driver's private data structure
2241 * \param buffer buffer to store data to
2242 * \param len buffer's size in bytes (usually one block size)
2244 * \return audio pts if audio present, 1 - otherwise
2246 static double grab_audio_frame(priv_t * priv, char *buffer, int len)
2248 int bytes = 0;
2249 int i;
2250 double pts;
2251 grabber_ringbuffer_t *rb = priv->chains[1]->rbuf;
2252 grabber_ringbuffer_t *vrb = priv->chains[0]->rbuf;
2254 if (!rb || !rb->ringbuffer)
2255 return 1;
2257 if(vrb && vrb->tStart<0){
2258 memset(buffer,0,len);
2259 return 0;
2261 if(vrb && rb->tStart<0)
2262 rb->tStart=vrb->tStart;
2264 if (len < rb->blocksize)
2265 bytes = len;
2266 else
2267 bytes = rb->blocksize;
2269 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n",
2270 rb->count, len);
2271 if(!rb->count){
2272 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2273 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2274 if(!rb->count){
2275 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2276 return 0;
2278 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2281 EnterCriticalSection(rb->pMutex);
2282 pts=rb->dpts[rb->head]-rb->tStart;
2283 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2284 rb->head = (rb->head + 1) % rb->buffersize;
2285 rb->count--;
2286 LeaveCriticalSection(rb->pMutex);
2287 return pts;
2291 * \brief returns audio frame size
2293 * \param priv driver's private data structure
2295 * \return audio block size if audio enabled and 1 - otherwise
2297 static int get_audio_framesize(priv_t * priv)
2299 if (!priv->chains[1]->rbuf)
2300 return 1; //no audio
2301 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->chains[1]->rbuf->blocksize);
2302 return priv->chains[1]->rbuf->blocksize;
2305 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp)
2307 if(!priv || !ptsp)
2308 return TVI_CONTROL_FALSE;
2310 //STUBS!!!
2311 ptsp->interlaced=0;
2312 ptsp->offset=256;
2314 ptsp->sampling_rate=27e6;
2315 ptsp->samples_per_line=720;
2317 ptsp->count[0]=16;
2318 ptsp->count[1]=16;
2319 //END STUBS!!!
2320 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]);
2322 mp_msg(MSGT_TV,MSGL_V,"vbi_get_props: sampling_rate=%d,offset:%d,samples_per_line: %d\n interlaced:%s, count=[%d,%d]\n",
2323 ptsp->sampling_rate,
2324 ptsp->offset,
2325 ptsp->samples_per_line,
2326 ptsp->interlaced?"Yes":"No",
2327 ptsp->count[0],
2328 ptsp->count[1]);
2330 return TVI_CONTROL_TRUE;
2333 static void vbi_grabber(priv_t* priv)
2335 grabber_ringbuffer_t *rb = priv->chains[2]->rbuf;
2336 int i;
2337 unsigned char* buf;
2338 if (!rb || !rb->ringbuffer)
2339 return;
2341 buf=calloc(1,rb->blocksize);
2342 for(i=0; i<23 && rb->count; i++){
2343 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize);
2344 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf);
2345 rb->head = (rb->head + 1) % rb->buffersize;
2346 rb->count--;
2348 free(buf);
2352 * \brief fills given buffer with video data (usually one frame)
2354 * \param priv driver's private data structure
2355 * \param buffer buffer to store data to
2356 * \param len buffer's size in bytes (usually one frame size)
2358 * \return frame size if video present, 0 - otherwise
2360 static double grab_video_frame(priv_t * priv, char *buffer, int len)
2362 int bytes = 0;
2363 int i;
2364 double pts;
2365 grabber_ringbuffer_t *rb = priv->chains[0]->rbuf;
2367 if (!rb || !rb->ringbuffer)
2368 return 1;
2369 if (len < rb->blocksize)
2370 bytes = len;
2371 else
2372 bytes = rb->blocksize;
2374 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n",
2375 rb->count, len);
2376 if(!rb->count){
2377 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2378 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2379 if(!rb->count){
2380 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2381 return 0;
2383 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2385 EnterCriticalSection(rb->pMutex);
2386 if(rb->tStart<0)
2387 rb->tStart=rb->dpts[rb->head];
2388 pts=rb->dpts[rb->head]-rb->tStart;
2389 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2390 rb->head = (rb->head + 1) % rb->buffersize;
2391 rb->count--;
2392 LeaveCriticalSection(rb->pMutex);
2394 vbi_grabber(priv);
2395 return pts;
2399 * \brief returns frame size
2401 * \param priv driver's private data structure
2403 * \return frame size if video present, 0 - otherwise
2405 static int get_video_framesize(priv_t * priv)
2407 // if(!priv->pmtVideo) return 1; //no video
2408 // return priv->pmtVideo->lSampleSize;
2409 if (!priv->chains[0]->rbuf)
2410 return 1; //no video
2411 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->chains[0]->rbuf->blocksize);
2412 return priv->chains[0]->rbuf->blocksize;
2416 * \brief calculate audio buffer size
2417 * \param video_buf_size size of video buffer in bytes
2418 * \param video_bitrate video bit rate
2419 * \param audio_bitrate audio bit rate
2420 * \return audio buffer isze in bytes
2422 * \remarks length of video buffer and resulted audio buffer calculated in
2423 * seconds will be the same.
2425 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate)
2427 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate);
2428 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n",
2429 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size);
2430 return audio_buf_size;
2434 * \brief common chain initialization routine
2435 * \param chain chain data structure
2437 * \note pCaptureFilter member should be initialized before call to this routine
2439 static HRESULT init_chain_common(ICaptureGraphBuilder2 *pBuilder, chain_t *chain)
2441 HRESULT hr;
2442 int i;
2444 if(!chain->pCaptureFilter)
2445 return E_POINTER;
2447 show_filter_info(chain->pCaptureFilter);
2449 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2450 (IUnknown *) chain->pCaptureFilter,
2451 PINDIR_OUTPUT, chain->pin_category,
2452 chain->majortype, FALSE, 0, &chain->pCapturePin);
2454 if (FAILED(hr)) {
2455 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
2456 return hr;
2459 hr = OLE_CALL_ARGS(pBuilder, FindInterface,
2460 chain->pin_category,
2461 chain->majortype,
2462 chain->pCaptureFilter,
2463 &IID_IAMStreamConfig,
2464 (void **) &(chain->pStreamConfig));
2465 if (FAILED(hr))
2466 chain->pStreamConfig = NULL;
2469 Getting available video formats (last pointer in array will be NULL)
2470 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as
2471 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default
2472 min/max values.
2474 hr = get_available_formats_stream(chain);
2475 if (FAILED(hr)) {
2476 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr);
2477 hr = get_available_formats_pin(pBuilder, chain);
2478 if(FAILED(hr)){
2479 return hr;
2482 chain->nFormatUsed = 0;
2484 //If argument to CreateMediaType is NULL then result will be NULL too.
2485 chain->pmt = CreateMediaType(chain->arpmt[0]);
2487 for (i = 0; chain->arpmt[i]; i++)
2488 DisplayMediaType("Available format", chain->arpmt[i]);
2490 return S_OK;
2493 * \brief build video stream chain in graph
2494 * \param priv private data structure
2496 * \return S_OK if chain was built successfully, apropriate error code otherwise
2498 static HRESULT build_video_chain(priv_t *priv)
2500 HRESULT hr;
2502 if(priv->chains[0]->rbuf)
2503 return S_OK;
2505 if (priv->chains[0]->pStreamConfig) {
2506 hr = OLE_CALL_ARGS(priv->chains[0]->pStreamConfig, SetFormat, priv->chains[0]->pmt);
2507 if (FAILED(hr)) {
2508 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select video format. Error:0x%x\n", (unsigned int)hr);
2512 priv->chains[0]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2513 if(!priv->chains[0]->rbuf)
2514 return E_OUTOFMEMORY;
2516 if (priv->tv_param->buffer_size >= 0) {
2517 priv->chains[0]->rbuf->buffersize = priv->tv_param->buffer_size;
2518 } else {
2519 priv->chains[0]->rbuf->buffersize = 16;
2522 priv->chains[0]->rbuf->buffersize *= 1024 * 1024;
2523 hr=build_sub_graph(priv, priv->chains[0], &PIN_CATEGORY_CAPTURE);
2524 if(FAILED(hr)){
2525 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build video chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2526 return hr;
2528 return S_OK;
2532 * \brief build audio stream chain in graph
2533 * \param priv private data structure
2535 * \return S_OK if chain was built successfully, apropriate error code otherwise
2537 static HRESULT build_audio_chain(priv_t *priv)
2539 HRESULT hr;
2541 if(priv->chains[1]->rbuf)
2542 return S_OK;
2544 if(priv->immediate_mode)
2545 return S_OK;
2547 if (priv->chains[1]->pStreamConfig) {
2548 hr = OLE_CALL_ARGS(priv->chains[1]->pStreamConfig, SetFormat,
2549 priv->chains[1]->pmt);
2550 if (FAILED(hr)) {
2551 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select audio format. Error:0x%x\n", (unsigned int)hr);
2555 if(priv->chains[1]->pmt){
2556 priv->chains[1]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2557 if(!priv->chains[1]->rbuf)
2558 return E_OUTOFMEMORY;
2560 /* let the audio buffer be the same size (in seconds) than video one */
2561 priv->chains[1]->rbuf->buffersize=audio_buf_size_from_video(
2562 priv->chains[0]->rbuf->buffersize,
2563 (((VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat)->dwBitRate),
2564 (((WAVEFORMATEX *) (priv->chains[1]->pmt->pbFormat))->nAvgBytesPerSec));
2566 hr=build_sub_graph(priv, priv->chains[1],&PIN_CATEGORY_CAPTURE);
2567 if(FAILED(hr)){
2568 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build audio chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2569 return 0;
2572 return S_OK;
2576 * \brief build VBI stream chain in graph
2577 * \param priv private data structure
2579 * \return S_OK if chain was built successfully, apropriate error code otherwise
2581 static HRESULT build_vbi_chain(priv_t *priv)
2583 HRESULT hr;
2585 if(priv->chains[2]->rbuf)
2586 return S_OK;
2588 if(priv->tv_param->teletext.device)
2590 priv->chains[2]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2591 if(!priv->chains[2]->rbuf)
2592 return E_OUTOFMEMORY;
2594 init_ringbuffer(priv->chains[2]->rbuf,24,priv->tsp.bufsize);
2596 hr=build_sub_graph(priv, priv->chains[2],&PIN_CATEGORY_VBI);
2597 if(FAILED(hr)){
2598 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build VBI chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2599 return 0;
2602 return S_OK;
2606 * \brief playback/capture real start
2608 * \param priv driver's private data structure
2610 * \return 1 if success, 0 - otherwise
2612 * TODO: move some code from init() here
2614 static int start(priv_t * priv)
2616 HRESULT hr;
2618 hr = build_video_chain(priv);
2619 if(FAILED(hr))
2620 return 0;
2622 hr = build_audio_chain(priv);
2623 if(FAILED(hr))
2624 return 0;
2626 hr = build_vbi_chain(priv);
2627 if(FAILED(hr))
2628 return 0;
2631 Graph is ready to capture. Starting graph.
2633 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2634 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n");
2635 usec_sleep(10000000);
2636 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n");
2638 if (!priv->pMediaControl) {
2639 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)E_POINTER);
2640 return 0;
2642 hr = OLE_CALL(priv->pMediaControl, Run);
2643 if (FAILED(hr)) {
2644 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to start graph! Error:0x%x\n", (unsigned int)hr);
2645 return 0;
2647 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n");
2648 priv->state = 1;
2650 return 1;
2654 * \brief driver initialization
2656 * \param priv driver's private data structure
2658 * \return 1 if success, 0 - otherwise
2660 static int init(priv_t * priv)
2662 HRESULT hr;
2663 int result = 0;
2664 long lInput, lTunerInput;
2665 IEnumFilters *pEnum;
2666 IBaseFilter *pFilter;
2667 IPin *pVPOutPin;
2668 int i;
2670 priv->state=0;
2672 CoInitialize(NULL);
2674 for(i=0; i<3;i++)
2675 priv->chains[i] = calloc(1, sizeof(chain_t));
2677 priv->chains[0]->type=video;
2678 priv->chains[0]->majortype=&MEDIATYPE_Video;
2679 priv->chains[0]->pin_category=&PIN_CATEGORY_CAPTURE;
2680 priv->chains[1]->type=audio;
2681 priv->chains[1]->majortype=&MEDIATYPE_Audio;
2682 priv->chains[1]->pin_category=&PIN_CATEGORY_CAPTURE;
2683 priv->chains[2]->type=vbi;
2684 priv->chains[2]->majortype=&MEDIATYPE_VBI;
2685 priv->chains[2]->pin_category=&PIN_CATEGORY_VBI;
2688 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL,
2689 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2690 (void **) &priv->pGraph);
2691 if(FAILED(hr)){
2692 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr);
2693 break;
2695 //Debug
2696 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2697 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister));
2700 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL,
2701 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2,
2702 (void **) &priv->pBuilder);
2703 if(FAILED(hr)){
2704 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr);
2705 break;
2708 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph);
2709 if(FAILED(hr)){
2710 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr);
2711 break;
2714 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n");
2715 priv->chains[0]->pCaptureFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory);
2716 if(!priv->chains[0]->pCaptureFilter){
2717 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find video capture device\n");
2718 break;
2720 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[0]->pCaptureFilter, NULL);
2721 if(FAILED(hr)){
2722 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2723 break;
2725 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n");
2726 if (priv->adev_index != -1) {
2727 priv->chains[1]->pCaptureFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices
2728 if(!priv->chains[1]->pCaptureFilter){
2729 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find audio capture device\n");
2730 break;
2733 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[1]->pCaptureFilter, NULL);
2734 if(FAILED(hr)){
2735 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2736 break;
2738 } else
2739 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[1]->pCaptureFilter);
2741 /* increase refrence counter for capture filter ad store pointer into vbi chain structure too */
2742 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[2]->pCaptureFilter);
2744 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp);
2745 if (FAILED(hr) && hr != E_NOINTERFACE)
2746 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr);
2748 if (hr != S_OK) {
2749 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Adjusting of brightness/hue/saturation/contrast is not supported by device\n");
2750 priv->pVideoProcAmp = NULL;
2753 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2754 &PIN_CATEGORY_CAPTURE,
2755 priv->chains[0]->majortype,
2756 priv->chains[0]->pCaptureFilter,
2757 &IID_IAMCrossbar, (void **) &(priv->pCrossbar));
2758 if (FAILED(hr)) {
2759 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Selection of capture source is not supported by device\n");
2760 priv->pCrossbar = NULL;
2763 if (priv->tv_param->amode >= 0) {
2764 IAMTVAudio *pTVAudio;
2765 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->chains[0]->pCaptureFilter,&IID_IAMTVAudio, (void *) &pTVAudio);
2766 if (hr == S_OK) {
2767 switch (priv->tv_param->amode) {
2768 case 0:
2769 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO);
2770 break;
2771 case 1:
2772 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO);
2773 break;
2774 case 2:
2775 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2776 AMTVAUDIO_MODE_LANG_A);
2777 break;
2778 case 3:
2779 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2780 AMTVAUDIO_MODE_LANG_B);
2781 break;
2783 OLE_RELEASE_SAFE(pTVAudio);
2784 if (FAILED(hr))
2785 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to set audio mode %d. Error:0x%x\n", priv->tv_param->amode,(unsigned int)hr);
2789 // Video chain initialization
2790 hr = init_chain_common(priv->pBuilder, priv->chains[0]);
2791 if(FAILED(hr))
2792 break;
2795 Audio chain initialization
2796 Since absent audio stream is not fatal,
2797 at least one NULL pointer should be kept in format arrays
2798 (to avoid another additional check everywhere for array presence).
2800 hr = init_chain_common(priv->pBuilder, priv->chains[1]);
2801 if(FAILED(hr))
2803 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize audio chain (Error:0x%x). Audio disabled\n", (unsigned long)hr);
2804 priv->chains[1]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2805 priv->chains[1]->arStreamCaps=calloc(1, sizeof(void*));
2809 VBI chain initialization
2810 Since absent VBI stream is not fatal,
2811 at least one NULL pointer should be kept in format arrays
2812 (to avoid another additional check everywhere for array presence).
2814 hr = init_chain_common(priv->pBuilder, priv->chains[2]);
2815 if(FAILED(hr))
2817 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize VBI chain (Error:0x%x). Teletext disabled\n", (unsigned long)hr);
2818 priv->chains[2]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2819 priv->chains[2]->arStreamCaps=calloc(1, sizeof(void*));
2822 if (!priv->chains[0]->pStreamConfig)
2823 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Changing video width/height is not supported by device.\n");
2825 if (!priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]
2826 || !extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
2827 &(priv->fcc), &(priv->width),
2828 &(priv->height))) {
2829 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse video format structure.\n");
2830 break;
2833 if (priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]) {
2834 if (!extract_audio_format(priv->chains[1]->pmt, &(priv->samplerate), NULL, NULL)) {
2835 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse audio format structure.\n");
2836 DisplayMediaType("audio format failed",priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]);
2837 break;
2841 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl);
2842 if(FAILED(hr)){
2843 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)hr);
2844 break;
2846 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2847 &PIN_CATEGORY_CAPTURE, NULL,
2848 priv->chains[0]->pCaptureFilter,
2849 &IID_IAMTVTuner, (void **) &(priv->pTVTuner));
2851 if (!priv->pTVTuner) {
2852 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr);
2855 // shows Tuner capabilities
2856 get_capabilities(priv);
2858 if (priv->pTVTuner) {
2859 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode,
2860 chanlist2country(priv->tv_param->chanlist));
2861 if(FAILED(hr)){
2862 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr);
2865 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV);
2866 if(FAILED(hr)){
2867 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr);
2868 break;
2871 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
2872 if(FAILED(hr)){
2873 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr);
2874 break;
2877 /* small hack */
2878 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna;
2880 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput);
2881 if(FAILED(hr)){
2882 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr);
2883 break;
2889 for VIVO cards we should check if preview pin is available on video capture device.
2890 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter.
2891 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph
2893 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
2894 (IUnknown *) priv->chains[0]->pCaptureFilter,
2895 PINDIR_OUTPUT,
2896 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE,
2897 0, (IPin **) & pVPOutPin);
2898 if (SUCCEEDED(hr)) {
2899 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin);
2900 OLE_RELEASE_SAFE(pVPOutPin);
2902 if (FAILED(hr)) {
2903 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to terminate VideoPort pin with any filter in graph. Error:0x%x\n", (unsigned int)hr);
2904 break;
2908 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
2909 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
2910 LPVIDEOWINDOW pVideoWindow;
2911 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
2912 if (SUCCEEDED(hr))
2914 if(priv->tv_param->hidden_vp_renderer){
2915 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
2916 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
2917 }else
2919 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter);
2921 OLE_RELEASE_SAFE(pVideoWindow);
2923 OLE_RELEASE_SAFE(pFilter);
2925 OLE_RELEASE_SAFE(pEnum);
2926 if(priv->tv_param->system_clock)
2928 LPREFERENCECLOCK rc;
2929 IBaseFilter* pBF;
2930 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL,
2931 CLSCTX_INPROC_SERVER, &IID_IReferenceClock,
2932 (void *) &rc);
2934 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF);
2935 OLE_CALL_ARGS(pBF,SetSyncSource,rc);
2937 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE)
2938 break;
2939 result = 1;
2940 } while(0);
2942 if (!result){
2943 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Directshow graph initialization failure.\n");
2944 uninit(priv);
2946 return result;
2950 * \brief chain uninitialization
2951 * \param chain chain data structure
2953 static void destroy_chain(chain_t *chain)
2955 int i;
2957 if(!chain)
2958 return;
2960 OLE_RELEASE_SAFE(chain->pStreamConfig);
2961 OLE_RELEASE_SAFE(chain->pCaptureFilter);
2962 OLE_RELEASE_SAFE(chain->pCSGCB);
2963 OLE_RELEASE_SAFE(chain->pCapturePin);
2964 OLE_RELEASE_SAFE(chain->pSGIn);
2965 OLE_RELEASE_SAFE(chain->pSG);
2966 OLE_RELEASE_SAFE(chain->pSGF);
2968 if (chain->pmt)
2969 DeleteMediaType(chain->pmt);
2971 if (chain->arpmt) {
2972 for (i = 0; chain->arpmt[i]; i++) {
2973 DeleteMediaType(chain->arpmt[i]);
2975 free(chain->arpmt);
2978 if (chain->arStreamCaps) {
2979 for (i = 0; chain->arStreamCaps[i]; i++) {
2980 free(chain->arStreamCaps[i]);
2982 free(chain->arStreamCaps);
2985 if (chain->rbuf) {
2986 destroy_ringbuffer(chain->rbuf);
2987 free(chain->rbuf);
2988 chain->rbuf = NULL;
2990 free(chain);
2993 * \brief driver uninitialization
2995 * \param priv driver's private data structure
2997 * \return always 1
2999 static int uninit(priv_t * priv)
3001 int i;
3002 if (!priv)
3003 return 1;
3004 //Debug
3005 if (priv->dwRegister) {
3006 RemoveFromRot(priv->dwRegister);
3008 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1);
3009 //stop audio grabber thread
3011 if (priv->state && priv->pMediaControl) {
3012 OLE_CALL(priv->pMediaControl, Stop);
3014 OLE_RELEASE_SAFE(priv->pMediaControl);
3015 priv->state = 0;
3017 if (priv->pGraph) {
3018 if (priv->chains[0]->pCaptureFilter)
3019 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[0]->pCaptureFilter);
3020 if (priv->chains[1]->pCaptureFilter)
3021 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[1]->pCaptureFilter);
3023 OLE_RELEASE_SAFE(priv->pCrossbar);
3024 OLE_RELEASE_SAFE(priv->pVideoProcAmp);
3025 OLE_RELEASE_SAFE(priv->pGraph);
3026 OLE_RELEASE_SAFE(priv->pBuilder);
3027 if(priv->freq_table){
3028 priv->freq_table_len=-1;
3029 free(priv->freq_table);
3030 priv->freq_table=NULL;
3033 for(i=0; i<3;i++)
3035 destroy_chain(priv->chains[i]);
3036 priv->chains[i] = NULL;
3038 CoUninitialize();
3039 return 1;
3043 * \brief driver pre-initialization
3045 * \param device string, containing device name in form "x[.y]", where x is video capture device
3046 * (default: 0, first available); y (if given) sets audio capture device
3048 * \return 1 if success,0 - otherwise
3050 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param)
3052 tvi_handle_t *h;
3053 priv_t *priv;
3054 int a;
3056 h = tv_new_handle(sizeof(priv_t), &functions);
3057 if (!h)
3058 return NULL;
3060 priv = h->priv;
3062 memset(priv, 0, sizeof(priv_t));
3063 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3064 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3065 priv->adev_index = -1;
3066 priv->freq_table_len=-1;
3067 priv->tv_param=tv_param;
3069 if (tv_param->device) {
3070 if (sscanf(tv_param->device, "%d", &a) == 1) {
3071 priv->dev_index = a;
3072 } else {
3073 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device parameter: %s\n", tv_param->device);
3074 tv_free_handle(h);
3075 return NULL;
3077 if (priv->dev_index < 0) {
3078 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device index: %d\n", a);
3079 tv_free_handle(h);
3080 return NULL;
3083 if (tv_param->adevice) {
3084 if (sscanf(tv_param->adevice, "%d", &a) == 1) {
3085 priv->adev_index = a;
3086 } else {
3087 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice parameter: %s\n", tv_param->adevice);
3088 tv_free_handle(h);
3089 return NULL;
3091 if (priv->dev_index < 0) {
3092 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice index: %d\n", a);
3093 tv_free_handle(h);
3094 return NULL;
3097 return h;
3101 * \brief driver's ioctl handler
3103 * \param priv driver's private data structure
3104 * \param cmd ioctl command
3105 * \param arg ioct command's parameter
3107 * \return TVI_CONTROL_TRUE if success
3108 * \return TVI_CONTROL_FALSE if failure
3109 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called
3111 static int control(priv_t * priv, int cmd, void *arg)
3113 switch (cmd) {
3114 /* need rewrite */
3115 case TVI_CONTROL_VID_SET_FORMAT:
3117 int fcc, i,j;
3118 void* tmp,*tmp2;
3119 int result = TVI_CONTROL_TRUE;
3121 if (priv->state)
3122 return TVI_CONTROL_FALSE;
3123 fcc = *(int *) arg;
3125 if(!priv->chains[0]->arpmt)
3126 return TVI_CONTROL_FALSE;
3127 for (i = 0; priv->chains[0]->arpmt[i]; i++)
3128 if (check_video_format
3129 (priv->chains[0]->arpmt[i], fcc, priv->width, priv->height))
3130 break;
3131 if (!priv->chains[0]->arpmt[i])
3133 int fps = 0;
3134 VIDEOINFOHEADER* Vhdr = NULL;
3135 AM_MEDIA_TYPE *pmt;
3137 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: will try also use undeclared video format: %dx%d, %s\n",priv->width, priv->height, vo_format_name(fcc));
3139 if (priv->chains[0]->arpmt[0])
3140 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->arpmt[0]->pbFormat;
3142 if(Vhdr && Vhdr->bmiHeader.biSizeImage)
3143 fps = Vhdr->dwBitRate / (8 * Vhdr->bmiHeader.biSizeImage);
3145 pmt=create_video_format(fcc, priv->width, priv->height, fps);
3146 if(!pmt)
3148 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to create AM_MEDIA_TYPE structure for given format\n");
3149 return TVI_CONTROL_FALSE;
3151 priv->chains[0]->arpmt=realloc(priv->chains[0]->arpmt, (i+2)*sizeof(AM_MEDIA_TYPE*));
3152 priv->chains[0]->arpmt[i+1] = NULL;
3153 priv->chains[0]->arpmt[i] = pmt;
3155 priv->chains[0]->arStreamCaps=realloc(priv->chains[0]->arStreamCaps, (i+2)*sizeof(void*));
3156 priv->chains[0]->arpmt[i+1] = NULL;
3158 result = TVI_CONTROL_FALSE;
3162 tmp=priv->chains[0]->arpmt[i];
3163 tmp2=priv->chains[0]->arStreamCaps[i];
3164 for(j=i; j>0; j--)
3166 priv->chains[0]->arpmt[j] = priv->chains[0]->arpmt[j-1];
3167 priv->chains[0]->arStreamCaps[j] = priv->chains[0]->arStreamCaps[j-1];
3169 priv->chains[0]->arpmt[0] = tmp;
3170 priv->chains[0]->arStreamCaps[0] = tmp2;
3172 priv->chains[0]->nFormatUsed = 0;
3174 if (priv->chains[0]->pmt)
3175 DeleteMediaType(priv->chains[0]->pmt);
3176 priv->chains[0]->pmt =
3177 CreateMediaType(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]);
3178 DisplayMediaType("VID_SET_FORMAT", priv->chains[0]->pmt);
3180 Setting width & height to preferred by driver values
3182 extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
3183 &(priv->fcc), &(priv->width),
3184 &(priv->height));
3185 return result;
3187 case TVI_CONTROL_VID_GET_FORMAT:
3189 if(!priv->chains[0]->pmt)
3190 return TVI_CONTROL_FALSE;
3192 Build video chain (for video format negotiation).
3193 If this was done before, routine will do nothing.
3195 build_video_chain(priv);
3196 DisplayMediaType("VID_GET_FORMAT", priv->chains[0]->pmt);
3197 if (priv->fcc) {
3198 *(int *) arg = priv->fcc;
3199 return TVI_CONTROL_TRUE;
3200 } else
3201 return TVI_CONTROL_FALSE;
3203 case TVI_CONTROL_VID_SET_WIDTH:
3205 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3206 VIDEOINFOHEADER *Vhdr;
3207 int width = *(int *) arg;
3208 if (priv->state)
3209 return TVI_CONTROL_FALSE;
3211 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3212 if (!pCaps)
3213 return TVI_CONTROL_FALSE;
3214 if (width < pCaps->MinOutputSize.cx
3215 || width > pCaps->MaxOutputSize.cx)
3216 return TVI_CONTROL_FALSE;
3218 if (width % pCaps->OutputGranularityX)
3219 return TVI_CONTROL_FALSE;
3221 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3222 return TVI_CONTROL_FALSE;
3223 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3224 Vhdr->bmiHeader.biWidth = width;
3225 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3226 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3227 Vhdr->bmiHeader.biHeight) >> 3;
3229 priv->width = width;
3231 return TVI_CONTROL_TRUE;
3233 case TVI_CONTROL_VID_GET_WIDTH:
3235 if (priv->width) {
3236 *(int *) arg = priv->width;
3237 return TVI_CONTROL_TRUE;
3238 } else
3239 return TVI_CONTROL_FALSE;
3241 case TVI_CONTROL_VID_CHK_WIDTH:
3243 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3244 int width = *(int *) arg;
3245 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3246 if (!pCaps)
3247 return TVI_CONTROL_FALSE;
3248 if (width < pCaps->MinOutputSize.cx
3249 || width > pCaps->MaxOutputSize.cx)
3250 return TVI_CONTROL_FALSE;
3252 if (width % pCaps->OutputGranularityX)
3253 return TVI_CONTROL_FALSE;
3254 return TVI_CONTROL_TRUE;
3256 case TVI_CONTROL_VID_SET_HEIGHT:
3258 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3259 VIDEOINFOHEADER *Vhdr;
3260 int height = *(int *) arg;
3261 if (priv->state)
3262 return TVI_CONTROL_FALSE;
3264 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3265 if (!pCaps)
3266 return TVI_CONTROL_FALSE;
3267 if (height < pCaps->MinOutputSize.cy
3268 || height > pCaps->MaxOutputSize.cy)
3269 return TVI_CONTROL_FALSE;
3271 if (height % pCaps->OutputGranularityY)
3272 return TVI_CONTROL_FALSE;
3274 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3275 return TVI_CONTROL_FALSE;
3276 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3278 if (Vhdr->bmiHeader.biHeight < 0)
3279 Vhdr->bmiHeader.biHeight = -height;
3280 else
3281 Vhdr->bmiHeader.biHeight = height;
3282 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3283 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3284 Vhdr->bmiHeader.biHeight) >> 3;
3286 priv->height = height;
3287 return TVI_CONTROL_TRUE;
3289 case TVI_CONTROL_VID_GET_HEIGHT:
3291 if (priv->height) {
3292 *(int *) arg = priv->height;
3293 return TVI_CONTROL_TRUE;
3294 } else
3295 return TVI_CONTROL_FALSE;
3297 case TVI_CONTROL_VID_CHK_HEIGHT:
3299 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3300 int height = *(int *) arg;
3301 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3302 if (!pCaps)
3303 return TVI_CONTROL_FALSE;
3304 if (height < pCaps->MinOutputSize.cy
3305 || height > pCaps->MaxOutputSize.cy)
3306 return TVI_CONTROL_FALSE;
3308 if (height % pCaps->OutputGranularityY)
3309 return TVI_CONTROL_FALSE;
3311 return TVI_CONTROL_TRUE;
3313 case TVI_CONTROL_IS_AUDIO:
3314 if (!priv->chains[1]->pmt)
3315 return TVI_CONTROL_FALSE;
3316 else
3317 return TVI_CONTROL_TRUE;
3318 case TVI_CONTROL_IS_VIDEO:
3319 return TVI_CONTROL_TRUE;
3320 case TVI_CONTROL_AUD_GET_FORMAT:
3322 *(int *) arg = AF_FORMAT_S16_LE;
3323 if (!priv->chains[1]->pmt)
3324 return TVI_CONTROL_FALSE;
3325 else
3326 return TVI_CONTROL_TRUE;
3328 case TVI_CONTROL_AUD_GET_CHANNELS:
3330 *(int *) arg = priv->channels;
3331 if (!priv->chains[1]->pmt)
3332 return TVI_CONTROL_FALSE;
3333 else
3334 return TVI_CONTROL_TRUE;
3336 case TVI_CONTROL_AUD_SET_SAMPLERATE:
3338 int i, samplerate;
3339 if (priv->state)
3340 return TVI_CONTROL_FALSE;
3341 if (!priv->chains[1]->arpmt[0])
3342 return TVI_CONTROL_FALSE;
3344 samplerate = *(int *) arg;
3346 for (i = 0; priv->chains[1]->arpmt[i]; i++)
3347 if (check_audio_format
3348 (priv->chains[1]->arpmt[i], samplerate, 16, priv->channels))
3349 break;
3350 if (!priv->chains[1]->arpmt[i]) {
3351 //request not found. failing back to first available
3352 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Samplerate %d is not supported by device. Failing back to first available.\n", samplerate);
3353 i = 0;
3355 if (priv->chains[1]->pmt)
3356 DeleteMediaType(priv->chains[1]->pmt);
3357 priv->chains[1]->pmt = CreateMediaType(priv->chains[1]->arpmt[i]);
3358 extract_audio_format(priv->chains[1]->arpmt[i], &(priv->samplerate),
3359 NULL, &(priv->channels));
3360 return TVI_CONTROL_TRUE;
3362 case TVI_CONTROL_AUD_GET_SAMPLERATE:
3364 *(int *) arg = priv->samplerate;
3365 if (!priv->samplerate)
3366 return TVI_CONTROL_FALSE;
3367 if (!priv->chains[1]->pmt)
3368 return TVI_CONTROL_FALSE;
3369 else
3370 return TVI_CONTROL_TRUE;
3372 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
3374 WAVEFORMATEX *pWF;
3375 if (!priv->chains[1]->pmt)
3376 return TVI_CONTROL_FALSE;
3377 if (!priv->chains[1]->pmt->pbFormat)
3378 return TVI_CONTROL_FALSE;
3379 pWF = (WAVEFORMATEX *) priv->chains[1]->pmt->pbFormat;
3380 *(int *) arg = pWF->wBitsPerSample / 8;
3381 return TVI_CONTROL_TRUE;
3383 case TVI_CONTROL_IS_TUNER:
3385 if (!priv->pTVTuner)
3386 return TVI_CONTROL_FALSE;
3388 return TVI_CONTROL_TRUE;
3390 case TVI_CONTROL_TUN_SET_NORM:
3392 IAMAnalogVideoDecoder *pVD;
3393 long lAnalogFormat;
3394 int i;
3395 HRESULT hr;
3397 i = *(int *) arg;
3398 i--;
3399 if (i < 0 || i >= tv_available_norms_count)
3400 return TVI_CONTROL_FALSE;
3401 lAnalogFormat = tv_norms[tv_available_norms[i]].index;
3403 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3404 if (hr != S_OK)
3405 return TVI_CONTROL_FALSE;
3406 hr = OLE_CALL_ARGS(pVD, put_TVFormat, lAnalogFormat);
3407 OLE_RELEASE_SAFE(pVD);
3408 if (FAILED(hr))
3409 return TVI_CONTROL_FALSE;
3410 else
3411 return TVI_CONTROL_TRUE;
3413 case TVI_CONTROL_TUN_GET_NORM:
3415 long lAnalogFormat;
3416 int i;
3417 HRESULT hr;
3418 IAMAnalogVideoDecoder *pVD;
3420 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3421 if (hr == S_OK) {
3422 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat);
3423 OLE_RELEASE_SAFE(pVD);
3426 if (FAILED(hr)) { //trying another method
3427 if (!priv->pTVTuner)
3428 return TVI_CONTROL_FALSE;
3429 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat);
3430 if (FAILED(hr))
3431 return TVI_CONTROL_FALSE;
3433 for (i = 0; i < tv_available_norms_count; i++) {
3434 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) {
3435 *(int *) arg = i + 1;
3436 return TVI_CONTROL_TRUE;
3439 return TVI_CONTROL_FALSE;
3441 case TVI_CONTROL_SPC_GET_NORMID:
3443 int i;
3444 if (!priv->pTVTuner)
3445 return TVI_CONTROL_FALSE;
3446 for (i = 0; i < tv_available_norms_count; i++) {
3447 if (!strcasecmp
3448 (tv_norms[tv_available_norms[i]].name, (char *) arg)) {
3449 *(int *) arg = i + 1;
3450 return TVI_CONTROL_TRUE;
3453 return TVI_CONTROL_FALSE;
3455 case TVI_CONTROL_SPC_SET_INPUT:
3457 return set_crossbar_input(priv, *(int *) arg);
3459 case TVI_CONTROL_TUN_GET_FREQ:
3461 unsigned long lFreq;
3462 int ret;
3463 if (!priv->pTVTuner)
3464 return TVI_CONTROL_FALSE;
3466 ret = get_frequency(priv, &lFreq);
3467 lFreq = lFreq / (1000000/16); //convert from Hz to 1/16 MHz units
3469 *(unsigned long *) arg = lFreq;
3470 return ret;
3472 case TVI_CONTROL_TUN_SET_FREQ:
3474 unsigned long nFreq = *(unsigned long *) arg;
3475 if (!priv->pTVTuner)
3476 return TVI_CONTROL_FALSE;
3477 //convert to Hz
3478 nFreq = (1000000/16) * nFreq; //convert from 1/16 MHz units to Hz
3479 return set_frequency(priv, nFreq);
3481 case TVI_CONTROL_VID_SET_HUE:
3482 return set_control(priv, VideoProcAmp_Hue, *(int *) arg);
3483 case TVI_CONTROL_VID_GET_HUE:
3484 return get_control(priv, VideoProcAmp_Hue, (int *) arg);
3485 case TVI_CONTROL_VID_SET_CONTRAST:
3486 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg);
3487 case TVI_CONTROL_VID_GET_CONTRAST:
3488 return get_control(priv, VideoProcAmp_Contrast, (int *) arg);
3489 case TVI_CONTROL_VID_SET_SATURATION:
3490 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg);
3491 case TVI_CONTROL_VID_GET_SATURATION:
3492 return get_control(priv, VideoProcAmp_Saturation, (int *) arg);
3493 case TVI_CONTROL_VID_SET_BRIGHTNESS:
3494 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg);
3495 case TVI_CONTROL_VID_GET_BRIGHTNESS:
3496 return get_control(priv, VideoProcAmp_Brightness, (int *) arg);
3498 case TVI_CONTROL_VID_GET_FPS:
3500 VIDEOINFOHEADER *Vhdr;
3501 if (!priv->chains[0]->pmt)
3502 return TVI_CONTROL_FALSE;
3503 if (!priv->chains[0]->pmt->pbFormat)
3504 return TVI_CONTROL_FALSE;
3505 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3506 *(float *) arg =
3507 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8);
3508 return TVI_CONTROL_TRUE;
3510 case TVI_CONTROL_IMMEDIATE:
3511 priv->immediate_mode = 1;
3512 return TVI_CONTROL_TRUE;
3513 case TVI_CONTROL_VBI_INIT:
3515 void* ptr;
3516 ptr=&(priv->tsp);
3517 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==VBI_CONTROL_TRUE)
3518 priv->priv_vbi=ptr;
3519 else
3520 priv->priv_vbi=NULL;
3521 return TVI_CONTROL_TRUE;
3523 case TVI_CONTROL_GET_VBI_PTR:
3524 *(void **)arg=priv->priv_vbi;
3525 return TVI_CONTROL_TRUE;
3527 return TVI_CONTROL_UNKNOWN;