cleanup: Silence compilation warnings on MinGW-w64
[mplayer.git] / stream / tvi_dshow.c
blob3e17d9cb99648ec122ca90c4c9d366492e0f14c2
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 static void set_buffer_preference(int nDiv, WAVEFORMATEX *pWF,
509 IPin *pOutPin, IPin *pInPin)
511 ALLOCATOR_PROPERTIES prop;
512 IAMBufferNegotiation* pBN;
513 HRESULT hr;
515 prop.cbAlign = -1;
516 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
517 if (!prop.cbBuffer)
518 prop.cbBuffer = 1;
519 prop.cbBuffer += pWF->nBlockAlign - 1;
520 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
521 prop.cbPrefix = -1;
522 prop.cBuffers = -1;
524 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
525 if(FAILED(hr))
526 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
527 else{
528 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
529 if(FAILED(hr))
530 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
531 OLE_RELEASE_SAFE(pBN);
533 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
534 if(FAILED(hr))
535 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
536 else{
537 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
538 if(FAILED(hr))
539 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
540 OLE_RELEASE_SAFE(pBN);
544 *---------------------------------------------------------------------------------------
546 * CSampleGrabberCD class. Used for receiving samples from DirectShow.
548 *---------------------------------------------------------------------------------------
550 /// CSampleGrabberCD destructor
551 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
553 free(This->lpVtbl);
554 free(This);
557 /// CSampleGrabberCD IUnknown interface methods implementation
558 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
559 This,
560 const GUID * riid,
561 void **ppvObject)
563 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
564 GUID *r;
565 unsigned int i = 0;
566 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
567 if (!ppvObject)
568 return E_POINTER;
569 for (r = me->interfaces;
570 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
571 if (!memcmp(r, riid, sizeof(*r))) {
572 OLE_CALL(This, AddRef);
573 *ppvObject = This;
574 return 0;
576 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
577 return E_NOINTERFACE;
580 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
582 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
583 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
584 me->refcount);
585 return ++(me->refcount);
588 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
590 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
591 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
592 This, me->refcount - 1);
593 if (--(me->refcount) == 0)
594 CSampleGrabberCB_Destroy(me);
595 return 0;
599 static HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB *This,
600 double SampleTime,
601 BYTE *pBuffer,
602 long lBufferLen)
604 CSampleGrabberCB *this = (CSampleGrabberCB *) This;
605 grabber_ringbuffer_t *rb = this->pbuf;
607 if (!lBufferLen)
608 return E_FAIL;
610 if (!rb->ringbuffer) {
611 rb->buffersize /= lBufferLen;
612 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
613 return E_FAIL;
615 mp_msg(MSGT_TV, MSGL_DBG4,
616 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
617 EnterCriticalSection(rb->pMutex);
618 if (rb->count >= rb->buffersize) {
619 rb->head = (rb->head + 1) % rb->buffersize;
620 rb->count--;
623 memcpy(rb->ringbuffer[rb->tail], pBuffer,
624 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
625 rb->dpts[rb->tail] = SampleTime;
626 rb->tail = (rb->tail + 1) % rb->buffersize;
627 rb->count++;
628 LeaveCriticalSection(rb->pMutex);
630 return S_OK;
633 /// wrapper. directshow does the same when BufferCB callback is requested
634 static HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB *This,
635 double SampleTime,
636 LPMEDIASAMPLE pSample)
638 char* buf;
639 long len;
640 long long tStart,tEnd;
641 HRESULT hr;
642 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
644 len=OLE_CALL(pSample,GetSize);
645 tStart=tEnd=0;
646 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
647 if(FAILED(hr)){
648 return hr;
650 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
651 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
652 if(FAILED(hr)){
653 return hr;
655 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
656 return hr;
660 /// main grabbing routine
661 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
662 pbuf)
664 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
665 if (!This)
666 return NULL;
668 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
669 if (!This->lpVtbl) {
670 CSampleGrabberCB_Destroy(This);
671 return NULL;
673 This->refcount = 1;
674 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
675 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
676 This->lpVtbl->Release = CSampleGrabberCB_Release;
677 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
678 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
680 This->interfaces[0] = IID_IUnknown;
681 This->interfaces[1] = IID_ISampleGrabberCB;
683 This->pbuf = pbuf;
685 return This;
689 *---------------------------------------------------------------------------------------
691 * ROT related methods (register, unregister)
693 *---------------------------------------------------------------------------------------
696 Registering graph in ROT. User will be able to connect to graph from GraphEdit.
698 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
700 IMoniker *pMoniker;
701 IRunningObjectTable *pROT;
702 WCHAR wsz[256];
703 HRESULT hr;
705 if (FAILED(GetRunningObjectTable(0, &pROT))) {
706 return E_FAIL;
708 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
709 GetCurrentProcessId());
710 hr = CreateItemMoniker(L"!", wsz, &pMoniker);
711 if (SUCCEEDED(hr)) {
712 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
713 pUnkGraph, pMoniker, pdwRegister);
714 OLE_RELEASE_SAFE(pMoniker);
716 OLE_RELEASE_SAFE(pROT);
717 return hr;
720 /// Unregistering graph in ROT
721 static void RemoveFromRot(DWORD dwRegister)
723 IRunningObjectTable *pROT;
724 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
725 OLE_CALL_ARGS(pROT, Revoke, dwRegister);
726 OLE_RELEASE_SAFE(pROT);
731 *---------------------------------------------------------------------------------------
733 * ringbuffer related methods (init, destroy)
735 *---------------------------------------------------------------------------------------
738 * \brief ringbuffer destroying routine
740 * \param rb pointer to empty (just allocated) ringbuffer structure
742 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
744 static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
746 int i;
748 if (!rb)
749 return;
751 if (rb->ringbuffer) {
752 for (i = 0; i < rb->buffersize; i++)
753 free(rb->ringbuffer[i]);
754 free(rb->ringbuffer);
755 rb->ringbuffer = NULL;
757 free(rb->dpts);
758 rb->dpts = NULL;
759 if (rb->pMutex) {
760 DeleteCriticalSection(rb->pMutex);
761 free(rb->pMutex);
762 rb->pMutex = NULL;
765 rb->blocksize = 0;
766 rb->buffersize = 0;
767 rb->head = 0;
768 rb->tail = 0;
769 rb->count = 0;
773 * \brief ringbuffer initialization
775 * \param rb pointer to empty (just allocated) ringbuffer structure
776 * \param buffersize size of buffer in blocks
777 * \param blocksize size of buffer's block
779 * \return S_OK if success
780 * \return E_OUTOFMEMORY not enough memory
782 * \note routine does not allocates memory for grabber_rinbuffer_s structure
784 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
785 int blocksize)
787 int i;
789 if (!rb)
790 return E_OUTOFMEMORY;
792 rb->buffersize = buffersize < 2 ? 2 : buffersize;
793 rb->blocksize = blocksize;
795 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
796 rb->buffersize, rb->blocksize);
798 rb->ringbuffer = malloc(rb->buffersize * sizeof(char *));
799 if (!rb)
800 return E_POINTER;
801 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
803 for (i = 0; i < rb->buffersize; i++) {
804 rb->ringbuffer[i] = malloc(rb->blocksize * sizeof(char));
805 if (!rb->ringbuffer[i]) {
806 destroy_ringbuffer(rb);
807 return E_OUTOFMEMORY;
810 rb->dpts = malloc(rb->buffersize * sizeof(double));
811 if (!rb->dpts) {
812 destroy_ringbuffer(rb);
813 return E_OUTOFMEMORY;
815 rb->head = 0;
816 rb->tail = 0;
817 rb->count = 0;
818 rb->tStart = -1;
819 rb->pMutex = malloc(sizeof(CRITICAL_SECTION));
820 if (!rb->pMutex) {
821 destroy_ringbuffer(rb);
822 return E_OUTOFMEMORY;
824 InitializeCriticalSection(rb->pMutex);
825 return S_OK;
829 *---------------------------------------------------------------------------------------
831 * Tuner related methods (frequency, capabilities, etc
833 *---------------------------------------------------------------------------------------
836 * \brief returns string with name for givend PsysCon_* constant
838 * \param lPhysicalType constant from PhysicalConnectorType enumeration
840 * \return pointer to string with apropriate name
842 * \note
843 * Caller should not free returned pointer
845 static char *physcon2str(const long lPhysicalType)
847 int i;
848 for(i=0; tv_physcon_types[i].name; i++)
849 if(tv_physcon_types[i].type==lPhysicalType)
850 return tv_physcon_types[i].name;
851 return "Unknown";
855 * \brief converts MPlayer's chanlist to system country code.
857 * \param chanlist MPlayer's chanlist name
859 * \return system country code
861 * \remarks
862 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
863 * country (which is usually larger then MPlayer's one, so workaround will work fine).
865 * \todo
866 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
868 static int chanlist2country(char *chanlist)
870 int i;
871 for(i=0; tv_chanlist2country[i].chanlist_name; i++)
872 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
873 break;
874 return tv_chanlist2country[i].country_code;
878 * \brief loads specified resource from module and return pointer to it
880 * \param hDLL valid module desriptor
881 * \param index index of resource. resource with name "#<index>" will be loaded
883 * \return pointer to loader resource or NULL if error occured
885 static void *GetRC(HMODULE hDLL, int index)
887 char szRCDATA[10];
888 char szName[10];
889 HRSRC hRes;
890 HGLOBAL hTable;
892 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
893 snprintf(szName, 10, "#%d", index);
895 hRes = FindResource(hDLL, szName, szRCDATA);
896 if (!hRes) {
897 return NULL;
899 hTable = LoadResource(hDLL, hRes);
900 if (!hTable) {
901 return NULL;
903 return LockResource(hTable);
907 * \brief loads frequency table for given country from kstvtune.ax
909 * \param[in] nCountry - country code
910 * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
911 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
912 * \param[out] pnLen length of array
913 * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
915 * \return S_OK if success
916 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
917 * \return E_FAIL error occured during load
919 * \remarks
920 * - array must be freed by caller
921 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
923 static HRESULT load_freq_table(int nCountry, int nInputType,
924 long **pplFreqTable, int *pnLen,
925 int *pnFirst)
927 HMODULE hDLL;
928 long *plFreqTable;
929 TRCCountryList *pCountryList;
930 int i, index;
932 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%s)\n",nCountry,nInputType == TunerInputAntenna ? "broadcast" : "cable");
933 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
935 if (!pplFreqTable || !pnFirst || !pnLen)
936 return E_POINTER;
937 if (!nCountry)
938 return E_FAIL;
940 hDLL = LoadLibrary("kstvtune.ax");
941 if (!hDLL) {
942 return E_FAIL;
944 pCountryList = GetRC(hDLL, 9999);
945 if (!pCountryList) {
946 FreeLibrary(hDLL);
947 return E_FAIL;
949 for (i = 0; pCountryList[i].CountryCode != 0; i++)
950 if (pCountryList[i].CountryCode == nCountry)
951 break;
952 if (pCountryList[i].CountryCode == 0) {
953 FreeLibrary(hDLL);
954 return E_FAIL;
956 if (nInputType == TunerInputCable)
957 index = pCountryList[i].CableFreqTable;
958 else
959 index = pCountryList[i].BroadcastFreqTable;
961 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
962 if (!plFreqTable) {
963 FreeLibrary(hDLL);
964 return E_FAIL;
966 *pnFirst = plFreqTable[0];
967 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
968 *pplFreqTable = malloc((*pnLen) * sizeof(long));
969 if (!*pplFreqTable) {
970 FreeLibrary(hDLL);
971 return E_FAIL;
973 for (i = 0; i < *pnLen; i++) {
974 (*pplFreqTable)[i] = plFreqTable[i + 2];
975 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table #%d => (%ld)\n",i+*pnFirst,(*pplFreqTable)[i]);
977 FreeLibrary(hDLL);
978 return S_OK;
982 * \brief tunes to given frequency through IKsPropertySet call
984 * \param pTVTuner IAMTVTuner interface of capture device
985 * \param lFreq frequency to tune (in Hz)
987 * \return S_OK success
988 * \return apropriate error code otherwise
990 * \note
991 * Due to either bug in driver or error in following code calll to IKsProperty::Set
992 * in this methods always fail with error 0x8007007a.
994 * \todo test code on other machines and an error
996 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
998 HRESULT hr;
999 DWORD dwSupported = 0;
1000 DWORD cbBytes = 0;
1001 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
1002 KSPROPERTY_TUNER_FREQUENCY_S frequency;
1003 IKsPropertySet *pKSProp;
1005 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
1007 memset(&mode_caps, 0, sizeof(mode_caps));
1008 memset(&frequency, 0, sizeof(frequency));
1010 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1011 if (FAILED(hr))
1012 return hr; //no IKsPropertySet interface
1014 mode_caps.Mode = AMTUNER_MODE_TV;
1015 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
1016 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
1017 if (FAILED(hr)) {
1018 OLE_RELEASE_SAFE(pKSProp);
1019 return hr;
1022 if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
1023 OLE_RELEASE_SAFE(pKSProp);
1024 return E_FAIL; //PROPSETID_TINER not supported
1027 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1028 KSPROPERTY_TUNER_MODE_CAPS,
1029 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
1030 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
1031 &mode_caps, sizeof(mode_caps), &cbBytes);
1033 frequency.Frequency = lFreq;
1035 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
1036 frequency.TuningFlags = KS_TUNER_TUNING_FINE;
1037 else
1038 frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
1040 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
1041 OLE_RELEASE_SAFE(pKSProp);
1042 return E_FAIL;
1045 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
1046 KSPROPERTY_TUNER_FREQUENCY,
1047 INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
1048 INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
1049 &frequency, sizeof(frequency));
1050 if (FAILED(hr)) {
1051 OLE_RELEASE_SAFE(pKSProp);
1052 return hr;
1055 OLE_RELEASE_SAFE(pKSProp);
1057 return S_OK;
1061 * \brief find channel with nearest frequency and set it
1063 * \param priv driver's private data
1064 * \param lFreq frequency in Hz
1066 * \return S_OK if success
1067 * \return E_FAIL if error occured
1069 static HRESULT set_nearest_freq(priv_t * priv, long lFreq)
1071 HRESULT hr;
1072 int i;
1073 long lFreqDiff=-1;
1074 int nChannel;
1075 TunerInputType tunerInput;
1076 long lInput;
1078 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called: %ld\n", lFreq);
1079 if(priv->freq_table_len == -1 && !priv->freq_table) {
1081 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
1082 if(FAILED(hr)){ //Falling back to 0
1083 lInput=0;
1086 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput);
1088 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME
1089 priv->freq_table_len=0;
1090 priv->freq_table=NULL;
1091 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to load frequency table from kstvtune.ax\n");
1092 return E_FAIL;
1094 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: loaded system (%s) frequency table for country id=%d (channels:%d).\n", tunerInput == TunerInputAntenna ? "broadcast" : "cable",
1095 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len);
1098 if (priv->freq_table_len <= 0)
1099 return E_FAIL;
1101 //FIXME: rewrite search algo
1102 nChannel = -1;
1103 for (i = 0; i < priv->freq_table_len; i++) {
1104 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) {
1105 nChannel = priv->first_channel + i;
1106 lFreqDiff = labs(lFreq - priv->freq_table[i]);
1108 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);
1110 if (nChannel == -1) {
1111 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find nearest channel in system frequency table\n");
1112 return E_FAIL;
1114 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: set_nearest_freq #%d (%ld)\n",nChannel,priv->freq_table[nChannel - priv->first_channel]);
1115 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel,
1116 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
1117 if (FAILED(hr)) {
1118 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);
1119 return E_FAIL;
1121 return S_OK;
1125 * \brief setting frequency. decides whether use direct call/workaround
1127 * \param priv driver's private data
1128 * \param lFreq frequency in Hz
1130 * \return TVI_CONTROL_TRUE if success
1131 * \return TVI_CONTROL_FALSE if error occured
1133 * \todo check for freq boundary
1135 static int set_frequency(priv_t * priv, long lFreq)
1137 HRESULT hr;
1139 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called: %ld\n", lFreq);
1140 if (!priv->pTVTuner)
1141 return TVI_CONTROL_FALSE;
1142 if (priv->direct_setfreq_call) { //using direct call to set frequency
1143 hr = set_frequency_direct(priv->pTVTuner, lFreq);
1144 if (FAILED(hr)) {
1145 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to set frequency directly. OS built-in channels table will be used.\n");
1146 priv->direct_setfreq_call = 0;
1149 if (!priv->direct_setfreq_call) {
1150 hr = set_nearest_freq(priv, lFreq);
1152 if (FAILED(hr))
1153 return TVI_CONTROL_FALSE;
1154 #ifdef DEPRECATED
1155 priv->pGrabber->ClearBuffer(priv->pGrabber);
1156 #endif
1157 return TVI_CONTROL_TRUE;
1161 * \brief return current frequency from tuner (in Hz)
1163 * \param pTVTuner IAMTVTuner interface of tuner
1164 * \param plFreq address of variable that receives current frequency
1166 * \return S_OK success
1167 * \return E_POINTER pTVTuner==NULL || plFreq==NULL
1168 * \return apropriate error code otherwise
1170 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq)
1172 HRESULT hr;
1173 KSPROPERTY_TUNER_STATUS_S TunerStatus;
1174 DWORD cbBytes;
1175 IKsPropertySet *pKSProp;
1176 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n");
1178 if (!plFreq)
1179 return E_POINTER;
1181 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1182 if (FAILED(hr)) {
1183 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n");
1184 return hr;
1187 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1188 KSPROPERTY_TUNER_STATUS,
1189 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus),
1190 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus),
1191 &TunerStatus, sizeof(TunerStatus), &cbBytes);
1192 if (FAILED(hr)) {
1193 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n");
1194 return hr;
1196 *plFreq = TunerStatus.CurrentFrequency;
1197 return S_OK;
1201 * \brief gets current frequency
1203 * \param priv driver's private data structure
1204 * \param plFreq - pointer to long int to store frequency to (in Hz)
1206 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
1208 static int get_frequency(priv_t * priv, long *plFreq)
1210 HRESULT hr;
1212 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n");
1214 if (!plFreq || !priv->pTVTuner)
1215 return TVI_CONTROL_FALSE;
1217 if (priv->direct_getfreq_call) { //using direct call to get frequency
1218 hr = get_frequency_direct(priv->pTVTuner, plFreq);
1219 if (FAILED(hr)) {
1220 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Unable to get frequency directly. OS built-in channels table will be used.\n");
1221 priv->direct_getfreq_call = 0;
1224 if (!priv->direct_getfreq_call) {
1225 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq);
1226 if (FAILED(hr))
1227 return TVI_CONTROL_FALSE;
1230 return TVI_CONTROL_TRUE;
1234 * \brief get tuner capabilities
1236 * \param priv driver's private data
1238 static void get_capabilities(priv_t * priv)
1240 long lAvailableFormats;
1241 HRESULT hr;
1242 int i;
1243 long lInputPins, lOutputPins, lRelated, lPhysicalType;
1244 IEnumPins *pEnum;
1245 char tmp[200];
1246 IPin *pPin = 0;
1247 PIN_DIRECTION ThisPinDir;
1248 PIN_INFO pi;
1249 IAMAudioInputMixer *pIAMixer;
1251 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n");
1252 if (priv->pTVTuner) {
1254 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: supported norms:");
1255 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats,
1256 &lAvailableFormats);
1257 if (FAILED(hr))
1258 tv_available_norms_count = 0;
1259 else {
1260 for (i = 0; i < TV_NORMS_COUNT; i++) {
1261 if (lAvailableFormats & tv_norms[i].index) {
1262 tv_available_norms[tv_available_norms_count] = i;
1263 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1264 tv_available_norms_count + 1, tv_norms[i].name);
1265 tv_available_norms_count++;
1269 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1271 if (priv->pCrossbar) {
1272 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins,
1273 &lInputPins);
1275 tv_available_inputs = malloc(sizeof(long) * lInputPins);
1276 tv_available_inputs_count = 0;
1278 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available video inputs:");
1279 for (i = 0; i < lInputPins; i++) {
1280 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i,
1281 &lRelated, &lPhysicalType);
1283 if (lPhysicalType < 0x1000) {
1284 tv_available_inputs[tv_available_inputs_count++] = i;
1285 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1286 tv_available_inputs_count - 1,
1287 physcon2str(lPhysicalType));
1290 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1292 set_crossbar_input(priv, 0);
1295 if (priv->adev_index != -1) {
1296 hr = OLE_CALL_ARGS(priv->chains[1]->pCaptureFilter, EnumPins, &pEnum);
1297 if (FAILED(hr))
1298 return;
1299 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available audio inputs:");
1300 i = 0;
1301 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1302 memset(&pi, 0, sizeof(pi));
1303 memset(tmp, 0, 200);
1304 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1305 if (ThisPinDir == PINDIR_INPUT) {
1306 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1307 wtoa(pi.achName, tmp, 200);
1308 OLE_RELEASE_SAFE(pi.pFilter);
1309 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp);
1310 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1311 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer);
1312 if (SUCCEEDED(hr)) {
1313 if (i == priv->tv_param->audio_id) {
1314 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE);
1315 if(priv->tv_param->volume>0)
1316 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume);
1317 #if 0
1318 else
1319 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0);
1320 #endif
1321 mp_tmsg(MSGT_TV, MSGL_V, "(selected)");
1322 } else {
1323 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE);
1324 #if 0
1325 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0);
1326 #endif
1328 OLE_RELEASE_SAFE(pIAMixer);
1330 mp_msg(MSGT_TV, MSGL_V, ";");
1331 OLE_RELEASE_SAFE(pPin);
1332 i++;
1335 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1336 OLE_RELEASE_SAFE(pEnum);
1341 *---------------------------------------------------------------------------------------
1343 * Filter related methods
1345 *---------------------------------------------------------------------------------------
1348 * \brief building in graph audio/video capture chain
1350 * \param priv driver's private data
1351 * \param pCaptureFilter pointer to capture device's IBaseFilter interface
1352 * \param pbuf ringbuffer data structure
1353 * \param pmt media type for chain (AM_MEDIA_TYPE)
1355 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
1357 static HRESULT build_sub_graph(priv_t * priv, chain_t * chain, const GUID* ppin_category)
1359 HRESULT hr;
1360 int nFormatProbed = 0;
1362 IPin *pSGOut;
1363 IPin *pNRIn=NULL;
1365 IBaseFilter *pNR = NULL;
1367 hr=S_OK;
1369 //No supported formats
1370 if(!chain->arpmt[0])
1371 return E_FAIL;
1374 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
1375 (IUnknown *) chain->pCaptureFilter,
1376 PINDIR_OUTPUT, ppin_category,
1377 chain->majortype, FALSE, 0, &chain->pCapturePin);
1378 if(FAILED(hr)){
1379 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
1380 break;
1382 /* Addinf SampleGrabber filter for video stream */
1383 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &chain->pSGF);
1384 if(FAILED(hr)){
1385 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1386 break;
1388 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, chain->pSGF, L"Sample Grabber");
1389 if(FAILED(hr)){
1390 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1391 break;
1393 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &chain->pSGIn);
1394 if(FAILED(hr)){
1395 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr);
1396 break;
1398 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut);
1399 if(FAILED(hr)){
1400 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr);
1401 break;
1404 /* creating ringbuffer for video samples */
1405 chain->pCSGCB = CSampleGrabberCB_Create(chain->rbuf);
1406 if(!chain->pCSGCB){
1407 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY);
1408 break;
1411 /* initializing SampleGrabber filter */
1412 hr = OLE_QUERYINTERFACE(chain->pSGF, IID_ISampleGrabber, chain->pSG);
1413 if(FAILED(hr)){
1414 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1415 break;
1417 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data
1418 hr = OLE_CALL_ARGS(chain->pSG, SetCallback, (ISampleGrabberCB *) chain->pCSGCB, 0); //we want to receive sample
1420 if(FAILED(hr)){
1421 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1422 break;
1424 hr = OLE_CALL_ARGS(chain->pSG, SetOneShot, FALSE); //... for all frames
1425 if(FAILED(hr)){
1426 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1427 break;
1429 hr = OLE_CALL_ARGS(chain->pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber
1430 if(FAILED(hr)){
1431 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1432 break;
1435 if(priv->tv_param->normalize_audio_chunks && chain->type==audio){
1436 set_buffer_preference(20,(WAVEFORMATEX*)(chain->arpmt[nFormatProbed]->pbFormat),chain->pCapturePin,chain->pSGIn);
1439 for(nFormatProbed=0; chain->arpmt[nFormatProbed]; nFormatProbed++)
1441 DisplayMediaType("Probing format", chain->arpmt[nFormatProbed]);
1442 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, chain->arpmt[nFormatProbed]); //set desired mediatype
1443 if(FAILED(hr)){
1444 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1445 continue;
1447 /* connecting filters together: VideoCapture --> SampleGrabber */
1448 hr = OLE_CALL_ARGS(priv->pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1449 if(FAILED(hr)){
1450 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr);
1451 continue;
1453 break;
1456 if(!chain->arpmt[nFormatProbed])
1458 mp_msg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to negotiate media format\n");
1459 hr = E_FAIL;
1460 break;
1463 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, chain->pmt);
1464 if(FAILED(hr))
1466 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to get actual mediatype (Error:0x%x). Assuming equal to requested.\n", (unsigned int)hr);
1469 if(priv->tv_param->hidden_video_renderer){
1470 IEnumFilters* pEnum;
1471 IBaseFilter* pFilter;
1473 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)chain->pCapturePin,NULL,NULL);
1475 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
1476 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
1477 LPVIDEOWINDOW pVideoWindow;
1478 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
1479 if (SUCCEEDED(hr))
1481 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
1482 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
1483 OLE_RELEASE_SAFE(pVideoWindow);
1485 OLE_RELEASE_SAFE(pFilter);
1487 OLE_RELEASE_SAFE(pEnum);
1488 }else
1490 #if 0
1492 Code below is disabled, because terminating chain with NullRenderer leads to jerky video.
1493 Perhaps, this happens because NullRenderer filter discards each received
1494 frame while discarded frames causes live source filter to dramatically reduce frame rate.
1496 /* adding sink for video stream */
1497 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR);
1498 if(FAILED(hr)){
1499 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1500 break;
1502 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer");
1503 if(FAILED(hr)){
1504 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1505 break;
1507 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn);
1508 if(FAILED(hr)){
1509 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr);
1510 break;
1513 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection
1515 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){
1516 /* connecting filters together: SampleGrabber --> NullRenderer */
1517 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn);
1518 if(FAILED(hr)){
1519 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr);
1520 break;
1523 #endif
1526 hr = S_OK;
1527 } while(0);
1529 OLE_RELEASE_SAFE(pSGOut);
1530 OLE_RELEASE_SAFE(pNR);
1531 OLE_RELEASE_SAFE(pNRIn);
1533 return hr;
1537 * \brief configures crossbar for grabbing video stream from given input
1539 * \param priv driver's private data
1540 * \param input index of available video input to get data from
1542 * \return TVI_CONTROL_TRUE success
1543 * \return TVI_CONTROL_FALSE error
1545 static int set_crossbar_input(priv_t * priv, int input)
1547 HRESULT hr;
1548 int i, nVideoDecoder, nAudioDecoder;
1549 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins,
1550 lInputPins;
1552 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n");
1553 if (!priv->pCrossbar || input < 0
1554 || input >= tv_available_inputs_count)
1555 return TVI_CONTROL_FALSE;
1557 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins);
1559 lInput = tv_available_inputs[input];
1561 if (lInput < 0 || lInput >= lInputPins)
1562 return TVI_CONTROL_FALSE;
1564 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput,
1565 &lInputRelated, &lPhysicalType);
1567 nVideoDecoder = nAudioDecoder = -1;
1568 for (i = 0; i < lOutputPins; i++) {
1569 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i,
1570 &lRelated, &lPhysicalType);
1571 if (lPhysicalType == PhysConn_Video_VideoDecoder)
1572 nVideoDecoder = i;
1573 if (lPhysicalType == PhysConn_Audio_AudioDecoder)
1574 nAudioDecoder = i;
1576 if (nVideoDecoder >= 0) {
1577 //connecting given input with video decoder
1578 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput);
1579 if (hr != S_OK) {
1580 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to video decoder. Error:0x%x\n", (unsigned int)hr);
1581 return TVI_CONTROL_FALSE;
1584 if (nAudioDecoder >= 0 && lInputRelated >= 0) {
1585 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder,
1586 lInputRelated);
1587 if (hr != S_OK) {
1588 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to audio decoder. Error:0x%x\n", (unsigned int)hr);
1589 return TVI_CONTROL_FALSE;
1592 return TVI_CONTROL_TRUE;
1596 * \brief adjusts video control (hue,saturation,contrast,brightess)
1598 * \param priv driver's private data
1599 * \param control which control to adjust
1600 * \param value new value for control (0-100)
1602 * \return TVI_CONTROL_TRUE success
1603 * \return TVI_CONTROL_FALSE error
1605 static int set_control(priv_t * priv, int control, int value)
1607 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1608 HRESULT hr;
1610 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n");
1611 if (value < -100 || value > 100 || !priv->pVideoProcAmp)
1612 return TVI_CONTROL_FALSE;
1614 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1615 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1616 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual)
1617 return TVI_CONTROL_FALSE;
1619 lValue = lMin + (value + 100) * (lMax - lMin) / 200;
1621 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256
1623 if (lStepping > lMax) {
1624 mp_msg(MSGT_TV, MSGL_DBG3,
1625 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n",
1626 lStepping, lMax,control);
1627 lStepping = 1;
1629 lValue -= lValue % lStepping;
1630 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue,
1631 VideoProcAmp_Flags_Manual);
1632 if (FAILED(hr))
1633 return TVI_CONTROL_FALSE;
1635 return TVI_CONTROL_TRUE;
1639 * \brief get current value of video control (hue,saturation,contrast,brightess)
1641 * \param priv driver's private data
1642 * \param control which control to adjust
1643 * \param pvalue address of variable thar receives current value
1645 * \return TVI_CONTROL_TRUE success
1646 * \return TVI_CONTROL_FALSE error
1648 static int get_control(priv_t * priv, int control, int *pvalue)
1650 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1651 HRESULT hr;
1653 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n");
1654 if (!pvalue || !priv->pVideoProcAmp)
1655 return TVI_CONTROL_FALSE;
1657 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1658 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1659 if (FAILED(hr))
1660 return TVI_CONTROL_FALSE;
1661 if (lMin == lMax) {
1662 *pvalue = lMin;
1663 return TVI_CONTROL_TRUE;
1666 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags);
1667 if (FAILED(hr))
1668 return TVI_CONTROL_FALSE;
1670 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100;
1672 return TVI_CONTROL_TRUE;
1676 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps
1677 * \param fcc FourCC code for video format
1678 * \param width picture width
1679 * \param height pciture height
1680 * \param fps frames per second (required for bitrate calculation)
1682 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise
1684 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps)
1686 int i;
1687 AM_MEDIA_TYPE mt;
1688 VIDEOINFOHEADER vHdr;
1690 /* Check given fcc in lookup table*/
1691 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */;
1692 if(!img_fmt_list[i].fmt)
1693 return NULL;
1695 memset(&mt, 0, sizeof(AM_MEDIA_TYPE));
1696 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER));
1698 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader);
1699 vHdr.bmiHeader.biWidth = width;
1700 vHdr.bmiHeader.biHeight = height;
1701 //FIXME: is biPlanes required too?
1702 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes;
1703 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits;
1704 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression;
1705 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8;
1706 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps;
1708 mt.pbFormat = (char*)&vHdr;
1709 mt.cbFormat = sizeof(vHdr);
1711 mt.majortype = MEDIATYPE_Video;
1712 mt.subtype = *img_fmt_list[i].subtype;
1713 mt.formattype = FORMAT_VideoInfo;
1715 mt.bFixedSizeSamples = 1;
1716 mt.bTemporalCompression = 0;
1717 mt.lSampleSize = vHdr.bmiHeader.biSizeImage;
1719 return CreateMediaType(&mt);
1723 * \brief extracts fcc,width,height from AM_MEDIA_TYPE
1725 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1726 * \param pfcc address of variable that receives FourCC
1727 * \param pwidth address of variable that receives width
1728 * \param pheight address of variable that recevies height
1730 * \return 1 if data extracted successfully, 0 - otherwise
1732 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc,
1733 int *pwidth, int *pheight)
1735 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n");
1736 if (!pmt)
1737 return 0;
1738 if (!pmt->pbFormat)
1739 return 0;
1740 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0)
1741 return 0;
1742 if (pfcc)
1743 *pfcc = subtype2imgfmt(&(pmt->subtype));
1744 if (pwidth)
1745 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth;
1746 if (pheight)
1747 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight;
1748 return 1;
1752 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE
1754 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1755 * \param pfcc address of variable that receives samplerate
1756 * \param pwidth address of variable that receives number of bits per sample
1757 * \param pheight address of variable that recevies number of channels
1759 * \return 1 if data extracted successfully, 0 - otherwise
1761 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate,
1762 int *pbits, int *pchannels)
1764 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n");
1765 if (!pmt)
1766 return 0;
1767 if (!pmt->pbFormat)
1768 return 0;
1769 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1770 return 0;
1771 if (psamplerate)
1772 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec;
1773 if (pbits)
1774 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample;
1775 if (pchannels)
1776 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels;
1777 return 1;
1781 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels
1783 * \param pmt pointer to AM_MEDIA_TYPE for check
1784 * \param samplerate audio samplerate
1785 * \param bits bits per sample
1786 * \param channels number of audio channels
1788 * \return 1 if AM_MEDIA_TYPE compatible
1789 * \return 0 if not
1791 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate,
1792 int bits, int channels)
1794 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n");
1795 if (!pmt)
1796 return 0;
1797 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0)
1798 return 0;
1799 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0)
1800 return 0;
1801 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1802 return 0;
1803 if (!pmt->pbFormat)
1804 return 0;
1805 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate)
1806 return 0;
1807 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits)
1808 return 0;
1809 if (channels > 0
1810 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels)
1811 return 0;
1813 return 1;
1817 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height
1819 * \param pmt pointer to AM_MEDIA_TYPE for check
1820 * \param fcc FourCC (compression)
1821 * \param width width of picture
1822 * \param height height of picture
1824 * \return 1 if AM_MEDIA_TYPE compatible
1825 & \return 0 if not
1827 * \note
1828 * width and height are currently not used
1830 * \todo
1831 * add width/height check
1833 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width,
1834 int height)
1836 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n");
1837 if (!pmt)
1838 return 0;
1839 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0)
1840 return 0;
1841 if (subtype2imgfmt(&(pmt->subtype)) != fcc)
1842 return 0;
1843 return 1;
1847 * \brief converts DirectShow subtype to MPlayer's IMGFMT
1849 * \param subtype DirectShow subtype for video format
1851 * \return MPlayer's IMGFMT or 0 if error occured
1853 static int subtype2imgfmt(const GUID * subtype)
1855 int i;
1856 for (i = 0; img_fmt_list[i].fmt; i++) {
1857 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0)
1858 return img_fmt_list[i].fmt;
1860 return 0;
1864 * \brief prints filter name and it pins
1866 * \param pFilter - IBaseFilter to get data from
1868 * \return S_OK if success, error code otherwise
1870 static HRESULT show_filter_info(IBaseFilter * pFilter)
1872 char tmp[200];
1873 FILTER_INFO fi;
1874 LPENUMPINS pEnum = 0;
1875 IPin *pPin = 0;
1876 PIN_DIRECTION ThisPinDir;
1877 PIN_INFO pi;
1878 HRESULT hr;
1879 int i;
1881 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n");
1882 memset(&fi, 0, sizeof(fi));
1883 memset(tmp, 0, 200);
1885 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi);
1886 OLE_RELEASE_SAFE(fi.pGraph);
1887 wtoa(fi.achName, tmp, 200);
1888 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:",
1889 pFilter, tmp, fi.pGraph);
1890 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum);
1891 if (FAILED(hr))
1892 return hr;
1893 i = 0;
1894 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1895 memset(&pi, 0, sizeof(pi));
1896 memset(tmp, 0, 200);
1897 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1898 if (ThisPinDir == PINDIR_OUTPUT) {
1899 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1900 wtoa(pi.achName, tmp, 200);
1901 OLE_RELEASE_SAFE(pi.pFilter);
1902 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp);
1903 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1904 mp_msg(MSGT_TV, MSGL_DBG2, ";");
1905 OLE_RELEASE_SAFE(pPin);
1906 i++;
1909 mp_msg(MSGT_TV, MSGL_DBG2, "\n");
1910 OLE_RELEASE_SAFE(pEnum);
1911 return S_OK;
1915 * \brief gets device's frendly in ANSI encoding
1917 * \param pM IMoniker interface, got in enumeration process
1918 * \param category device category
1920 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise
1922 static int get_device_name(IMoniker * pM, char *pBuf, int nLen)
1924 HRESULT hr;
1925 VARIANT var;
1926 IPropertyBag *pPropBag;
1927 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag);
1928 if (FAILED(hr)) {
1929 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n");
1930 return TVI_CONTROL_FALSE;
1932 var.vt = VT_BSTR;
1933 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var,
1934 NULL);
1935 if (FAILED(hr)) {
1936 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var,
1937 NULL);
1939 OLE_RELEASE_SAFE(pPropBag);
1940 if (SUCCEEDED(hr)) {
1941 wtoa(var.bstrVal, pBuf, nLen);
1942 return TVI_CONTROL_TRUE;
1944 return TVI_CONTROL_FALSE;
1948 * \brief find capture device at given index
1950 * \param index device index to search for (-1 mean only print available)
1951 * \param category device category
1953 * \return IBaseFilter interface for capture device with given index
1955 * Sample values for category:
1956 * CLSID_VideoInputDeviceCategory - Video Capture Sources
1957 * CLSID_AudioInputDeviceCategory - Audio Capture Sources
1958 * See DirectShow SDK documentation for other possible values
1960 static IBaseFilter *find_capture_device(int index, REFCLSID category)
1962 IBaseFilter *pFilter = NULL;
1963 ICreateDevEnum *pDevEnum = NULL;
1964 IEnumMoniker *pClassEnum = NULL;
1965 IMoniker *pM;
1966 HRESULT hr;
1967 ULONG cFetched;
1968 int i;
1969 char tmp[DEVICE_NAME_MAX_LEN + 1];
1970 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL,
1971 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
1972 (void *) &pDevEnum);
1973 if (FAILED(hr)) {
1974 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n");
1975 return NULL;
1978 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0);
1979 OLE_RELEASE_SAFE(pDevEnum);
1980 if (FAILED(hr)) {
1981 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n");
1982 return NULL;
1984 if (hr == S_FALSE) {
1985 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n");
1986 return NULL;
1989 OLE_CALL(pClassEnum,Reset);
1990 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) {
1991 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE)
1992 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to get name for device #%d\n", i);
1993 else
1994 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Device #%d: %s\n", i, tmp);
1995 if (index != -1 && i == index) {
1996 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Using device #%d: %s\n", index, tmp);
1997 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter);
1998 if (FAILED(hr))
1999 pFilter = NULL;
2001 OLE_RELEASE_SAFE(pM);
2003 if (index != -1 && !pFilter) {
2004 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Device #%d not found\n",
2005 index);
2007 OLE_RELEASE_SAFE(pClassEnum);
2009 return pFilter;
2013 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps
2015 * \praram[in] chain chain data structure
2017 * \return S_OK success
2018 * \return E_POINTER one of parameters is NULL
2019 * \return E_FAIL required size of buffer is unknown for given media type
2020 * \return E_OUTOFMEMORY not enough memory
2021 * \return other error code from called methods
2023 * \remarks
2024 * last items of chain->arpmt and chain->arStreamCaps will be NULL
2026 static HRESULT get_available_formats_stream(chain_t *chain)
2028 AM_MEDIA_TYPE **arpmt;
2029 void **pBuf=NULL;
2031 HRESULT hr;
2032 int i, count, size;
2033 int done;
2035 mp_msg(MSGT_TV, MSGL_DBG4,
2036 "tvi_dshow: get_available_formats_stream called\n");
2038 if (!chain->pStreamConfig)
2039 return E_POINTER;
2041 hr=OLE_CALL_ARGS(chain->pStreamConfig, GetNumberOfCapabilities, &count, &size);
2042 if (FAILED(hr)) {
2043 mp_msg(MSGT_TV, MSGL_DBG4,
2044 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n");
2045 return hr;
2047 if (chain->type == video){
2048 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
2049 mp_msg(MSGT_TV, MSGL_DBG4,
2050 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2051 return E_FAIL;
2053 } else if (chain->type == audio){
2054 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) {
2055 mp_msg(MSGT_TV, MSGL_DBG4,
2056 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2057 return E_FAIL;
2059 } else {
2060 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_stream");
2061 return E_FAIL;
2063 done = 0;
2065 arpmt = malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2066 if (arpmt) {
2067 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2069 pBuf = malloc((count + 1) * sizeof(void *));
2070 if (pBuf) {
2071 memset(pBuf, 0, (count + 1) * sizeof(void *));
2073 for (i = 0; i < count; i++) {
2074 pBuf[i] = malloc(size);
2076 if (!pBuf[i])
2077 break;
2079 hr = OLE_CALL_ARGS(chain->pStreamConfig, GetStreamCaps, i,
2080 &(arpmt[i]), pBuf[i]);
2081 if (FAILED(hr))
2082 break;
2084 if (i == count) {
2085 chain->arpmt = arpmt;
2086 chain->arStreamCaps = pBuf;
2087 done = 1;
2091 if (!done) {
2092 for (i = 0; i < count; i++) {
2093 if (pBuf)
2094 free(pBuf[i]);
2095 if (arpmt && arpmt[i])
2096 DeleteMediaType(arpmt[i]);
2098 free(pBuf);
2099 free(arpmt);
2100 if (hr != S_OK) {
2101 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n");
2102 return hr;
2103 } else
2104 return E_OUTOFMEMORY;
2106 return S_OK;
2110 * \brief returns allocates an array and store available media formats for given pin type to it
2112 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder
2113 * \param chain chain data structure
2115 * \return S_OK success
2116 * \return E_POINTER one of given pointers is null
2117 * \return apropriate error code otherwise
2119 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder,
2120 chain_t *chain)
2122 IEnumMediaTypes *pEnum;
2123 int i, count, size;
2124 ULONG cFetched;
2125 AM_MEDIA_TYPE *pmt;
2126 HRESULT hr;
2127 void **pBuf;
2128 AM_MEDIA_TYPE **arpmt; //This will be real array
2129 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps;
2130 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps;
2131 int p1, p2, p3;
2133 mp_msg(MSGT_TV, MSGL_DBG4,
2134 "tvi_dshow: get_available_formats_pin called\n");
2135 if (!pBuilder || !chain->pCaptureFilter)
2136 return E_POINTER;
2138 if (!chain->pCapturePin)
2140 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2141 (IUnknown *) chain->pCaptureFilter,
2142 PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE,
2143 chain->majortype, FALSE, 0, &chain->pCapturePin);
2145 if (!chain->pCapturePin)
2146 return E_POINTER;
2148 if (chain->type == video) {
2149 size = sizeof(VIDEO_STREAM_CONFIG_CAPS);
2150 } else if (chain->type == audio) {
2151 size = sizeof(AUDIO_STREAM_CONFIG_CAPS);
2152 } else {
2153 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_pin");
2154 return E_FAIL;
2157 hr = OLE_CALL_ARGS(chain->pCapturePin, EnumMediaTypes, &pEnum);
2158 if (FAILED(hr)) {
2159 mp_msg(MSGT_TV, MSGL_DBG4,
2160 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n");
2161 return hr;
2163 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) {
2164 if (!pmt)
2165 break;
2167 OLE_CALL(pEnum,Reset);
2169 count = i;
2170 arpmt = malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2171 if (!arpmt)
2172 return E_OUTOFMEMORY;
2173 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2175 for (i = 0;
2176 i < count
2177 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK;
2178 i++);
2180 OLE_RELEASE_SAFE(pEnum);
2183 pBuf = malloc((count + 1) * sizeof(void *));
2184 if (!pBuf) {
2185 for (i = 0; i < count; i++)
2186 if (arpmt[i])
2187 DeleteMediaType(arpmt[i]);
2188 free(arpmt);
2189 return E_OUTOFMEMORY;
2191 memset(pBuf, 0, (count + 1) * sizeof(void *));
2193 for (i = 0; i < count; i++) {
2194 pBuf[i] = malloc(size);
2195 if (!pBuf[i])
2196 break;
2197 memset(pBuf[i], 0, size);
2199 if (chain->type == video) {
2200 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i];
2201 extract_video_format(arpmt[i], NULL, &p1, &p2);
2202 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx =
2204 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy =
2206 } else {
2207 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i];
2208 extract_audio_format(arpmt[i], &p1, &p2, &p3);
2209 pAudioCaps->MaximumSampleFrequency =
2210 pAudioCaps->MinimumSampleFrequency = p1;
2211 pAudioCaps->MaximumBitsPerSample =
2212 pAudioCaps->MinimumBitsPerSample = p2;
2213 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3;
2217 if (i != count) {
2218 for (i = 0; i < count; i++) {
2219 if (arpmt[i])
2220 DeleteMediaType(arpmt[i]);
2221 free(pBuf[i]);
2223 free(arpmt);
2224 free(pBuf);
2225 return E_OUTOFMEMORY;
2227 chain->arpmt = arpmt;
2228 chain->arStreamCaps = pBuf;
2230 return S_OK;
2234 *---------------------------------------------------------------------------------------
2236 * Public methods
2238 *---------------------------------------------------------------------------------------
2241 * \brief fills given buffer with audio data (usually one block)
2243 * \param priv driver's private data structure
2244 * \param buffer buffer to store data to
2245 * \param len buffer's size in bytes (usually one block size)
2247 * \return audio pts if audio present, 1 - otherwise
2249 static double grab_audio_frame(priv_t * priv, char *buffer, int len)
2251 int bytes = 0;
2252 int i;
2253 double pts;
2254 grabber_ringbuffer_t *rb = priv->chains[1]->rbuf;
2255 grabber_ringbuffer_t *vrb = priv->chains[0]->rbuf;
2257 if (!rb || !rb->ringbuffer)
2258 return 1;
2260 if(vrb && vrb->tStart<0){
2261 memset(buffer,0,len);
2262 return 0;
2264 if(vrb && rb->tStart<0)
2265 rb->tStart=vrb->tStart;
2267 if (len < rb->blocksize)
2268 bytes = len;
2269 else
2270 bytes = rb->blocksize;
2272 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n",
2273 rb->count, len);
2274 if(!rb->count){
2275 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2276 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2277 if(!rb->count){
2278 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2279 return 0;
2281 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2284 EnterCriticalSection(rb->pMutex);
2285 pts=rb->dpts[rb->head]-rb->tStart;
2286 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2287 rb->head = (rb->head + 1) % rb->buffersize;
2288 rb->count--;
2289 LeaveCriticalSection(rb->pMutex);
2290 return pts;
2294 * \brief returns audio frame size
2296 * \param priv driver's private data structure
2298 * \return audio block size if audio enabled and 1 - otherwise
2300 static int get_audio_framesize(priv_t * priv)
2302 if (!priv->chains[1]->rbuf)
2303 return 1; //no audio
2304 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->chains[1]->rbuf->blocksize);
2305 return priv->chains[1]->rbuf->blocksize;
2308 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp)
2310 if(!priv || !ptsp)
2311 return TVI_CONTROL_FALSE;
2313 //STUBS!!!
2314 ptsp->interlaced=0;
2315 ptsp->offset=256;
2317 ptsp->sampling_rate=27e6;
2318 ptsp->samples_per_line=720;
2320 ptsp->count[0]=16;
2321 ptsp->count[1]=16;
2322 //END STUBS!!!
2323 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]);
2325 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",
2326 ptsp->sampling_rate,
2327 ptsp->offset,
2328 ptsp->samples_per_line,
2329 ptsp->interlaced?"Yes":"No",
2330 ptsp->count[0],
2331 ptsp->count[1]);
2333 return TVI_CONTROL_TRUE;
2336 static void vbi_grabber(priv_t* priv)
2338 grabber_ringbuffer_t *rb = priv->chains[2]->rbuf;
2339 int i;
2340 unsigned char* buf;
2341 if (!rb || !rb->ringbuffer)
2342 return;
2344 buf=calloc(1,rb->blocksize);
2345 for(i=0; i<23 && rb->count; i++){
2346 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize);
2347 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf);
2348 rb->head = (rb->head + 1) % rb->buffersize;
2349 rb->count--;
2351 free(buf);
2355 * \brief fills given buffer with video data (usually one frame)
2357 * \param priv driver's private data structure
2358 * \param buffer buffer to store data to
2359 * \param len buffer's size in bytes (usually one frame size)
2361 * \return frame size if video present, 0 - otherwise
2363 static double grab_video_frame(priv_t * priv, char *buffer, int len)
2365 int bytes = 0;
2366 int i;
2367 double pts;
2368 grabber_ringbuffer_t *rb = priv->chains[0]->rbuf;
2370 if (!rb || !rb->ringbuffer)
2371 return 1;
2372 if (len < rb->blocksize)
2373 bytes = len;
2374 else
2375 bytes = rb->blocksize;
2377 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n",
2378 rb->count, len);
2379 if(!rb->count){
2380 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2381 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2382 if(!rb->count){
2383 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2384 return 0;
2386 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2388 EnterCriticalSection(rb->pMutex);
2389 if(rb->tStart<0)
2390 rb->tStart=rb->dpts[rb->head];
2391 pts=rb->dpts[rb->head]-rb->tStart;
2392 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2393 rb->head = (rb->head + 1) % rb->buffersize;
2394 rb->count--;
2395 LeaveCriticalSection(rb->pMutex);
2397 vbi_grabber(priv);
2398 return pts;
2402 * \brief returns frame size
2404 * \param priv driver's private data structure
2406 * \return frame size if video present, 0 - otherwise
2408 static int get_video_framesize(priv_t * priv)
2410 // if(!priv->pmtVideo) return 1; //no video
2411 // return priv->pmtVideo->lSampleSize;
2412 if (!priv->chains[0]->rbuf)
2413 return 1; //no video
2414 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->chains[0]->rbuf->blocksize);
2415 return priv->chains[0]->rbuf->blocksize;
2419 * \brief calculate audio buffer size
2420 * \param video_buf_size size of video buffer in bytes
2421 * \param video_bitrate video bit rate
2422 * \param audio_bitrate audio bit rate
2423 * \return audio buffer isze in bytes
2425 * \remarks length of video buffer and resulted audio buffer calculated in
2426 * seconds will be the same.
2428 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate)
2430 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate);
2431 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n",
2432 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size);
2433 return audio_buf_size;
2437 * \brief common chain initialization routine
2438 * \param chain chain data structure
2440 * \note pCaptureFilter member should be initialized before call to this routine
2442 static HRESULT init_chain_common(ICaptureGraphBuilder2 *pBuilder, chain_t *chain)
2444 HRESULT hr;
2445 int i;
2447 if(!chain->pCaptureFilter)
2448 return E_POINTER;
2450 show_filter_info(chain->pCaptureFilter);
2452 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2453 (IUnknown *) chain->pCaptureFilter,
2454 PINDIR_OUTPUT, chain->pin_category,
2455 chain->majortype, FALSE, 0, &chain->pCapturePin);
2457 if (FAILED(hr)) {
2458 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
2459 return hr;
2462 hr = OLE_CALL_ARGS(pBuilder, FindInterface,
2463 chain->pin_category,
2464 chain->majortype,
2465 chain->pCaptureFilter,
2466 &IID_IAMStreamConfig,
2467 (void **) &(chain->pStreamConfig));
2468 if (FAILED(hr))
2469 chain->pStreamConfig = NULL;
2472 Getting available video formats (last pointer in array will be NULL)
2473 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as
2474 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default
2475 min/max values.
2477 hr = get_available_formats_stream(chain);
2478 if (FAILED(hr)) {
2479 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr);
2480 hr = get_available_formats_pin(pBuilder, chain);
2481 if(FAILED(hr)){
2482 return hr;
2485 chain->nFormatUsed = 0;
2487 //If argument to CreateMediaType is NULL then result will be NULL too.
2488 chain->pmt = CreateMediaType(chain->arpmt[0]);
2490 for (i = 0; chain->arpmt[i]; i++)
2491 DisplayMediaType("Available format", chain->arpmt[i]);
2493 return S_OK;
2496 * \brief build video stream chain in graph
2497 * \param priv private data structure
2499 * \return S_OK if chain was built successfully, apropriate error code otherwise
2501 static HRESULT build_video_chain(priv_t *priv)
2503 HRESULT hr;
2505 if(priv->chains[0]->rbuf)
2506 return S_OK;
2508 if (priv->chains[0]->pStreamConfig) {
2509 hr = OLE_CALL_ARGS(priv->chains[0]->pStreamConfig, SetFormat, priv->chains[0]->pmt);
2510 if (FAILED(hr)) {
2511 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select video format. Error:0x%x\n", (unsigned int)hr);
2515 priv->chains[0]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2516 if(!priv->chains[0]->rbuf)
2517 return E_OUTOFMEMORY;
2519 if (priv->tv_param->buffer_size >= 0) {
2520 priv->chains[0]->rbuf->buffersize = priv->tv_param->buffer_size;
2521 } else {
2522 priv->chains[0]->rbuf->buffersize = 16;
2525 priv->chains[0]->rbuf->buffersize *= 1024 * 1024;
2526 hr=build_sub_graph(priv, priv->chains[0], &PIN_CATEGORY_CAPTURE);
2527 if(FAILED(hr)){
2528 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build video chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2529 return hr;
2531 return S_OK;
2535 * \brief build audio stream chain in graph
2536 * \param priv private data structure
2538 * \return S_OK if chain was built successfully, apropriate error code otherwise
2540 static HRESULT build_audio_chain(priv_t *priv)
2542 HRESULT hr;
2544 if(priv->chains[1]->rbuf)
2545 return S_OK;
2547 if(priv->immediate_mode)
2548 return S_OK;
2550 if (priv->chains[1]->pStreamConfig) {
2551 hr = OLE_CALL_ARGS(priv->chains[1]->pStreamConfig, SetFormat,
2552 priv->chains[1]->pmt);
2553 if (FAILED(hr)) {
2554 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select audio format. Error:0x%x\n", (unsigned int)hr);
2558 if(priv->chains[1]->pmt){
2559 priv->chains[1]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2560 if(!priv->chains[1]->rbuf)
2561 return E_OUTOFMEMORY;
2563 /* let the audio buffer be the same size (in seconds) than video one */
2564 priv->chains[1]->rbuf->buffersize=audio_buf_size_from_video(
2565 priv->chains[0]->rbuf->buffersize,
2566 (((VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat)->dwBitRate),
2567 (((WAVEFORMATEX *) (priv->chains[1]->pmt->pbFormat))->nAvgBytesPerSec));
2569 hr=build_sub_graph(priv, priv->chains[1],&PIN_CATEGORY_CAPTURE);
2570 if(FAILED(hr)){
2571 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build audio chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2572 return 0;
2575 return S_OK;
2579 * \brief build VBI stream chain in graph
2580 * \param priv private data structure
2582 * \return S_OK if chain was built successfully, apropriate error code otherwise
2584 static HRESULT build_vbi_chain(priv_t *priv)
2586 HRESULT hr;
2588 if(priv->chains[2]->rbuf)
2589 return S_OK;
2591 if(priv->tv_param->teletext.device)
2593 priv->chains[2]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2594 if(!priv->chains[2]->rbuf)
2595 return E_OUTOFMEMORY;
2597 init_ringbuffer(priv->chains[2]->rbuf,24,priv->tsp.bufsize);
2599 hr=build_sub_graph(priv, priv->chains[2],&PIN_CATEGORY_VBI);
2600 if(FAILED(hr)){
2601 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build VBI chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2602 return 0;
2605 return S_OK;
2609 * \brief playback/capture real start
2611 * \param priv driver's private data structure
2613 * \return 1 if success, 0 - otherwise
2615 * TODO: move some code from init() here
2617 static int start(priv_t * priv)
2619 HRESULT hr;
2621 hr = build_video_chain(priv);
2622 if(FAILED(hr))
2623 return 0;
2625 hr = build_audio_chain(priv);
2626 if(FAILED(hr))
2627 return 0;
2629 hr = build_vbi_chain(priv);
2630 if(FAILED(hr))
2631 return 0;
2634 Graph is ready to capture. Starting graph.
2636 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2637 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n");
2638 usec_sleep(10000000);
2639 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n");
2641 if (!priv->pMediaControl) {
2642 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)E_POINTER);
2643 return 0;
2645 hr = OLE_CALL(priv->pMediaControl, Run);
2646 if (FAILED(hr)) {
2647 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to start graph! Error:0x%x\n", (unsigned int)hr);
2648 return 0;
2650 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n");
2651 priv->state = 1;
2653 return 1;
2657 * \brief driver initialization
2659 * \param priv driver's private data structure
2661 * \return 1 if success, 0 - otherwise
2663 static int init(priv_t * priv)
2665 HRESULT hr;
2666 int result = 0;
2667 long lInput, lTunerInput;
2668 IEnumFilters *pEnum;
2669 IBaseFilter *pFilter;
2670 IPin *pVPOutPin;
2671 int i;
2673 priv->state=0;
2675 CoInitialize(NULL);
2677 for(i=0; i<3;i++)
2678 priv->chains[i] = calloc(1, sizeof(chain_t));
2680 priv->chains[0]->type=video;
2681 priv->chains[0]->majortype=&MEDIATYPE_Video;
2682 priv->chains[0]->pin_category=&PIN_CATEGORY_CAPTURE;
2683 priv->chains[1]->type=audio;
2684 priv->chains[1]->majortype=&MEDIATYPE_Audio;
2685 priv->chains[1]->pin_category=&PIN_CATEGORY_CAPTURE;
2686 priv->chains[2]->type=vbi;
2687 priv->chains[2]->majortype=&MEDIATYPE_VBI;
2688 priv->chains[2]->pin_category=&PIN_CATEGORY_VBI;
2691 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL,
2692 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2693 (void **) &priv->pGraph);
2694 if(FAILED(hr)){
2695 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr);
2696 break;
2698 //Debug
2699 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2700 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister));
2703 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL,
2704 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2,
2705 (void **) &priv->pBuilder);
2706 if(FAILED(hr)){
2707 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr);
2708 break;
2711 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph);
2712 if(FAILED(hr)){
2713 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr);
2714 break;
2717 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n");
2718 priv->chains[0]->pCaptureFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory);
2719 if(!priv->chains[0]->pCaptureFilter){
2720 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find video capture device\n");
2721 break;
2723 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[0]->pCaptureFilter, NULL);
2724 if(FAILED(hr)){
2725 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2726 break;
2728 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n");
2729 if (priv->adev_index != -1) {
2730 priv->chains[1]->pCaptureFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices
2731 if(!priv->chains[1]->pCaptureFilter){
2732 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find audio capture device\n");
2733 break;
2736 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[1]->pCaptureFilter, NULL);
2737 if(FAILED(hr)){
2738 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2739 break;
2741 } else
2742 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[1]->pCaptureFilter);
2744 /* increase refrence counter for capture filter ad store pointer into vbi chain structure too */
2745 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[2]->pCaptureFilter);
2747 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp);
2748 if (FAILED(hr) && hr != E_NOINTERFACE)
2749 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr);
2751 if (hr != S_OK) {
2752 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Adjusting of brightness/hue/saturation/contrast is not supported by device\n");
2753 priv->pVideoProcAmp = NULL;
2756 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2757 &PIN_CATEGORY_CAPTURE,
2758 priv->chains[0]->majortype,
2759 priv->chains[0]->pCaptureFilter,
2760 &IID_IAMCrossbar, (void **) &(priv->pCrossbar));
2761 if (FAILED(hr)) {
2762 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Selection of capture source is not supported by device\n");
2763 priv->pCrossbar = NULL;
2766 if (priv->tv_param->amode >= 0) {
2767 IAMTVAudio *pTVAudio;
2768 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->chains[0]->pCaptureFilter,&IID_IAMTVAudio, (void *) &pTVAudio);
2769 if (hr == S_OK) {
2770 switch (priv->tv_param->amode) {
2771 case 0:
2772 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO);
2773 break;
2774 case 1:
2775 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO);
2776 break;
2777 case 2:
2778 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2779 AMTVAUDIO_MODE_LANG_A);
2780 break;
2781 case 3:
2782 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2783 AMTVAUDIO_MODE_LANG_B);
2784 break;
2786 OLE_RELEASE_SAFE(pTVAudio);
2787 if (FAILED(hr))
2788 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);
2792 // Video chain initialization
2793 hr = init_chain_common(priv->pBuilder, priv->chains[0]);
2794 if(FAILED(hr))
2795 break;
2798 Audio chain initialization
2799 Since absent audio stream is not fatal,
2800 at least one NULL pointer should be kept in format arrays
2801 (to avoid another additional check everywhere for array presence).
2803 hr = init_chain_common(priv->pBuilder, priv->chains[1]);
2804 if(FAILED(hr))
2806 mp_msg(MSGT_TV, MSGL_V,
2807 "tvi_dshow: Unable to initialize audio chain (Error:0x%lx). Audio disabled\n",
2808 (unsigned long)hr);
2809 priv->chains[1]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2810 priv->chains[1]->arStreamCaps=calloc(1, sizeof(void*));
2814 VBI chain initialization
2815 Since absent VBI stream is not fatal,
2816 at least one NULL pointer should be kept in format arrays
2817 (to avoid another additional check everywhere for array presence).
2819 hr = init_chain_common(priv->pBuilder, priv->chains[2]);
2820 if(FAILED(hr))
2822 mp_msg(MSGT_TV, MSGL_V,
2823 "tvi_dshow: Unable to initialize VBI chain (Error:0x%lx). Teletext disabled\n",
2824 (unsigned long)hr);
2825 priv->chains[2]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2826 priv->chains[2]->arStreamCaps=calloc(1, sizeof(void*));
2829 if (!priv->chains[0]->pStreamConfig)
2830 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Changing video width/height is not supported by device.\n");
2832 if (!priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]
2833 || !extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
2834 &(priv->fcc), &(priv->width),
2835 &(priv->height))) {
2836 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse video format structure.\n");
2837 break;
2840 if (priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]) {
2841 if (!extract_audio_format(priv->chains[1]->pmt, &(priv->samplerate), NULL, NULL)) {
2842 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse audio format structure.\n");
2843 DisplayMediaType("audio format failed",priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]);
2844 break;
2848 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl);
2849 if(FAILED(hr)){
2850 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)hr);
2851 break;
2853 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2854 &PIN_CATEGORY_CAPTURE, NULL,
2855 priv->chains[0]->pCaptureFilter,
2856 &IID_IAMTVTuner, (void **) &(priv->pTVTuner));
2858 if (!priv->pTVTuner) {
2859 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr);
2862 // shows Tuner capabilities
2863 get_capabilities(priv);
2865 if (priv->pTVTuner) {
2866 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode,
2867 chanlist2country(priv->tv_param->chanlist));
2868 if(FAILED(hr)){
2869 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr);
2872 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV);
2873 if(FAILED(hr)){
2874 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr);
2875 break;
2878 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
2879 if(FAILED(hr)){
2880 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr);
2881 break;
2884 /* small hack */
2885 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna;
2887 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput);
2888 if(FAILED(hr)){
2889 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr);
2890 break;
2896 for VIVO cards we should check if preview pin is available on video capture device.
2897 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter.
2898 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph
2900 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
2901 (IUnknown *) priv->chains[0]->pCaptureFilter,
2902 PINDIR_OUTPUT,
2903 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE,
2904 0, (IPin **) & pVPOutPin);
2905 if (SUCCEEDED(hr)) {
2906 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin);
2907 OLE_RELEASE_SAFE(pVPOutPin);
2909 if (FAILED(hr)) {
2910 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);
2911 break;
2915 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
2916 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
2917 LPVIDEOWINDOW pVideoWindow;
2918 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
2919 if (SUCCEEDED(hr))
2921 if(priv->tv_param->hidden_vp_renderer){
2922 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
2923 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
2924 }else
2926 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter);
2928 OLE_RELEASE_SAFE(pVideoWindow);
2930 OLE_RELEASE_SAFE(pFilter);
2932 OLE_RELEASE_SAFE(pEnum);
2933 if(priv->tv_param->system_clock)
2935 LPREFERENCECLOCK rc;
2936 IBaseFilter* pBF;
2937 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL,
2938 CLSCTX_INPROC_SERVER, &IID_IReferenceClock,
2939 (void *) &rc);
2941 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF);
2942 OLE_CALL_ARGS(pBF,SetSyncSource,rc);
2944 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE)
2945 break;
2946 result = 1;
2947 } while(0);
2949 if (!result){
2950 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Directshow graph initialization failure.\n");
2951 uninit(priv);
2953 return result;
2957 * \brief chain uninitialization
2958 * \param chain chain data structure
2960 static void destroy_chain(chain_t *chain)
2962 int i;
2964 if(!chain)
2965 return;
2967 OLE_RELEASE_SAFE(chain->pStreamConfig);
2968 OLE_RELEASE_SAFE(chain->pCaptureFilter);
2969 OLE_RELEASE_SAFE(chain->pCSGCB);
2970 OLE_RELEASE_SAFE(chain->pCapturePin);
2971 OLE_RELEASE_SAFE(chain->pSGIn);
2972 OLE_RELEASE_SAFE(chain->pSG);
2973 OLE_RELEASE_SAFE(chain->pSGF);
2975 if (chain->pmt)
2976 DeleteMediaType(chain->pmt);
2978 if (chain->arpmt) {
2979 for (i = 0; chain->arpmt[i]; i++) {
2980 DeleteMediaType(chain->arpmt[i]);
2982 free(chain->arpmt);
2985 if (chain->arStreamCaps) {
2986 for (i = 0; chain->arStreamCaps[i]; i++) {
2987 free(chain->arStreamCaps[i]);
2989 free(chain->arStreamCaps);
2992 if (chain->rbuf) {
2993 destroy_ringbuffer(chain->rbuf);
2994 free(chain->rbuf);
2995 chain->rbuf = NULL;
2997 free(chain);
3000 * \brief driver uninitialization
3002 * \param priv driver's private data structure
3004 * \return always 1
3006 static int uninit(priv_t * priv)
3008 int i;
3009 if (!priv)
3010 return 1;
3011 //Debug
3012 if (priv->dwRegister) {
3013 RemoveFromRot(priv->dwRegister);
3015 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1);
3016 //stop audio grabber thread
3018 if (priv->state && priv->pMediaControl) {
3019 OLE_CALL(priv->pMediaControl, Stop);
3021 OLE_RELEASE_SAFE(priv->pMediaControl);
3022 priv->state = 0;
3024 if (priv->pGraph) {
3025 if (priv->chains[0]->pCaptureFilter)
3026 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[0]->pCaptureFilter);
3027 if (priv->chains[1]->pCaptureFilter)
3028 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[1]->pCaptureFilter);
3030 OLE_RELEASE_SAFE(priv->pCrossbar);
3031 OLE_RELEASE_SAFE(priv->pVideoProcAmp);
3032 OLE_RELEASE_SAFE(priv->pGraph);
3033 OLE_RELEASE_SAFE(priv->pBuilder);
3034 if(priv->freq_table){
3035 priv->freq_table_len=-1;
3036 free(priv->freq_table);
3037 priv->freq_table=NULL;
3040 for(i=0; i<3;i++)
3042 destroy_chain(priv->chains[i]);
3043 priv->chains[i] = NULL;
3045 CoUninitialize();
3046 return 1;
3050 * \brief driver pre-initialization
3052 * \param device string, containing device name in form "x[.y]", where x is video capture device
3053 * (default: 0, first available); y (if given) sets audio capture device
3055 * \return 1 if success,0 - otherwise
3057 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param)
3059 tvi_handle_t *h;
3060 priv_t *priv;
3061 int a;
3063 h = tv_new_handle(sizeof(priv_t), &functions);
3064 if (!h)
3065 return NULL;
3067 priv = h->priv;
3069 memset(priv, 0, sizeof(priv_t));
3070 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3071 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3072 priv->adev_index = -1;
3073 priv->freq_table_len=-1;
3074 priv->tv_param=tv_param;
3076 if (tv_param->device) {
3077 if (sscanf(tv_param->device, "%d", &a) == 1) {
3078 priv->dev_index = a;
3079 } else {
3080 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device parameter: %s\n", tv_param->device);
3081 tv_free_handle(h);
3082 return NULL;
3084 if (priv->dev_index < 0) {
3085 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device index: %d\n", a);
3086 tv_free_handle(h);
3087 return NULL;
3090 if (tv_param->adevice) {
3091 if (sscanf(tv_param->adevice, "%d", &a) == 1) {
3092 priv->adev_index = a;
3093 } else {
3094 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice parameter: %s\n", tv_param->adevice);
3095 tv_free_handle(h);
3096 return NULL;
3098 if (priv->dev_index < 0) {
3099 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice index: %d\n", a);
3100 tv_free_handle(h);
3101 return NULL;
3104 return h;
3108 * \brief driver's ioctl handler
3110 * \param priv driver's private data structure
3111 * \param cmd ioctl command
3112 * \param arg ioct command's parameter
3114 * \return TVI_CONTROL_TRUE if success
3115 * \return TVI_CONTROL_FALSE if failure
3116 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called
3118 static int control(priv_t * priv, int cmd, void *arg)
3120 switch (cmd) {
3121 /* need rewrite */
3122 case TVI_CONTROL_VID_SET_FORMAT:
3124 int fcc, i,j;
3125 void* tmp,*tmp2;
3126 int result = TVI_CONTROL_TRUE;
3128 if (priv->state)
3129 return TVI_CONTROL_FALSE;
3130 fcc = *(int *) arg;
3132 if(!priv->chains[0]->arpmt)
3133 return TVI_CONTROL_FALSE;
3134 for (i = 0; priv->chains[0]->arpmt[i]; i++)
3135 if (check_video_format
3136 (priv->chains[0]->arpmt[i], fcc, priv->width, priv->height))
3137 break;
3138 if (!priv->chains[0]->arpmt[i])
3140 int fps = 0;
3141 VIDEOINFOHEADER* Vhdr = NULL;
3142 AM_MEDIA_TYPE *pmt;
3144 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));
3146 if (priv->chains[0]->arpmt[0])
3147 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->arpmt[0]->pbFormat;
3149 if(Vhdr && Vhdr->bmiHeader.biSizeImage)
3150 fps = Vhdr->dwBitRate / (8 * Vhdr->bmiHeader.biSizeImage);
3152 pmt=create_video_format(fcc, priv->width, priv->height, fps);
3153 if(!pmt)
3155 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to create AM_MEDIA_TYPE structure for given format\n");
3156 return TVI_CONTROL_FALSE;
3158 priv->chains[0]->arpmt=realloc(priv->chains[0]->arpmt, (i+2)*sizeof(AM_MEDIA_TYPE*));
3159 priv->chains[0]->arpmt[i+1] = NULL;
3160 priv->chains[0]->arpmt[i] = pmt;
3162 priv->chains[0]->arStreamCaps=realloc(priv->chains[0]->arStreamCaps, (i+2)*sizeof(void*));
3163 priv->chains[0]->arpmt[i+1] = NULL;
3165 result = TVI_CONTROL_FALSE;
3169 tmp=priv->chains[0]->arpmt[i];
3170 tmp2=priv->chains[0]->arStreamCaps[i];
3171 for(j=i; j>0; j--)
3173 priv->chains[0]->arpmt[j] = priv->chains[0]->arpmt[j-1];
3174 priv->chains[0]->arStreamCaps[j] = priv->chains[0]->arStreamCaps[j-1];
3176 priv->chains[0]->arpmt[0] = tmp;
3177 priv->chains[0]->arStreamCaps[0] = tmp2;
3179 priv->chains[0]->nFormatUsed = 0;
3181 if (priv->chains[0]->pmt)
3182 DeleteMediaType(priv->chains[0]->pmt);
3183 priv->chains[0]->pmt =
3184 CreateMediaType(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]);
3185 DisplayMediaType("VID_SET_FORMAT", priv->chains[0]->pmt);
3187 Setting width & height to preferred by driver values
3189 extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
3190 &(priv->fcc), &(priv->width),
3191 &(priv->height));
3192 return result;
3194 case TVI_CONTROL_VID_GET_FORMAT:
3196 if(!priv->chains[0]->pmt)
3197 return TVI_CONTROL_FALSE;
3199 Build video chain (for video format negotiation).
3200 If this was done before, routine will do nothing.
3202 build_video_chain(priv);
3203 DisplayMediaType("VID_GET_FORMAT", priv->chains[0]->pmt);
3204 if (priv->fcc) {
3205 *(int *) arg = priv->fcc;
3206 return TVI_CONTROL_TRUE;
3207 } else
3208 return TVI_CONTROL_FALSE;
3210 case TVI_CONTROL_VID_SET_WIDTH:
3212 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3213 VIDEOINFOHEADER *Vhdr;
3214 int width = *(int *) arg;
3215 if (priv->state)
3216 return TVI_CONTROL_FALSE;
3218 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3219 if (!pCaps)
3220 return TVI_CONTROL_FALSE;
3221 if (width < pCaps->MinOutputSize.cx
3222 || width > pCaps->MaxOutputSize.cx)
3223 return TVI_CONTROL_FALSE;
3225 if (width % pCaps->OutputGranularityX)
3226 return TVI_CONTROL_FALSE;
3228 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3229 return TVI_CONTROL_FALSE;
3230 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3231 Vhdr->bmiHeader.biWidth = width;
3232 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3233 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3234 Vhdr->bmiHeader.biHeight) >> 3;
3236 priv->width = width;
3238 return TVI_CONTROL_TRUE;
3240 case TVI_CONTROL_VID_GET_WIDTH:
3242 if (priv->width) {
3243 *(int *) arg = priv->width;
3244 return TVI_CONTROL_TRUE;
3245 } else
3246 return TVI_CONTROL_FALSE;
3248 case TVI_CONTROL_VID_CHK_WIDTH:
3250 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3251 int width = *(int *) arg;
3252 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3253 if (!pCaps)
3254 return TVI_CONTROL_FALSE;
3255 if (width < pCaps->MinOutputSize.cx
3256 || width > pCaps->MaxOutputSize.cx)
3257 return TVI_CONTROL_FALSE;
3259 if (width % pCaps->OutputGranularityX)
3260 return TVI_CONTROL_FALSE;
3261 return TVI_CONTROL_TRUE;
3263 case TVI_CONTROL_VID_SET_HEIGHT:
3265 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3266 VIDEOINFOHEADER *Vhdr;
3267 int height = *(int *) arg;
3268 if (priv->state)
3269 return TVI_CONTROL_FALSE;
3271 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3272 if (!pCaps)
3273 return TVI_CONTROL_FALSE;
3274 if (height < pCaps->MinOutputSize.cy
3275 || height > pCaps->MaxOutputSize.cy)
3276 return TVI_CONTROL_FALSE;
3278 if (height % pCaps->OutputGranularityY)
3279 return TVI_CONTROL_FALSE;
3281 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3282 return TVI_CONTROL_FALSE;
3283 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3285 if (Vhdr->bmiHeader.biHeight < 0)
3286 Vhdr->bmiHeader.biHeight = -height;
3287 else
3288 Vhdr->bmiHeader.biHeight = height;
3289 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3290 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3291 Vhdr->bmiHeader.biHeight) >> 3;
3293 priv->height = height;
3294 return TVI_CONTROL_TRUE;
3296 case TVI_CONTROL_VID_GET_HEIGHT:
3298 if (priv->height) {
3299 *(int *) arg = priv->height;
3300 return TVI_CONTROL_TRUE;
3301 } else
3302 return TVI_CONTROL_FALSE;
3304 case TVI_CONTROL_VID_CHK_HEIGHT:
3306 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3307 int height = *(int *) arg;
3308 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3309 if (!pCaps)
3310 return TVI_CONTROL_FALSE;
3311 if (height < pCaps->MinOutputSize.cy
3312 || height > pCaps->MaxOutputSize.cy)
3313 return TVI_CONTROL_FALSE;
3315 if (height % pCaps->OutputGranularityY)
3316 return TVI_CONTROL_FALSE;
3318 return TVI_CONTROL_TRUE;
3320 case TVI_CONTROL_IS_AUDIO:
3321 if (!priv->chains[1]->pmt)
3322 return TVI_CONTROL_FALSE;
3323 else
3324 return TVI_CONTROL_TRUE;
3325 case TVI_CONTROL_IS_VIDEO:
3326 return TVI_CONTROL_TRUE;
3327 case TVI_CONTROL_AUD_GET_FORMAT:
3329 *(int *) arg = AF_FORMAT_S16_LE;
3330 if (!priv->chains[1]->pmt)
3331 return TVI_CONTROL_FALSE;
3332 else
3333 return TVI_CONTROL_TRUE;
3335 case TVI_CONTROL_AUD_GET_CHANNELS:
3337 *(int *) arg = priv->channels;
3338 if (!priv->chains[1]->pmt)
3339 return TVI_CONTROL_FALSE;
3340 else
3341 return TVI_CONTROL_TRUE;
3343 case TVI_CONTROL_AUD_SET_SAMPLERATE:
3345 int i, samplerate;
3346 if (priv->state)
3347 return TVI_CONTROL_FALSE;
3348 if (!priv->chains[1]->arpmt[0])
3349 return TVI_CONTROL_FALSE;
3351 samplerate = *(int *) arg;
3353 for (i = 0; priv->chains[1]->arpmt[i]; i++)
3354 if (check_audio_format
3355 (priv->chains[1]->arpmt[i], samplerate, 16, priv->channels))
3356 break;
3357 if (!priv->chains[1]->arpmt[i]) {
3358 //request not found. failing back to first available
3359 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Samplerate %d is not supported by device. Failing back to first available.\n", samplerate);
3360 i = 0;
3362 if (priv->chains[1]->pmt)
3363 DeleteMediaType(priv->chains[1]->pmt);
3364 priv->chains[1]->pmt = CreateMediaType(priv->chains[1]->arpmt[i]);
3365 extract_audio_format(priv->chains[1]->arpmt[i], &(priv->samplerate),
3366 NULL, &(priv->channels));
3367 return TVI_CONTROL_TRUE;
3369 case TVI_CONTROL_AUD_GET_SAMPLERATE:
3371 *(int *) arg = priv->samplerate;
3372 if (!priv->samplerate)
3373 return TVI_CONTROL_FALSE;
3374 if (!priv->chains[1]->pmt)
3375 return TVI_CONTROL_FALSE;
3376 else
3377 return TVI_CONTROL_TRUE;
3379 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
3381 WAVEFORMATEX *pWF;
3382 if (!priv->chains[1]->pmt)
3383 return TVI_CONTROL_FALSE;
3384 if (!priv->chains[1]->pmt->pbFormat)
3385 return TVI_CONTROL_FALSE;
3386 pWF = (WAVEFORMATEX *) priv->chains[1]->pmt->pbFormat;
3387 *(int *) arg = pWF->wBitsPerSample / 8;
3388 return TVI_CONTROL_TRUE;
3390 case TVI_CONTROL_IS_TUNER:
3392 if (!priv->pTVTuner)
3393 return TVI_CONTROL_FALSE;
3395 return TVI_CONTROL_TRUE;
3397 case TVI_CONTROL_TUN_SET_NORM:
3399 IAMAnalogVideoDecoder *pVD;
3400 long lAnalogFormat;
3401 int i;
3402 HRESULT hr;
3404 i = *(int *) arg;
3405 i--;
3406 if (i < 0 || i >= tv_available_norms_count)
3407 return TVI_CONTROL_FALSE;
3408 lAnalogFormat = tv_norms[tv_available_norms[i]].index;
3410 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3411 if (hr != S_OK)
3412 return TVI_CONTROL_FALSE;
3413 hr = OLE_CALL_ARGS(pVD, put_TVFormat, lAnalogFormat);
3414 OLE_RELEASE_SAFE(pVD);
3415 if (FAILED(hr))
3416 return TVI_CONTROL_FALSE;
3417 else
3418 return TVI_CONTROL_TRUE;
3420 case TVI_CONTROL_TUN_GET_NORM:
3422 long lAnalogFormat;
3423 int i;
3424 HRESULT hr;
3425 IAMAnalogVideoDecoder *pVD;
3427 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3428 if (hr == S_OK) {
3429 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat);
3430 OLE_RELEASE_SAFE(pVD);
3433 if (FAILED(hr)) { //trying another method
3434 if (!priv->pTVTuner)
3435 return TVI_CONTROL_FALSE;
3436 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat);
3437 if (FAILED(hr))
3438 return TVI_CONTROL_FALSE;
3440 for (i = 0; i < tv_available_norms_count; i++) {
3441 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) {
3442 *(int *) arg = i + 1;
3443 return TVI_CONTROL_TRUE;
3446 return TVI_CONTROL_FALSE;
3448 case TVI_CONTROL_SPC_GET_NORMID:
3450 int i;
3451 if (!priv->pTVTuner)
3452 return TVI_CONTROL_FALSE;
3453 for (i = 0; i < tv_available_norms_count; i++) {
3454 if (!strcasecmp
3455 (tv_norms[tv_available_norms[i]].name, (char *) arg)) {
3456 *(int *) arg = i + 1;
3457 return TVI_CONTROL_TRUE;
3460 return TVI_CONTROL_FALSE;
3462 case TVI_CONTROL_SPC_SET_INPUT:
3464 return set_crossbar_input(priv, *(int *) arg);
3466 case TVI_CONTROL_TUN_GET_FREQ:
3468 unsigned long lFreq;
3469 int ret;
3470 if (!priv->pTVTuner)
3471 return TVI_CONTROL_FALSE;
3473 ret = get_frequency(priv, &lFreq);
3474 lFreq = lFreq / (1000000/16); //convert from Hz to 1/16 MHz units
3476 *(unsigned long *) arg = lFreq;
3477 return ret;
3479 case TVI_CONTROL_TUN_SET_FREQ:
3481 unsigned long nFreq = *(unsigned long *) arg;
3482 if (!priv->pTVTuner)
3483 return TVI_CONTROL_FALSE;
3484 //convert to Hz
3485 nFreq = (1000000/16) * nFreq; //convert from 1/16 MHz units to Hz
3486 return set_frequency(priv, nFreq);
3488 case TVI_CONTROL_VID_SET_HUE:
3489 return set_control(priv, VideoProcAmp_Hue, *(int *) arg);
3490 case TVI_CONTROL_VID_GET_HUE:
3491 return get_control(priv, VideoProcAmp_Hue, (int *) arg);
3492 case TVI_CONTROL_VID_SET_CONTRAST:
3493 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg);
3494 case TVI_CONTROL_VID_GET_CONTRAST:
3495 return get_control(priv, VideoProcAmp_Contrast, (int *) arg);
3496 case TVI_CONTROL_VID_SET_SATURATION:
3497 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg);
3498 case TVI_CONTROL_VID_GET_SATURATION:
3499 return get_control(priv, VideoProcAmp_Saturation, (int *) arg);
3500 case TVI_CONTROL_VID_SET_BRIGHTNESS:
3501 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg);
3502 case TVI_CONTROL_VID_GET_BRIGHTNESS:
3503 return get_control(priv, VideoProcAmp_Brightness, (int *) arg);
3505 case TVI_CONTROL_VID_GET_FPS:
3507 VIDEOINFOHEADER *Vhdr;
3508 if (!priv->chains[0]->pmt)
3509 return TVI_CONTROL_FALSE;
3510 if (!priv->chains[0]->pmt->pbFormat)
3511 return TVI_CONTROL_FALSE;
3512 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3513 *(float *) arg =
3514 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8);
3515 return TVI_CONTROL_TRUE;
3517 case TVI_CONTROL_IMMEDIATE:
3518 priv->immediate_mode = 1;
3519 return TVI_CONTROL_TRUE;
3520 case TVI_CONTROL_VBI_INIT:
3522 void* ptr;
3523 ptr=&(priv->tsp);
3524 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==VBI_CONTROL_TRUE)
3525 priv->priv_vbi=ptr;
3526 else
3527 priv->priv_vbi=NULL;
3528 return TVI_CONTROL_TRUE;
3530 case TVI_CONTROL_GET_VBI_PTR:
3531 *(void **)arg=priv->priv_vbi;
3532 return TVI_CONTROL_TRUE;
3534 return TVI_CONTROL_UNKNOWN;