sync with en/mplayer.1 rev. 30611
[mplayer/glamo.git] / stream / tvi_dshow.c
blob9aec47ed54847648f492e76f7481964b8e0bb758
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 "help_mp.h"
82 #include "osdep/timer.h"
85 #include "tv.h"
86 #include "mp_msg.h"
87 #include "frequencies.h"
90 #include "tvi_dshow.h"
92 #ifndef STDCALL
93 // mingw64 needs this
94 #define STDCALL __stdcall
95 #endif
97 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param);
100 *---------------------------------------------------------------------------------------
102 * Data structures
104 *---------------------------------------------------------------------------------------
107 information about this file
109 const tvi_info_t tvi_info_dshow = {
110 tvi_init_dshow,
111 "DirectShow TV",
112 "dshow",
113 "Vladimir Voroshilov",
114 "Very experimental!! Use with caution"
119 ringbuffer related info
121 typedef struct {
122 CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex)
123 char **ringbuffer; ///< ringbuffer array
124 double*dpts; ///< samples' timestamps
126 int buffersize; ///< size of buffer in blocks
127 int blocksize; ///< size of individual block
128 int head; ///< index of first valid sample
129 int tail; ///< index of last valid sample
130 int count; ///< count of valid samples in ringbuffer
131 double tStart; ///< pts of first sample (first sample should have pts 0)
132 } grabber_ringbuffer_t;
134 typedef enum { unknown, video, audio, vbi } stream_type;
137 CSampleGrabberCD definition
139 typedef struct CSampleGrabberCB {
140 ISampleGrabberCBVtbl *lpVtbl;
141 int refcount;
142 GUID interfaces[2];
143 grabber_ringbuffer_t *pbuf;
144 } CSampleGrabberCB;
147 Chain related structure
149 typedef struct {
150 stream_type type; ///< stream type
151 const GUID* majortype; ///< GUID of major mediatype (video/audio/vbi)
152 const GUID* pin_category; ///< pin category (pointer to one of PIN_CATEGORY_*)
154 IBaseFilter *pCaptureFilter; ///< capture device filter
155 IAMStreamConfig *pStreamConfig; ///< for configuring stream
156 ISampleGrabber *pSG; ///< ISampleGrabber interface of SampleGrabber filter
157 IBaseFilter *pSGF; ///< IBaseFilter interface of SampleGrabber filter
158 IPin *pCapturePin; ///< output capture pin
159 IPin *pSGIn; ///< input pin of SampleGrabber filter
161 grabber_ringbuffer_t *rbuf; ///< sample frabber data
162 CSampleGrabberCB* pCSGCB; ///< callback object
164 AM_MEDIA_TYPE *pmt; ///< stream properties.
165 int nFormatUsed; ///< index of used format
166 AM_MEDIA_TYPE **arpmt; ///< available formats
167 void** arStreamCaps; ///< VIDEO_STREAM_CONFIG_CAPS or AUDIO_STREAM_CONFIG_CAPS
168 } chain_t;
170 typedef struct {
171 int dev_index; ///< capture device index in device list (defaul: 0, first available device)
172 int adev_index; ///< audio capture device index in device list (default: -1, not used)
173 int immediate_mode; ///< immediate mode (no sound capture)
174 int state; ///< state: 1-filter graph running, 0-filter graph stopped
175 int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency
176 int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency
178 int fcc; ///< used video format code (FourCC)
179 int width; ///< picture width (default: auto)
180 int height; ///< picture height (default: auto)
182 int channels; ///< number of audio channels (default: auto)
183 int samplerate; ///< audio samplerate (default: auto)
185 long *freq_table; ///< frequency table (in Hz)
186 int freq_table_len; ///< length of freq table
187 int first_channel; ///< channel number of first entry in freq table
188 int input; ///< used input
190 chain_t* chains[3]; ///< chains' data (0-video, 1-audio, 2-vbi)
192 IAMTVTuner *pTVTuner; ///< interface for tuner device
193 IGraphBuilder *pGraph; ///< filter graph
194 ICaptureGraphBuilder2 *pBuilder; ///< graph builder
195 IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...)
196 IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc
197 IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...)
198 DWORD dwRegister; ///< allow graphedit to connect to our graph
199 void *priv_vbi; ///< private VBI data structure
200 tt_stream_props tsp; ///< data for VBI initialization
202 tv_param_t* tv_param; ///< TV parameters
203 } priv_t;
205 #include "tvi_def.h"
208 country table entry structure (for loading freq table stored in kstvtuner.ax
210 \note
211 structure have to be 2-byte aligned and have 10-byte length!!
213 typedef struct __attribute__((__packed__)) {
214 WORD CountryCode; ///< Country code
215 WORD CableFreqTable; ///< index of resource with frequencies for cable channels
216 WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels
217 DWORD VideoStandard; ///< used video standard
218 } TRCCountryList;
220 information about image formats
222 typedef struct {
223 uint32_t fmt; ///< FourCC
224 const GUID *subtype; ///< DirectShow's subtype
225 int nBits; ///< number of bits
226 int nCompression; ///< complression
227 int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure
228 } img_fmt;
231 *---------------------------------------------------------------------------------------
233 * Methods forward declaration
235 *---------------------------------------------------------------------------------------
237 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
238 int blocksize);
239 static HRESULT show_filter_info(IBaseFilter * pFilter);
240 #if 0
241 //defined in current MinGW release
242 HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **);
243 HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **);
244 #endif
245 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
246 pbuf);
247 static int set_crossbar_input(priv_t * priv, int input);
248 static int subtype2imgfmt(const GUID * subtype);
251 *---------------------------------------------------------------------------------------
253 * Global constants and variables
255 *---------------------------------------------------------------------------------------
258 lookup tables for physical connector types
260 static const struct {
261 long type;
262 char *name;
263 } tv_physcon_types[]={
264 {PhysConn_Video_Tuner, "Tuner" },
265 {PhysConn_Video_Composite, "Composite" },
266 {PhysConn_Video_SVideo, "S-Video" },
267 {PhysConn_Video_RGB, "RGB" },
268 {PhysConn_Video_YRYBY, "YRYBY" },
269 {PhysConn_Video_SerialDigital, "SerialDigital" },
270 {PhysConn_Video_ParallelDigital, "ParallelDigital"},
271 {PhysConn_Video_VideoDecoder, "VideoDecoder" },
272 {PhysConn_Video_VideoEncoder, "VideoEncoder" },
273 {PhysConn_Video_SCART, "SCART" },
274 {PhysConn_Video_Black, "Blaack" },
275 {PhysConn_Audio_Tuner, "Tuner" },
276 {PhysConn_Audio_Line, "Line" },
277 {PhysConn_Audio_Mic, "Mic" },
278 {PhysConn_Audio_AESDigital, "AESDiital" },
279 {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" },
280 {PhysConn_Audio_AudioDecoder, "AudioDecoder" },
281 {PhysConn_Audio_SCSI, "SCSI" },
282 {PhysConn_Video_SCSI, "SCSI" },
283 {PhysConn_Audio_AUX, "AUX" },
284 {PhysConn_Video_AUX, "AUX" },
285 {PhysConn_Audio_1394, "1394" },
286 {PhysConn_Video_1394, "1394" },
287 {PhysConn_Audio_USB, "USB" },
288 {PhysConn_Video_USB, "USB" },
289 {-1, NULL }
292 static const struct {
293 char *chanlist_name;
294 int country_code;
295 } tv_chanlist2country[]={
296 {"us-bcast", 1},
297 {"russia", 7},
298 {"argentina", 54},
299 {"japan-bcast", 81},
300 {"china-bcast", 86},
301 {"southafrica", 27},
302 {"australia", 61},
303 {"ireland", 353},
304 {"france", 33},
305 {"italy", 39},
306 {"newzealand", 64},
307 //directshow table uses eastern europe freq table for russia
308 {"europe-east", 7},
309 //directshow table uses western europe freq table for germany
310 {"europe-west", 49},
311 /* cable channels */
312 {"us-cable", 1},
313 {"us-cable-hrc", 1},
314 {"japan-cable", 81},
315 //default is USA
316 {NULL, 1}
320 array, contains information about various supported (i hope) image formats
322 static const img_fmt img_fmt_list[] = {
323 {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 16, IMGFMT_YUY2, 0},
324 {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 12, IMGFMT_YV12, 0},
325 {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 12, IMGFMT_IYUV, 0},
326 {IMGFMT_I420, &MEDIASUBTYPE_I420, 12, IMGFMT_I420, 0},
327 {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 16, IMGFMT_UYVY, 0},
328 {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 16, IMGFMT_YVYU, 0},
329 {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 9, IMGFMT_YVU9, 0},
330 {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0},
331 {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0},
332 {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12},
333 {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12},
334 {0, &GUID_NULL, 0, 0, 0}
337 #define TV_NORMS_COUNT 19
338 static const struct {
339 long index;
340 char *name;
341 } tv_norms[TV_NORMS_COUNT] = {
343 AnalogVideo_NTSC_M, "ntsc-m"}, {
344 AnalogVideo_NTSC_M_J, "ntsc-mj"}, {
345 AnalogVideo_NTSC_433, "ntsc-433"}, {
346 AnalogVideo_PAL_B, "pal-b"}, {
347 AnalogVideo_PAL_D, "pal-d"}, {
348 AnalogVideo_PAL_G, "pal-g"}, {
349 AnalogVideo_PAL_H, "pal-h"}, {
350 AnalogVideo_PAL_I, "pal-i"}, {
351 AnalogVideo_PAL_M, "pal-m"}, {
352 AnalogVideo_PAL_N, "pal-n"}, {
353 AnalogVideo_PAL_60, "pal-60"}, {
354 AnalogVideo_SECAM_B, "secam-b"}, {
355 AnalogVideo_SECAM_D, "secam-d"}, {
356 AnalogVideo_SECAM_G, "secam-g"}, {
357 AnalogVideo_SECAM_H, "secam-h"}, {
358 AnalogVideo_SECAM_K, "secam-k"}, {
359 AnalogVideo_SECAM_K1, "secam-k1"}, {
360 AnalogVideo_SECAM_L, "secam-l"}, {
361 AnalogVideo_SECAM_L1, "secam-l1"}
363 static long tv_available_norms[TV_NORMS_COUNT];
364 static int tv_available_norms_count = 0;
367 static long *tv_available_inputs;
368 static int tv_available_inputs_count = 0;
371 *---------------------------------------------------------------------------------------
373 * Various GUID definitions
375 *---------------------------------------------------------------------------------------
377 // selectany can not be used with "static", fixes compilation with mingw-w64
378 #undef DECLSPEC_SELECTANY
379 #define DECLSPEC_SELECTANY
380 /// CLSID definitions (used for CoCreateInstance call)
381 #define CLSID_SampleGrabber MP_CLSID_SampleGrabber
382 static DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B,
383 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
384 #define CLSID_NullRenderer MP_CLSID_NullRenderer
385 static DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B,
386 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
387 #define CLSID_SystemDeviceEnum MP_CLSID_SystemDeviceEnum
388 static DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B,
389 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
390 #define CLSID_CaptureGraphBuilder2 MP_CLSID_CaptureGraphBuilder2
391 static DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3,
392 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
393 #define CLSID_VideoInputDeviceCategory MP_CLSID_VideoInputDeviceCategory
394 static DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0,
395 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
396 #define CLSID_AudioInputDeviceCategory MP_CLSID_AudioInputDeviceCategory
397 static DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0,
398 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
399 #define CLSID_FilterGraph MP_CLSID_FilterGraph
400 static DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53,
401 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
402 #define CLSID_SystemClock MP_CLSID_SystemClock
403 static DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53,
404 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
405 #ifdef NOT_USED
406 #define CLSID_CaptureGraphBuilder MP_CLSID_CaptureGraphBuilder
407 static DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3,
408 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
409 #define CLSID_VideoPortManager MP_CLSID_VideoPortManager
410 static DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a,
411 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2);
412 #define IID_IPin MP_IID_IPin
413 static DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20,
414 0xaf, 0x0b, 0xa7, 0x70);
415 #define IID_ICaptureGraphBuilder MP_IID_ICaptureGraphBuilder
416 static DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3,
417 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5);
418 #define IID_IFilterGraph MP_IID_IFilterGraph
419 static DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
420 0x20, 0xaf, 0x0b, 0xa7, 0x70);
421 #define PIN_CATEGORY_PREVIEW MP_PIN_CATEGORY_PREVIEW
422 static DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
423 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
424 #endif
426 /// IID definitions (used for QueryInterface call)
427 #define IID_IReferenceClock MP_IID_IReferenceClock
428 static DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a,
429 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
430 #define IID_IAMBufferNegotiation MP_IID_IAMBufferNegotiation
431 static DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0,
432 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
433 #define IID_IKsPropertySet MP_IID_IKsPropertySet
434 static DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa,
435 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
436 #define IID_ISampleGrabber MP_IID_ISampleGrabber
437 static DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD,
438 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F);
439 #define IID_ISampleGrabberCB MP_IID_ISampleGrabberCB
440 static DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0,
441 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85);
442 #define IID_ICaptureGraphBuilder2 MP_IID_ICaptureGraphBuilder2
443 static DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,
444 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d);
445 #define IID_ICreateDevEnum MP_IID_ICreateDevEnum
446 static DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b,
447 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
448 #define IID_IGraphBuilder MP_IID_IGraphBuilder
449 static DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a,
450 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
451 #define IID_IAMVideoProcAmp MP_IID_IAMVideoProcAmp
452 static DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C,
453 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
454 #define IID_IVideoWindow MP_IID_IVideoWindow
455 static DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
456 0x20, 0xaf, 0x0b, 0xa7, 0x70);
457 #define IID_IMediaControl MP_IID_IMediaControl
458 static DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a,
459 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
460 #define IID_IAMTVTuner MP_IID_IAMTVTuner
461 static DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00,
462 0xAA, 0x00, 0xBD, 0x83, 0x39);
463 #define IID_IAMCrossbar MP_IID_IAMCrossbar
464 static DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00,
465 0xa0, 0xc9, 0x11, 0x89, 0x56);
466 #define IID_IAMStreamConfig MP_IID_IAMStreamConfig
467 static DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c,
468 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
469 #define IID_IAMAudioInputMixer MP_IID_IAMAudioInputMixer
470 static DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0,
471 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
472 #define IID_IAMTVAudio MP_IID_IAMTVAudio
473 static DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00,
474 0xA0, 0xC9, 0x56, 0x02, 0x66);
475 #define IID_IAMAnalogVideoDecoder MP_IID_IAMAnalogVideoDecoder
476 static DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1,
477 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
478 #define IID_IPropertyBag MP_IID_IPropertyBag
479 static DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00,
480 0xaa, 0x00, 0x4b, 0xb8, 0x51);
481 #define PIN_CATEGORY_CAPTURE MP_PIN_CATEGORY_CAPTURE
482 static DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f,
483 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
484 #define PIN_CATEGORY_VIDEOPORT MP_PIN_CATEGORY_VIDEOPORT
485 static DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f,
486 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
487 #define PIN_CATEGORY_PREVIEW MP_PIN_CATEGORY_PREVIEW
488 static DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
489 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
490 #define PIN_CATEGORY_VBI MP_PIN_CATEGORY_VBI
491 static DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f,
492 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
493 #define PROPSETID_TUNER MP_PROPSETID_TUNER
494 static DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00,
495 0xa0, 0xc9, 0x11, 0x89, 0x56);
496 #define MEDIATYPE_VBI MP_MEDIATYPE_VBI
497 static DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00,
498 0x00, 0xc0, 0xcc, 0x16, 0xba);
500 #define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
501 #define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
503 #define DEVICE_NAME_MAX_LEN 2000
505 /*---------------------------------------------------------------------------------------
506 * Methods, called only from this file
507 *---------------------------------------------------------------------------------------*/
509 void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){
510 ALLOCATOR_PROPERTIES prop;
511 IAMBufferNegotiation* pBN;
512 HRESULT hr;
514 prop.cbAlign = -1;
515 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
516 if (!prop.cbBuffer)
517 prop.cbBuffer = 1;
518 prop.cbBuffer += pWF->nBlockAlign - 1;
519 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
520 prop.cbPrefix = -1;
521 prop.cBuffers = -1;
523 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
524 if(FAILED(hr))
525 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
526 else{
527 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
528 if(FAILED(hr))
529 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
530 OLE_RELEASE_SAFE(pBN);
532 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
533 if(FAILED(hr))
534 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
535 else{
536 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
537 if(FAILED(hr))
538 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
539 OLE_RELEASE_SAFE(pBN);
543 *---------------------------------------------------------------------------------------
545 * CSampleGrabberCD class. Used for receiving samples from DirectShow.
547 *---------------------------------------------------------------------------------------
549 /// CSampleGrabberCD destructor
550 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
552 free(This->lpVtbl);
553 free(This);
556 /// CSampleGrabberCD IUnknown interface methods implementation
557 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
558 This,
559 const GUID * riid,
560 void **ppvObject)
562 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
563 GUID *r;
564 unsigned int i = 0;
565 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
566 if (!ppvObject)
567 return E_POINTER;
568 for (r = me->interfaces;
569 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
570 if (!memcmp(r, riid, sizeof(*r))) {
571 OLE_CALL(This, AddRef);
572 *ppvObject = This;
573 return 0;
575 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
576 return E_NOINTERFACE;
579 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
581 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
582 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
583 me->refcount);
584 return ++(me->refcount);
587 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
589 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
590 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
591 This, me->refcount - 1);
592 if (--(me->refcount) == 0)
593 CSampleGrabberCB_Destroy(me);
594 return 0;
598 HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This,
599 double SampleTime,
600 BYTE * pBuffer, long lBufferLen)
602 CSampleGrabberCB *this = (CSampleGrabberCB *) This;
603 grabber_ringbuffer_t *rb = this->pbuf;
605 if (!lBufferLen)
606 return E_FAIL;
608 if (!rb->ringbuffer) {
609 rb->buffersize /= lBufferLen;
610 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
611 return E_FAIL;
613 mp_msg(MSGT_TV, MSGL_DBG4,
614 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
615 EnterCriticalSection(rb->pMutex);
616 if (rb->count >= rb->buffersize) {
617 rb->head = (rb->head + 1) % rb->buffersize;
618 rb->count--;
621 memcpy(rb->ringbuffer[rb->tail], pBuffer,
622 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
623 rb->dpts[rb->tail] = SampleTime;
624 rb->tail = (rb->tail + 1) % rb->buffersize;
625 rb->count++;
626 LeaveCriticalSection(rb->pMutex);
628 return S_OK;
631 /// wrapper. directshow does the same when BufferCB callback is requested
632 HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This,
633 double SampleTime,
634 LPMEDIASAMPLE pSample)
636 char* buf;
637 long len;
638 long long tStart,tEnd;
639 HRESULT hr;
640 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
642 len=OLE_CALL(pSample,GetSize);
643 tStart=tEnd=0;
644 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
645 if(FAILED(hr)){
646 return hr;
648 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
649 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
650 if(FAILED(hr)){
651 return hr;
653 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
654 return hr;
658 /// main grabbing routine
659 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
660 pbuf)
662 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
663 if (!This)
664 return NULL;
666 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
667 if (!This->lpVtbl) {
668 CSampleGrabberCB_Destroy(This);
669 return NULL;
671 This->refcount = 1;
672 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
673 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
674 This->lpVtbl->Release = CSampleGrabberCB_Release;
675 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
676 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
678 This->interfaces[0] = IID_IUnknown;
679 This->interfaces[1] = IID_ISampleGrabberCB;
681 This->pbuf = pbuf;
683 return This;
687 *---------------------------------------------------------------------------------------
689 * ROT related methods (register, unregister)
691 *---------------------------------------------------------------------------------------
694 Registering graph in ROT. User will be able to connect to graph from GraphEdit.
696 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
698 IMoniker *pMoniker;
699 IRunningObjectTable *pROT;
700 WCHAR wsz[256];
701 HRESULT hr;
703 if (FAILED(GetRunningObjectTable(0, &pROT))) {
704 return E_FAIL;
706 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
707 GetCurrentProcessId());
708 hr = CreateItemMoniker(L"!", wsz, &pMoniker);
709 if (SUCCEEDED(hr)) {
710 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
711 pUnkGraph, pMoniker, pdwRegister);
712 OLE_RELEASE_SAFE(pMoniker);
714 OLE_RELEASE_SAFE(pROT);
715 return hr;
718 /// Unregistering graph in ROT
719 static void RemoveFromRot(DWORD dwRegister)
721 IRunningObjectTable *pROT;
722 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
723 OLE_CALL_ARGS(pROT, Revoke, dwRegister);
724 OLE_RELEASE_SAFE(pROT);
729 *---------------------------------------------------------------------------------------
731 * ringbuffer related methods (init, destroy)
733 *---------------------------------------------------------------------------------------
736 * \brief ringbuffer destroying routine
738 * \param rb pointer to empty (just allocated) ringbuffer structure
740 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
742 static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
744 int i;
746 if (!rb)
747 return;
749 if (rb->ringbuffer) {
750 for (i = 0; i < rb->buffersize; i++)
751 if (rb->ringbuffer[i])
752 free(rb->ringbuffer[i]);
753 free(rb->ringbuffer);
754 rb->ringbuffer = NULL;
756 if (rb->dpts) {
757 free(rb->dpts);
758 rb->dpts = NULL;
760 if (rb->pMutex) {
761 DeleteCriticalSection(rb->pMutex);
762 free(rb->pMutex);
763 rb->pMutex = NULL;
766 rb->blocksize = 0;
767 rb->buffersize = 0;
768 rb->head = 0;
769 rb->tail = 0;
770 rb->count = 0;
774 * \brief ringbuffer initialization
776 * \param rb pointer to empty (just allocated) ringbuffer structure
777 * \param buffersize size of buffer in blocks
778 * \param blocksize size of buffer's block
780 * \return S_OK if success
781 * \return E_OUTOFMEMORY not enough memory
783 * \note routine does not allocates memory for grabber_rinbuffer_s structure
785 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
786 int blocksize)
788 int i;
790 if (!rb)
791 return E_OUTOFMEMORY;
793 rb->buffersize = buffersize < 2 ? 2 : buffersize;
794 rb->blocksize = blocksize;
796 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
797 rb->buffersize, rb->blocksize);
799 rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *));
800 if (!rb)
801 return E_POINTER;
802 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
804 for (i = 0; i < rb->buffersize; i++) {
805 rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char));
806 if (!rb->ringbuffer[i]) {
807 destroy_ringbuffer(rb);
808 return E_OUTOFMEMORY;
811 rb->dpts = (double*) malloc(rb->buffersize * sizeof(double));
812 if (!rb->dpts) {
813 destroy_ringbuffer(rb);
814 return E_OUTOFMEMORY;
816 rb->head = 0;
817 rb->tail = 0;
818 rb->count = 0;
819 rb->tStart = -1;
820 rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
821 if (!rb->pMutex) {
822 destroy_ringbuffer(rb);
823 return E_OUTOFMEMORY;
825 InitializeCriticalSection(rb->pMutex);
826 return S_OK;
830 *---------------------------------------------------------------------------------------
832 * Tuner related methods (frequency, capabilities, etc
834 *---------------------------------------------------------------------------------------
837 * \brief returns string with name for givend PsysCon_* constant
839 * \param lPhysicalType constant from PhysicalConnectorType enumeration
841 * \return pointer to string with apropriate name
843 * \note
844 * Caller should not free returned pointer
846 static char *physcon2str(const long lPhysicalType)
848 int i;
849 for(i=0; tv_physcon_types[i].name; i++)
850 if(tv_physcon_types[i].type==lPhysicalType)
851 return tv_physcon_types[i].name;
852 return "Unknown";
856 * \brief converts MPlayer's chanlist to system country code.
858 * \param chanlist MPlayer's chanlist name
860 * \return system country code
862 * \remarks
863 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
864 * country (which is usually larger then MPlayer's one, so workaround will work fine).
866 * \todo
867 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
869 static int chanlist2country(char *chanlist)
871 int i;
872 for(i=0; tv_chanlist2country[i].chanlist_name; i++)
873 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
874 break;
875 return tv_chanlist2country[i].country_code;
879 * \brief loads specified resource from module and return pointer to it
881 * \param hDLL valid module desriptor
882 * \param index index of resource. resource with name "#<index>" will be loaded
884 * \return pointer to loader resource or NULL if error occured
886 static void *GetRC(HMODULE hDLL, int index)
888 char szRCDATA[10];
889 char szName[10];
890 HRSRC hRes;
891 HGLOBAL hTable;
893 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
894 snprintf(szName, 10, "#%d", index);
896 hRes = FindResource(hDLL, szName, szRCDATA);
897 if (!hRes) {
898 return NULL;
900 hTable = LoadResource(hDLL, hRes);
901 if (!hTable) {
902 return NULL;
904 return LockResource(hTable);
908 * \brief loads frequency table for given country from kstvtune.ax
910 * \param[in] nCountry - country code
911 * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
912 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
913 * \param[out] pnLen length of array
914 * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
916 * \return S_OK if success
917 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
918 * \return E_FAIL error occured during load
920 * \remarks
921 * - array must be freed by caller
922 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
924 static HRESULT load_freq_table(int nCountry, int nInputType,
925 long **pplFreqTable, int *pnLen,
926 int *pnFirst)
928 HMODULE hDLL;
929 long *plFreqTable;
930 TRCCountryList *pCountryList;
931 int i, index;
933 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%s)\n",nCountry,nInputType == TunerInputAntenna ? "broadcast" : "cable");
934 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
936 if (!pplFreqTable || !pnFirst || !pnLen)
937 return E_POINTER;
938 if (!nCountry)
939 return E_FAIL;
941 hDLL = LoadLibrary("kstvtune.ax");
942 if (!hDLL) {
943 return E_FAIL;
945 pCountryList = GetRC(hDLL, 9999);
946 if (!pCountryList) {
947 FreeLibrary(hDLL);
948 return E_FAIL;
950 for (i = 0; pCountryList[i].CountryCode != 0; i++)
951 if (pCountryList[i].CountryCode == nCountry)
952 break;
953 if (pCountryList[i].CountryCode == 0) {
954 FreeLibrary(hDLL);
955 return E_FAIL;
957 if (nInputType == TunerInputCable)
958 index = pCountryList[i].CableFreqTable;
959 else
960 index = pCountryList[i].BroadcastFreqTable;
962 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
963 if (!plFreqTable) {
964 FreeLibrary(hDLL);
965 return E_FAIL;
967 *pnFirst = plFreqTable[0];
968 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
969 *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long));
970 if (!*pplFreqTable) {
971 FreeLibrary(hDLL);
972 return E_FAIL;
974 for (i = 0; i < *pnLen; i++) {
975 (*pplFreqTable)[i] = plFreqTable[i + 2];
976 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table #%d => (%ld)\n",i+*pnFirst,(*pplFreqTable)[i]);
978 FreeLibrary(hDLL);
979 return S_OK;
983 * \brief tunes to given frequency through IKsPropertySet call
985 * \param pTVTuner IAMTVTuner interface of capture device
986 * \param lFreq frequency to tune (in Hz)
988 * \return S_OK success
989 * \return apropriate error code otherwise
991 * \note
992 * Due to either bug in driver or error in following code calll to IKsProperty::Set
993 * in this methods always fail with error 0x8007007a.
995 * \todo test code on other machines and an error
997 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
999 HRESULT hr;
1000 DWORD dwSupported = 0;
1001 DWORD cbBytes = 0;
1002 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
1003 KSPROPERTY_TUNER_FREQUENCY_S frequency;
1004 IKsPropertySet *pKSProp;
1006 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
1008 memset(&mode_caps, 0, sizeof(mode_caps));
1009 memset(&frequency, 0, sizeof(frequency));
1011 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1012 if (FAILED(hr))
1013 return hr; //no IKsPropertySet interface
1015 mode_caps.Mode = AMTUNER_MODE_TV;
1016 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
1017 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
1018 if (FAILED(hr)) {
1019 OLE_RELEASE_SAFE(pKSProp);
1020 return hr;
1023 if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
1024 OLE_RELEASE_SAFE(pKSProp);
1025 return E_FAIL; //PROPSETID_TINER not supported
1028 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1029 KSPROPERTY_TUNER_MODE_CAPS,
1030 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
1031 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
1032 &mode_caps, sizeof(mode_caps), &cbBytes);
1034 frequency.Frequency = lFreq;
1036 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
1037 frequency.TuningFlags = KS_TUNER_TUNING_FINE;
1038 else
1039 frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
1041 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
1042 OLE_RELEASE_SAFE(pKSProp);
1043 return E_FAIL;
1046 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
1047 KSPROPERTY_TUNER_FREQUENCY,
1048 INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
1049 INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
1050 &frequency, sizeof(frequency));
1051 if (FAILED(hr)) {
1052 OLE_RELEASE_SAFE(pKSProp);
1053 return hr;
1056 OLE_RELEASE_SAFE(pKSProp);
1058 return S_OK;
1062 * \brief find channel with nearest frequency and set it
1064 * \param priv driver's private data
1065 * \param lFreq frequency in Hz
1067 * \return S_OK if success
1068 * \return E_FAIL if error occured
1070 static HRESULT set_nearest_freq(priv_t * priv, long lFreq)
1072 HRESULT hr;
1073 int i;
1074 long lFreqDiff=-1;
1075 int nChannel;
1076 TunerInputType tunerInput;
1077 long lInput;
1079 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called: %ld\n", lFreq);
1080 if(priv->freq_table_len == -1 && !priv->freq_table) {
1082 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
1083 if(FAILED(hr)){ //Falling back to 0
1084 lInput=0;
1087 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput);
1089 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME
1090 priv->freq_table_len=0;
1091 priv->freq_table=NULL;
1092 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableExtractFreqTable);
1093 return E_FAIL;
1095 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_FreqTableLoaded, tunerInput == TunerInputAntenna ? "broadcast" : "cable",
1096 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len);
1099 if (priv->freq_table_len <= 0)
1100 return E_FAIL;
1102 //FIXME: rewrite search algo
1103 nChannel = -1;
1104 for (i = 0; i < priv->freq_table_len; i++) {
1105 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) {
1106 nChannel = priv->first_channel + i;
1107 lFreqDiff = labs(lFreq - priv->freq_table[i]);
1109 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);
1111 if (nChannel == -1) {
1112 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableFindNearestChannel);
1113 return E_FAIL;
1115 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: set_nearest_freq #%d (%ld)\n",nChannel,priv->freq_table[nChannel - priv->first_channel]);
1116 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel,
1117 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
1118 if (FAILED(hr)) {
1119 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableToSetChannel, (unsigned int)hr);
1120 return E_FAIL;
1122 return S_OK;
1126 * \brief setting frequency. decides whether use direct call/workaround
1128 * \param priv driver's private data
1129 * \param lFreq frequency in Hz
1131 * \return TVI_CONTROL_TRUE if success
1132 * \return TVI_CONTROL_FALSE if error occured
1134 * \todo check for freq boundary
1136 static int set_frequency(priv_t * priv, long lFreq)
1138 HRESULT hr;
1140 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called: %ld\n", lFreq);
1141 if (!priv->pTVTuner)
1142 return TVI_CONTROL_FALSE;
1143 if (priv->direct_setfreq_call) { //using direct call to set frequency
1144 hr = set_frequency_direct(priv->pTVTuner, lFreq);
1145 if (FAILED(hr)) {
1146 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DirectSetFreqFailed);
1147 priv->direct_setfreq_call = 0;
1150 if (!priv->direct_setfreq_call) {
1151 hr = set_nearest_freq(priv, lFreq);
1153 if (FAILED(hr))
1154 return TVI_CONTROL_FALSE;
1155 #ifdef DEPRECATED
1156 priv->pGrabber->ClearBuffer(priv->pGrabber);
1157 #endif
1158 return TVI_CONTROL_TRUE;
1162 * \brief return current frequency from tuner (in Hz)
1164 * \param pTVTuner IAMTVTuner interface of tuner
1165 * \param plFreq address of variable that receives current frequency
1167 * \return S_OK success
1168 * \return E_POINTER pTVTuner==NULL || plFreq==NULL
1169 * \return apropriate error code otherwise
1171 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq)
1173 HRESULT hr;
1174 KSPROPERTY_TUNER_STATUS_S TunerStatus;
1175 DWORD cbBytes;
1176 IKsPropertySet *pKSProp;
1177 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n");
1179 if (!plFreq)
1180 return E_POINTER;
1182 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1183 if (FAILED(hr)) {
1184 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n");
1185 return hr;
1188 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1189 KSPROPERTY_TUNER_STATUS,
1190 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus),
1191 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus),
1192 &TunerStatus, sizeof(TunerStatus), &cbBytes);
1193 if (FAILED(hr)) {
1194 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n");
1195 return hr;
1197 *plFreq = TunerStatus.CurrentFrequency;
1198 return S_OK;
1202 * \brief gets current frequency
1204 * \param priv driver's private data structure
1205 * \param plFreq - pointer to long int to store frequency to (in Hz)
1207 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
1209 static int get_frequency(priv_t * priv, long *plFreq)
1211 HRESULT hr;
1213 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n");
1215 if (!plFreq || !priv->pTVTuner)
1216 return TVI_CONTROL_FALSE;
1218 if (priv->direct_getfreq_call) { //using direct call to get frequency
1219 hr = get_frequency_direct(priv->pTVTuner, plFreq);
1220 if (FAILED(hr)) {
1221 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_DirectGetFreqFailed);
1222 priv->direct_getfreq_call = 0;
1225 if (!priv->direct_getfreq_call) {
1226 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq);
1227 if (FAILED(hr))
1228 return TVI_CONTROL_FALSE;
1231 return TVI_CONTROL_TRUE;
1235 * \brief get tuner capabilities
1237 * \param priv driver's private data
1239 static void get_capabilities(priv_t * priv)
1241 long lAvailableFormats;
1242 HRESULT hr;
1243 int i;
1244 long lInputPins, lOutputPins, lRelated, lPhysicalType;
1245 IEnumPins *pEnum;
1246 char tmp[200];
1247 IPin *pPin = 0;
1248 PIN_DIRECTION ThisPinDir;
1249 PIN_INFO pi;
1250 IAMAudioInputMixer *pIAMixer;
1252 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n");
1253 if (priv->pTVTuner) {
1255 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_SupportedNorms);
1256 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats,
1257 &lAvailableFormats);
1258 if (FAILED(hr))
1259 tv_available_norms_count = 0;
1260 else {
1261 for (i = 0; i < TV_NORMS_COUNT; i++) {
1262 if (lAvailableFormats & tv_norms[i].index) {
1263 tv_available_norms[tv_available_norms_count] = i;
1264 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1265 tv_available_norms_count + 1, tv_norms[i].name);
1266 tv_available_norms_count++;
1270 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1272 if (priv->pCrossbar) {
1273 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins,
1274 &lInputPins);
1276 tv_available_inputs = (long *) malloc(sizeof(long) * lInputPins);
1277 tv_available_inputs_count = 0;
1279 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableVideoInputs);
1280 for (i = 0; i < lInputPins; i++) {
1281 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i,
1282 &lRelated, &lPhysicalType);
1284 if (lPhysicalType < 0x1000) {
1285 tv_available_inputs[tv_available_inputs_count++] = i;
1286 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1287 tv_available_inputs_count - 1,
1288 physcon2str(lPhysicalType));
1291 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1293 set_crossbar_input(priv, 0);
1296 if (priv->adev_index != -1) {
1297 hr = OLE_CALL_ARGS(priv->chains[1]->pCaptureFilter, EnumPins, &pEnum);
1298 if (FAILED(hr))
1299 return;
1300 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableAudioInputs);
1301 i = 0;
1302 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1303 memset(&pi, 0, sizeof(pi));
1304 memset(tmp, 0, 200);
1305 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1306 if (ThisPinDir == PINDIR_INPUT) {
1307 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1308 wtoa(pi.achName, tmp, 200);
1309 OLE_RELEASE_SAFE(pi.pFilter);
1310 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp);
1311 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1312 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer);
1313 if (SUCCEEDED(hr)) {
1314 if (i == priv->tv_param->audio_id) {
1315 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE);
1316 if(priv->tv_param->volume>0)
1317 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume);
1318 #if 0
1319 else
1320 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0);
1321 #endif
1322 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_InputSelected);
1323 } else {
1324 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE);
1325 #if 0
1326 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0);
1327 #endif
1329 OLE_RELEASE_SAFE(pIAMixer);
1331 mp_msg(MSGT_TV, MSGL_V, ";");
1332 OLE_RELEASE_SAFE(pPin);
1333 i++;
1336 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1337 OLE_RELEASE_SAFE(pEnum);
1342 *---------------------------------------------------------------------------------------
1344 * Filter related methods
1346 *---------------------------------------------------------------------------------------
1349 * \brief building in graph audio/video capture chain
1351 * \param priv driver's private data
1352 * \param pCaptureFilter pointer to capture device's IBaseFilter interface
1353 * \param pbuf ringbuffer data structure
1354 * \param pmt media type for chain (AM_MEDIA_TYPE)
1356 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
1358 static HRESULT build_sub_graph(priv_t * priv, chain_t * chain, const GUID* ppin_category)
1360 HRESULT hr;
1361 int nFormatProbed = 0;
1363 IPin *pSGOut;
1364 IPin *pNRIn=NULL;
1366 IBaseFilter *pNR = NULL;
1368 hr=S_OK;
1370 //No supported formats
1371 if(!chain->arpmt[0])
1372 return E_FAIL;
1375 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
1376 (IUnknown *) chain->pCaptureFilter,
1377 PINDIR_OUTPUT, ppin_category,
1378 chain->majortype, FALSE, 0, &chain->pCapturePin);
1379 if(FAILED(hr)){
1380 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
1381 break;
1383 /* Addinf SampleGrabber filter for video stream */
1384 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &chain->pSGF);
1385 if(FAILED(hr)){
1386 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1387 break;
1389 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, chain->pSGF, L"Sample Grabber");
1390 if(FAILED(hr)){
1391 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1392 break;
1394 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &chain->pSGIn);
1395 if(FAILED(hr)){
1396 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr);
1397 break;
1399 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut);
1400 if(FAILED(hr)){
1401 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr);
1402 break;
1405 /* creating ringbuffer for video samples */
1406 chain->pCSGCB = CSampleGrabberCB_Create(chain->rbuf);
1407 if(!chain->pCSGCB){
1408 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY);
1409 break;
1412 /* initializing SampleGrabber filter */
1413 hr = OLE_QUERYINTERFACE(chain->pSGF, IID_ISampleGrabber, chain->pSG);
1414 if(FAILED(hr)){
1415 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1416 break;
1418 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data
1419 hr = OLE_CALL_ARGS(chain->pSG, SetCallback, (ISampleGrabberCB *) chain->pCSGCB, 0); //we want to receive sample
1421 if(FAILED(hr)){
1422 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1423 break;
1425 hr = OLE_CALL_ARGS(chain->pSG, SetOneShot, FALSE); //... for all frames
1426 if(FAILED(hr)){
1427 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1428 break;
1430 hr = OLE_CALL_ARGS(chain->pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber
1431 if(FAILED(hr)){
1432 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1433 break;
1436 if(priv->tv_param->normalize_audio_chunks && chain->type==audio){
1437 set_buffer_preference(20,(WAVEFORMATEX*)(chain->arpmt[nFormatProbed]->pbFormat),chain->pCapturePin,chain->pSGIn);
1440 for(nFormatProbed=0; chain->arpmt[nFormatProbed]; nFormatProbed++)
1442 DisplayMediaType("Probing format", chain->arpmt[nFormatProbed]);
1443 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, chain->arpmt[nFormatProbed]); //set desired mediatype
1444 if(FAILED(hr)){
1445 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1446 continue;
1448 /* connecting filters together: VideoCapture --> SampleGrabber */
1449 hr = OLE_CALL_ARGS(priv->pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1450 if(FAILED(hr)){
1451 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr);
1452 continue;
1454 break;
1457 if(!chain->arpmt[nFormatProbed])
1459 mp_msg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to negotiate media format\n");
1460 hr = E_FAIL;
1461 break;
1464 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, chain->pmt);
1465 if(FAILED(hr))
1467 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_GetActualMediatypeFailed, (unsigned int)hr);
1470 if(priv->tv_param->hidden_video_renderer){
1471 IEnumFilters* pEnum;
1472 IBaseFilter* pFilter;
1474 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)chain->pCapturePin,NULL,NULL);
1476 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
1477 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
1478 LPVIDEOWINDOW pVideoWindow;
1479 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
1480 if (SUCCEEDED(hr))
1482 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
1483 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
1484 OLE_RELEASE_SAFE(pVideoWindow);
1486 OLE_RELEASE_SAFE(pFilter);
1488 OLE_RELEASE_SAFE(pEnum);
1489 }else
1491 #if 0
1493 Code below is disabled, because terminating chain with NullRenderer leads to jerky video.
1494 Perhaps, this happens because NullRenderer filter discards each received
1495 frame while discarded frames causes live source filter to dramatically reduce frame rate.
1497 /* adding sink for video stream */
1498 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR);
1499 if(FAILED(hr)){
1500 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1501 break;
1503 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer");
1504 if(FAILED(hr)){
1505 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1506 break;
1508 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn);
1509 if(FAILED(hr)){
1510 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr);
1511 break;
1514 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection
1516 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){
1517 /* connecting filters together: SampleGrabber --> NullRenderer */
1518 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn);
1519 if(FAILED(hr)){
1520 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr);
1521 break;
1524 #endif
1527 hr = S_OK;
1528 } while(0);
1530 OLE_RELEASE_SAFE(pSGOut);
1531 OLE_RELEASE_SAFE(pNR);
1532 OLE_RELEASE_SAFE(pNRIn);
1534 return hr;
1538 * \brief configures crossbar for grabbing video stream from given input
1540 * \param priv driver's private data
1541 * \param input index of available video input to get data from
1543 * \return TVI_CONTROL_TRUE success
1544 * \return TVI_CONTROL_FALSE error
1546 static int set_crossbar_input(priv_t * priv, int input)
1548 HRESULT hr;
1549 int i, nVideoDecoder, nAudioDecoder;
1550 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins,
1551 lInputPins;
1553 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n");
1554 if (!priv->pCrossbar || input < 0
1555 || input >= tv_available_inputs_count)
1556 return TVI_CONTROL_FALSE;
1558 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins);
1560 lInput = tv_available_inputs[input];
1562 if (lInput < 0 || lInput >= lInputPins)
1563 return TVI_CONTROL_FALSE;
1565 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput,
1566 &lInputRelated, &lPhysicalType);
1568 nVideoDecoder = nAudioDecoder = -1;
1569 for (i = 0; i < lOutputPins; i++) {
1570 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i,
1571 &lRelated, &lPhysicalType);
1572 if (lPhysicalType == PhysConn_Video_VideoDecoder)
1573 nVideoDecoder = i;
1574 if (lPhysicalType == PhysConn_Audio_AudioDecoder)
1575 nAudioDecoder = i;
1577 if (nVideoDecoder >= 0) {
1578 //connecting given input with video decoder
1579 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput);
1580 if (hr != S_OK) {
1581 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputVideoDecoder, (unsigned int)hr);
1582 return TVI_CONTROL_FALSE;
1585 if (nAudioDecoder >= 0 && lInputRelated >= 0) {
1586 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder,
1587 lInputRelated);
1588 if (hr != S_OK) {
1589 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputAudioDecoder, (unsigned int)hr);
1590 return TVI_CONTROL_FALSE;
1593 return TVI_CONTROL_TRUE;
1597 * \brief adjusts video control (hue,saturation,contrast,brightess)
1599 * \param priv driver's private data
1600 * \param control which control to adjust
1601 * \param value new value for control (0-100)
1603 * \return TVI_CONTROL_TRUE success
1604 * \return TVI_CONTROL_FALSE error
1606 static int set_control(priv_t * priv, int control, int value)
1608 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1609 HRESULT hr;
1611 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n");
1612 if (value < -100 || value > 100 || !priv->pVideoProcAmp)
1613 return TVI_CONTROL_FALSE;
1615 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1616 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1617 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual)
1618 return TVI_CONTROL_FALSE;
1620 lValue = lMin + (value + 100) * (lMax - lMin) / 200;
1622 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256
1624 if (lStepping > lMax) {
1625 mp_msg(MSGT_TV, MSGL_DBG3,
1626 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n",
1627 lStepping, lMax,control);
1628 lStepping = 1;
1630 lValue -= lValue % lStepping;
1631 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue,
1632 VideoProcAmp_Flags_Manual);
1633 if (FAILED(hr))
1634 return TVI_CONTROL_FALSE;
1636 return TVI_CONTROL_TRUE;
1640 * \brief get current value of video control (hue,saturation,contrast,brightess)
1642 * \param priv driver's private data
1643 * \param control which control to adjust
1644 * \param pvalue address of variable thar receives current value
1646 * \return TVI_CONTROL_TRUE success
1647 * \return TVI_CONTROL_FALSE error
1649 static int get_control(priv_t * priv, int control, int *pvalue)
1651 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1652 HRESULT hr;
1654 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n");
1655 if (!pvalue || !priv->pVideoProcAmp)
1656 return TVI_CONTROL_FALSE;
1658 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1659 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1660 if (FAILED(hr))
1661 return TVI_CONTROL_FALSE;
1662 if (lMin == lMax) {
1663 *pvalue = lMin;
1664 return TVI_CONTROL_TRUE;
1667 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags);
1668 if (FAILED(hr))
1669 return TVI_CONTROL_FALSE;
1671 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100;
1673 return TVI_CONTROL_TRUE;
1677 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps
1678 * \param fcc FourCC code for video format
1679 * \param width picture width
1680 * \param height pciture height
1681 * \param fps frames per second (required for bitrate calculation)
1683 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise
1685 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps)
1687 int i;
1688 AM_MEDIA_TYPE mt;
1689 VIDEOINFOHEADER vHdr;
1691 /* Check given fcc in lookup table*/
1692 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */;
1693 if(!img_fmt_list[i].fmt)
1694 return NULL;
1696 memset(&mt, 0, sizeof(AM_MEDIA_TYPE));
1697 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER));
1699 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader);
1700 vHdr.bmiHeader.biWidth = width;
1701 vHdr.bmiHeader.biHeight = height;
1702 //FIXME: is biPlanes required too?
1703 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes;
1704 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits;
1705 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression;
1706 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8;
1707 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps;
1709 mt.pbFormat = (char*)&vHdr;
1710 mt.cbFormat = sizeof(vHdr);
1712 mt.majortype = MEDIATYPE_Video;
1713 mt.subtype = *img_fmt_list[i].subtype;
1714 mt.formattype = FORMAT_VideoInfo;
1716 mt.bFixedSizeSamples = 1;
1717 mt.bTemporalCompression = 0;
1718 mt.lSampleSize = vHdr.bmiHeader.biSizeImage;
1720 return CreateMediaType(&mt);
1724 * \brief extracts fcc,width,height from AM_MEDIA_TYPE
1726 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1727 * \param pfcc address of variable that receives FourCC
1728 * \param pwidth address of variable that receives width
1729 * \param pheight address of variable that recevies height
1731 * \return 1 if data extracted successfully, 0 - otherwise
1733 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc,
1734 int *pwidth, int *pheight)
1736 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n");
1737 if (!pmt)
1738 return 0;
1739 if (!pmt->pbFormat)
1740 return 0;
1741 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0)
1742 return 0;
1743 if (pfcc)
1744 *pfcc = subtype2imgfmt(&(pmt->subtype));
1745 if (pwidth)
1746 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth;
1747 if (pheight)
1748 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight;
1749 return 1;
1753 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE
1755 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1756 * \param pfcc address of variable that receives samplerate
1757 * \param pwidth address of variable that receives number of bits per sample
1758 * \param pheight address of variable that recevies number of channels
1760 * \return 1 if data extracted successfully, 0 - otherwise
1762 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate,
1763 int *pbits, int *pchannels)
1765 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n");
1766 if (!pmt)
1767 return 0;
1768 if (!pmt->pbFormat)
1769 return 0;
1770 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1771 return 0;
1772 if (psamplerate)
1773 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec;
1774 if (pbits)
1775 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample;
1776 if (pchannels)
1777 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels;
1778 return 1;
1782 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels
1784 * \param pmt pointer to AM_MEDIA_TYPE for check
1785 * \param samplerate audio samplerate
1786 * \param bits bits per sample
1787 * \param channels number of audio channels
1789 * \return 1 if AM_MEDIA_TYPE compatible
1790 * \return 0 if not
1792 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate,
1793 int bits, int channels)
1795 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n");
1796 if (!pmt)
1797 return 0;
1798 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0)
1799 return 0;
1800 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0)
1801 return 0;
1802 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1803 return 0;
1804 if (!pmt->pbFormat)
1805 return 0;
1806 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate)
1807 return 0;
1808 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits)
1809 return 0;
1810 if (channels > 0
1811 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels)
1812 return 0;
1814 return 1;
1818 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height
1820 * \param pmt pointer to AM_MEDIA_TYPE for check
1821 * \param fcc FourCC (compression)
1822 * \param width width of picture
1823 * \param height height of picture
1825 * \return 1 if AM_MEDIA_TYPE compatible
1826 & \return 0 if not
1828 * \note
1829 * width and height are currently not used
1831 * \todo
1832 * add width/height check
1834 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width,
1835 int height)
1837 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n");
1838 if (!pmt)
1839 return 0;
1840 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0)
1841 return 0;
1842 if (subtype2imgfmt(&(pmt->subtype)) != fcc)
1843 return 0;
1844 return 1;
1848 * \brief converts DirectShow subtype to MPlayer's IMGFMT
1850 * \param subtype DirectShow subtype for video format
1852 * \return MPlayer's IMGFMT or 0 if error occured
1854 static int subtype2imgfmt(const GUID * subtype)
1856 int i;
1857 for (i = 0; img_fmt_list[i].fmt; i++) {
1858 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0)
1859 return img_fmt_list[i].fmt;
1861 return 0;
1865 * \brief prints filter name and it pins
1867 * \param pFilter - IBaseFilter to get data from
1869 * \return S_OK if success, error code otherwise
1871 static HRESULT show_filter_info(IBaseFilter * pFilter)
1873 char tmp[200];
1874 FILTER_INFO fi;
1875 LPENUMPINS pEnum = 0;
1876 IPin *pPin = 0;
1877 PIN_DIRECTION ThisPinDir;
1878 PIN_INFO pi;
1879 HRESULT hr;
1880 int i;
1882 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n");
1883 memset(&fi, 0, sizeof(fi));
1884 memset(tmp, 0, 200);
1886 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi);
1887 OLE_RELEASE_SAFE(fi.pGraph);
1888 wtoa(fi.achName, tmp, 200);
1889 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:",
1890 pFilter, tmp, fi.pGraph);
1891 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum);
1892 if (FAILED(hr))
1893 return hr;
1894 i = 0;
1895 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1896 memset(&pi, 0, sizeof(pi));
1897 memset(tmp, 0, 200);
1898 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1899 if (ThisPinDir == PINDIR_OUTPUT) {
1900 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1901 wtoa(pi.achName, tmp, 200);
1902 OLE_RELEASE_SAFE(pi.pFilter);
1903 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp);
1904 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1905 mp_msg(MSGT_TV, MSGL_DBG2, ";");
1906 OLE_RELEASE_SAFE(pPin);
1907 i++;
1910 mp_msg(MSGT_TV, MSGL_DBG2, "\n");
1911 OLE_RELEASE_SAFE(pEnum);
1912 return S_OK;
1916 * \brief gets device's frendly in ANSI encoding
1918 * \param pM IMoniker interface, got in enumeration process
1919 * \param category device category
1921 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise
1923 static int get_device_name(IMoniker * pM, char *pBuf, int nLen)
1925 HRESULT hr;
1926 VARIANT var;
1927 IPropertyBag *pPropBag;
1928 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag);
1929 if (FAILED(hr)) {
1930 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n");
1931 return TVI_CONTROL_FALSE;
1933 var.vt = VT_BSTR;
1934 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var,
1935 NULL);
1936 if (FAILED(hr)) {
1937 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var,
1938 NULL);
1940 OLE_RELEASE_SAFE(pPropBag);
1941 if (SUCCEEDED(hr)) {
1942 wtoa(var.bstrVal, pBuf, nLen);
1943 return TVI_CONTROL_TRUE;
1945 return TVI_CONTROL_FALSE;
1949 * \brief find capture device at given index
1951 * \param index device index to search for (-1 mean only print available)
1952 * \param category device category
1954 * \return IBaseFilter interface for capture device with given index
1956 * Sample values for category:
1957 * CLSID_VideoInputDeviceCategory - Video Capture Sources
1958 * CLSID_AudioInputDeviceCategory - Audio Capture Sources
1959 * See DirectShow SDK documentation for other possible values
1961 static IBaseFilter *find_capture_device(int index, REFCLSID category)
1963 IBaseFilter *pFilter = NULL;
1964 ICreateDevEnum *pDevEnum = NULL;
1965 IEnumMoniker *pClassEnum = NULL;
1966 IMoniker *pM;
1967 HRESULT hr;
1968 ULONG cFetched;
1969 int i;
1970 char tmp[DEVICE_NAME_MAX_LEN + 1];
1971 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL,
1972 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
1973 (void *) &pDevEnum);
1974 if (FAILED(hr)) {
1975 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n");
1976 return NULL;
1979 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0);
1980 OLE_RELEASE_SAFE(pDevEnum);
1981 if (FAILED(hr)) {
1982 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n");
1983 return NULL;
1985 if (hr == S_FALSE) {
1986 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n");
1987 return NULL;
1990 OLE_CALL(pClassEnum,Reset);
1991 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) {
1992 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE)
1993 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableGetDeviceName, i);
1994 else
1995 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DeviceName, i, tmp);
1996 if (index != -1 && i == index) {
1997 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_UsingDevice, index, tmp);
1998 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter);
1999 if (FAILED(hr))
2000 pFilter = NULL;
2002 OLE_RELEASE_SAFE(pM);
2004 if (index != -1 && !pFilter) {
2005 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_DeviceNotFound,
2006 index);
2008 OLE_RELEASE_SAFE(pClassEnum);
2010 return pFilter;
2014 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps
2016 * \praram[in] chain chain data structure
2018 * \return S_OK success
2019 * \return E_POINTER one of parameters is NULL
2020 * \return E_FAIL required size of buffer is unknown for given media type
2021 * \return E_OUTOFMEMORY not enough memory
2022 * \return other error code from called methods
2024 * \remarks
2025 * last items of chain->arpmt and chain->arStreamCaps will be NULL
2027 static HRESULT get_available_formats_stream(chain_t *chain)
2029 AM_MEDIA_TYPE **arpmt;
2030 void **pBuf=NULL;
2032 HRESULT hr;
2033 int i, count, size;
2034 int done;
2036 mp_msg(MSGT_TV, MSGL_DBG4,
2037 "tvi_dshow: get_available_formats_stream called\n");
2039 if (!chain->pStreamConfig)
2040 return E_POINTER;
2042 hr=OLE_CALL_ARGS(chain->pStreamConfig, GetNumberOfCapabilities, &count, &size);
2043 if (FAILED(hr)) {
2044 mp_msg(MSGT_TV, MSGL_DBG4,
2045 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n");
2046 return hr;
2048 if (chain->type == video){
2049 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
2050 mp_msg(MSGT_TV, MSGL_DBG4,
2051 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2052 return E_FAIL;
2054 } else if (chain->type == audio){
2055 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) {
2056 mp_msg(MSGT_TV, MSGL_DBG4,
2057 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2058 return E_FAIL;
2060 } else {
2061 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_stream");
2062 return E_FAIL;
2064 done = 0;
2066 arpmt = (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2067 if (arpmt) {
2068 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2070 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2071 if (pBuf) {
2072 memset(pBuf, 0, (count + 1) * sizeof(void *));
2074 for (i = 0; i < count; i++) {
2075 pBuf[i] = malloc(size);
2077 if (!pBuf[i])
2078 break;
2080 hr = OLE_CALL_ARGS(chain->pStreamConfig, GetStreamCaps, i,
2081 &(arpmt[i]), pBuf[i]);
2082 if (FAILED(hr))
2083 break;
2085 if (i == count) {
2086 chain->arpmt = arpmt;
2087 chain->arStreamCaps = pBuf;
2088 done = 1;
2092 if (!done) {
2093 for (i = 0; i < count; i++) {
2094 if (pBuf && pBuf[i])
2095 free(pBuf[i]);
2096 if (arpmt && arpmt[i])
2097 DeleteMediaType(arpmt[i]);
2099 if (pBuf)
2100 free(pBuf);
2101 if (arpmt)
2102 free(arpmt);
2103 if (hr != S_OK) {
2104 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n");
2105 return hr;
2106 } else
2107 return E_OUTOFMEMORY;
2109 return S_OK;
2113 * \brief returns allocates an array and store available media formats for given pin type to it
2115 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder
2116 * \param chain chain data structure
2118 * \return S_OK success
2119 * \return E_POINTER one of given pointers is null
2120 * \return apropriate error code otherwise
2122 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder,
2123 chain_t *chain)
2125 IEnumMediaTypes *pEnum;
2126 int i, count, size;
2127 ULONG cFetched;
2128 AM_MEDIA_TYPE *pmt;
2129 HRESULT hr;
2130 void **pBuf;
2131 AM_MEDIA_TYPE **arpmt; //This will be real array
2132 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps;
2133 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps;
2134 int p1, p2, p3;
2136 mp_msg(MSGT_TV, MSGL_DBG4,
2137 "tvi_dshow: get_available_formats_pin called\n");
2138 if (!pBuilder || !chain->pCaptureFilter)
2139 return E_POINTER;
2141 if (!chain->pCapturePin)
2143 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2144 (IUnknown *) chain->pCaptureFilter,
2145 PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE,
2146 chain->majortype, FALSE, 0, &chain->pCapturePin);
2148 if (!chain->pCapturePin)
2149 return E_POINTER;
2151 if (chain->type == video) {
2152 size = sizeof(VIDEO_STREAM_CONFIG_CAPS);
2153 } else if (chain->type == audio) {
2154 size = sizeof(AUDIO_STREAM_CONFIG_CAPS);
2155 } else {
2156 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_pin");
2157 return E_FAIL;
2160 hr = OLE_CALL_ARGS(chain->pCapturePin, EnumMediaTypes, &pEnum);
2161 if (FAILED(hr)) {
2162 mp_msg(MSGT_TV, MSGL_DBG4,
2163 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n");
2164 return hr;
2166 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) {
2167 if (!pmt)
2168 break;
2170 OLE_CALL(pEnum,Reset);
2172 count = i;
2173 arpmt =
2174 (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2175 if (!arpmt)
2176 return E_OUTOFMEMORY;
2177 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2179 for (i = 0;
2180 i < count
2181 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK;
2182 i++);
2184 OLE_RELEASE_SAFE(pEnum);
2187 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2188 if (!pBuf) {
2189 for (i = 0; i < count; i++)
2190 if (arpmt[i])
2191 DeleteMediaType(arpmt[i]);
2192 free(arpmt);
2193 return E_OUTOFMEMORY;
2195 memset(pBuf, 0, (count + 1) * sizeof(void *));
2197 for (i = 0; i < count; i++) {
2198 pBuf[i] = malloc(size);
2199 if (!pBuf[i])
2200 break;
2201 memset(pBuf[i], 0, size);
2203 if (chain->type == video) {
2204 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i];
2205 extract_video_format(arpmt[i], NULL, &p1, &p2);
2206 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx =
2208 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy =
2210 } else {
2211 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i];
2212 extract_audio_format(arpmt[i], &p1, &p2, &p3);
2213 pAudioCaps->MaximumSampleFrequency =
2214 pAudioCaps->MinimumSampleFrequency = p1;
2215 pAudioCaps->MaximumBitsPerSample =
2216 pAudioCaps->MinimumBitsPerSample = p2;
2217 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3;
2221 if (i != count) {
2222 for (i = 0; i < count; i++) {
2223 if (arpmt[i])
2224 DeleteMediaType(arpmt[i]);
2225 if (pBuf[i])
2226 free(pBuf[i]);
2228 free(arpmt);
2229 free(pBuf);
2230 return E_OUTOFMEMORY;
2232 chain->arpmt = arpmt;
2233 chain->arStreamCaps = pBuf;
2235 return S_OK;
2239 *---------------------------------------------------------------------------------------
2241 * Public methods
2243 *---------------------------------------------------------------------------------------
2246 * \brief fills given buffer with audio data (usually one block)
2248 * \param priv driver's private data structure
2249 * \param buffer buffer to store data to
2250 * \param len buffer's size in bytes (usually one block size)
2252 * \return audio pts if audio present, 1 - otherwise
2254 static double grab_audio_frame(priv_t * priv, char *buffer, int len)
2256 int bytes = 0;
2257 int i;
2258 double pts;
2259 grabber_ringbuffer_t *rb = priv->chains[1]->rbuf;
2260 grabber_ringbuffer_t *vrb = priv->chains[0]->rbuf;
2262 if (!rb || !rb->ringbuffer)
2263 return 1;
2265 if(vrb && vrb->tStart<0){
2266 memset(buffer,0,len);
2267 return 0;
2269 if(vrb && rb->tStart<0)
2270 rb->tStart=vrb->tStart;
2272 if (len < rb->blocksize)
2273 bytes = len;
2274 else
2275 bytes = rb->blocksize;
2277 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n",
2278 rb->count, len);
2279 if(!rb->count){
2280 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2281 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2282 if(!rb->count){
2283 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2284 return 0;
2286 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2289 EnterCriticalSection(rb->pMutex);
2290 pts=rb->dpts[rb->head]-rb->tStart;
2291 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2292 rb->head = (rb->head + 1) % rb->buffersize;
2293 rb->count--;
2294 LeaveCriticalSection(rb->pMutex);
2295 return pts;
2299 * \brief returns audio frame size
2301 * \param priv driver's private data structure
2303 * \return audio block size if audio enabled and 1 - otherwise
2305 static int get_audio_framesize(priv_t * priv)
2307 if (!priv->chains[1]->rbuf)
2308 return 1; //no audio
2309 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->chains[1]->rbuf->blocksize);
2310 return priv->chains[1]->rbuf->blocksize;
2313 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp)
2315 if(!priv || !ptsp)
2316 return TVI_CONTROL_FALSE;
2318 //STUBS!!!
2319 ptsp->interlaced=0;
2320 ptsp->offset=256;
2322 ptsp->sampling_rate=27e6;
2323 ptsp->samples_per_line=720;
2325 ptsp->count[0]=16;
2326 ptsp->count[1]=16;
2327 //END STUBS!!!
2328 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]);
2330 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",
2331 ptsp->sampling_rate,
2332 ptsp->offset,
2333 ptsp->samples_per_line,
2334 ptsp->interlaced?"Yes":"No",
2335 ptsp->count[0],
2336 ptsp->count[1]);
2338 return TVI_CONTROL_TRUE;
2341 static void vbi_grabber(priv_t* priv)
2343 grabber_ringbuffer_t *rb = priv->chains[2]->rbuf;
2344 int i;
2345 unsigned char* buf;
2346 if (!rb || !rb->ringbuffer)
2347 return;
2349 buf=calloc(1,rb->blocksize);
2350 for(i=0; i<23 && rb->count; i++){
2351 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize);
2352 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf);
2353 rb->head = (rb->head + 1) % rb->buffersize;
2354 rb->count--;
2356 free(buf);
2360 * \brief fills given buffer with video data (usually one frame)
2362 * \param priv driver's private data structure
2363 * \param buffer buffer to store data to
2364 * \param len buffer's size in bytes (usually one frame size)
2366 * \return frame size if video present, 0 - otherwise
2368 static double grab_video_frame(priv_t * priv, char *buffer, int len)
2370 int bytes = 0;
2371 int i;
2372 double pts;
2373 grabber_ringbuffer_t *rb = priv->chains[0]->rbuf;
2375 if (!rb || !rb->ringbuffer)
2376 return 1;
2377 if (len < rb->blocksize)
2378 bytes = len;
2379 else
2380 bytes = rb->blocksize;
2382 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n",
2383 rb->count, len);
2384 if(!rb->count){
2385 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2386 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2387 if(!rb->count){
2388 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2389 return 0;
2391 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2393 EnterCriticalSection(rb->pMutex);
2394 if(rb->tStart<0)
2395 rb->tStart=rb->dpts[rb->head];
2396 pts=rb->dpts[rb->head]-rb->tStart;
2397 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2398 rb->head = (rb->head + 1) % rb->buffersize;
2399 rb->count--;
2400 LeaveCriticalSection(rb->pMutex);
2402 vbi_grabber(priv);
2403 return pts;
2407 * \brief returns frame size
2409 * \param priv driver's private data structure
2411 * \return frame size if video present, 0 - otherwise
2413 static int get_video_framesize(priv_t * priv)
2415 // if(!priv->pmtVideo) return 1; //no video
2416 // return priv->pmtVideo->lSampleSize;
2417 if (!priv->chains[0]->rbuf)
2418 return 1; //no video
2419 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->chains[0]->rbuf->blocksize);
2420 return priv->chains[0]->rbuf->blocksize;
2424 * \brief calculate audio buffer size
2425 * \param video_buf_size size of video buffer in bytes
2426 * \param video_bitrate video bit rate
2427 * \param audio_bitrate audio bit rate
2428 * \return audio buffer isze in bytes
2430 * \remarks length of video buffer and resulted audio buffer calculated in
2431 * seconds will be the same.
2433 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate)
2435 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate);
2436 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n",
2437 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size);
2438 return audio_buf_size;
2442 * \brief common chain initialization routine
2443 * \param chain chain data structure
2445 * \note pCaptureFilter member should be initialized before call to this routine
2447 static HRESULT init_chain_common(ICaptureGraphBuilder2 *pBuilder, chain_t *chain)
2449 HRESULT hr;
2450 int i;
2452 if(!chain->pCaptureFilter)
2453 return E_POINTER;
2455 show_filter_info(chain->pCaptureFilter);
2457 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2458 (IUnknown *) chain->pCaptureFilter,
2459 PINDIR_OUTPUT, chain->pin_category,
2460 chain->majortype, FALSE, 0, &chain->pCapturePin);
2462 if (FAILED(hr)) {
2463 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
2464 return hr;
2467 hr = OLE_CALL_ARGS(pBuilder, FindInterface,
2468 chain->pin_category,
2469 chain->majortype,
2470 chain->pCaptureFilter,
2471 &IID_IAMStreamConfig,
2472 (void **) &(chain->pStreamConfig));
2473 if (FAILED(hr))
2474 chain->pStreamConfig = NULL;
2477 Getting available video formats (last pointer in array will be NULL)
2478 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as
2479 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default
2480 min/max values.
2482 hr = get_available_formats_stream(chain);
2483 if (FAILED(hr)) {
2484 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr);
2485 hr = get_available_formats_pin(pBuilder, chain);
2486 if(FAILED(hr)){
2487 return hr;
2490 chain->nFormatUsed = 0;
2492 //If argument to CreateMediaType is NULL then result will be NULL too.
2493 chain->pmt = CreateMediaType(chain->arpmt[0]);
2495 for (i = 0; chain->arpmt[i]; i++)
2496 DisplayMediaType("Available format", chain->arpmt[i]);
2498 return S_OK;
2501 * \brief build video stream chain in graph
2502 * \param priv private data structure
2504 * \return S_OK if chain was built successfully, apropriate error code otherwise
2506 static HRESULT build_video_chain(priv_t *priv)
2508 HRESULT hr;
2510 if(priv->chains[0]->rbuf)
2511 return S_OK;
2513 if (priv->chains[0]->pStreamConfig) {
2514 hr = OLE_CALL_ARGS(priv->chains[0]->pStreamConfig, SetFormat, priv->chains[0]->pmt);
2515 if (FAILED(hr)) {
2516 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectVideoFormat, (unsigned int)hr);
2520 priv->chains[0]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2521 if(!priv->chains[0]->rbuf)
2522 return E_OUTOFMEMORY;
2524 if (priv->tv_param->buffer_size >= 0) {
2525 priv->chains[0]->rbuf->buffersize = priv->tv_param->buffer_size;
2526 } else {
2527 priv->chains[0]->rbuf->buffersize = 16;
2530 priv->chains[0]->rbuf->buffersize *= 1024 * 1024;
2531 hr=build_sub_graph(priv, priv->chains[0], &PIN_CATEGORY_CAPTURE);
2532 if(FAILED(hr)){
2533 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVideoSubGraph,(unsigned int)hr);
2534 return hr;
2536 return S_OK;
2540 * \brief build audio stream chain in graph
2541 * \param priv private data structure
2543 * \return S_OK if chain was built successfully, apropriate error code otherwise
2545 static HRESULT build_audio_chain(priv_t *priv)
2547 HRESULT hr;
2549 if(priv->chains[1]->rbuf)
2550 return S_OK;
2552 if(priv->immediate_mode)
2553 return S_OK;
2555 if (priv->chains[1]->pStreamConfig) {
2556 hr = OLE_CALL_ARGS(priv->chains[1]->pStreamConfig, SetFormat,
2557 priv->chains[1]->pmt);
2558 if (FAILED(hr)) {
2559 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectAudioFormat, (unsigned int)hr);
2563 if(priv->chains[1]->pmt){
2564 priv->chains[1]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2565 if(!priv->chains[1]->rbuf)
2566 return E_OUTOFMEMORY;
2568 /* let the audio buffer be the same size (in seconds) than video one */
2569 priv->chains[1]->rbuf->buffersize=audio_buf_size_from_video(
2570 priv->chains[0]->rbuf->buffersize,
2571 (((VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat)->dwBitRate),
2572 (((WAVEFORMATEX *) (priv->chains[1]->pmt->pbFormat))->nAvgBytesPerSec));
2574 hr=build_sub_graph(priv, priv->chains[1],&PIN_CATEGORY_CAPTURE);
2575 if(FAILED(hr)){
2576 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildAudioSubGraph,(unsigned int)hr);
2577 return 0;
2580 return S_OK;
2584 * \brief build VBI stream chain in graph
2585 * \param priv private data structure
2587 * \return S_OK if chain was built successfully, apropriate error code otherwise
2589 static HRESULT build_vbi_chain(priv_t *priv)
2591 HRESULT hr;
2593 if(priv->chains[2]->rbuf)
2594 return S_OK;
2596 if(priv->tv_param->teletext.device)
2598 priv->chains[2]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2599 if(!priv->chains[2]->rbuf)
2600 return E_OUTOFMEMORY;
2602 init_ringbuffer(priv->chains[2]->rbuf,24,priv->tsp.bufsize);
2604 hr=build_sub_graph(priv, priv->chains[2],&PIN_CATEGORY_VBI);
2605 if(FAILED(hr)){
2606 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVBISubGraph,(unsigned int)hr);
2607 return 0;
2610 return S_OK;
2614 * \brief playback/capture real start
2616 * \param priv driver's private data structure
2618 * \return 1 if success, 0 - otherwise
2620 * TODO: move some code from init() here
2622 static int start(priv_t * priv)
2624 HRESULT hr;
2626 hr = build_video_chain(priv);
2627 if(FAILED(hr))
2628 return 0;
2630 hr = build_audio_chain(priv);
2631 if(FAILED(hr))
2632 return 0;
2634 hr = build_vbi_chain(priv);
2635 if(FAILED(hr))
2636 return 0;
2639 Graph is ready to capture. Starting graph.
2641 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2642 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n");
2643 usec_sleep(10000000);
2644 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n");
2646 if (!priv->pMediaControl) {
2647 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)E_POINTER);
2648 return 0;
2650 hr = OLE_CALL(priv->pMediaControl, Run);
2651 if (FAILED(hr)) {
2652 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableStartGraph, (unsigned int)hr);
2653 return 0;
2655 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n");
2656 priv->state = 1;
2658 return 1;
2662 * \brief driver initialization
2664 * \param priv driver's private data structure
2666 * \return 1 if success, 0 - otherwise
2668 static int init(priv_t * priv)
2670 HRESULT hr;
2671 int result = 0;
2672 long lInput, lTunerInput;
2673 IEnumFilters *pEnum;
2674 IBaseFilter *pFilter;
2675 IPin *pVPOutPin;
2676 int i;
2678 priv->state=0;
2680 CoInitialize(NULL);
2682 for(i=0; i<3;i++)
2683 priv->chains[i] = calloc(1, sizeof(chain_t));
2685 priv->chains[0]->type=video;
2686 priv->chains[0]->majortype=&MEDIATYPE_Video;
2687 priv->chains[0]->pin_category=&PIN_CATEGORY_CAPTURE;
2688 priv->chains[1]->type=audio;
2689 priv->chains[1]->majortype=&MEDIATYPE_Audio;
2690 priv->chains[1]->pin_category=&PIN_CATEGORY_CAPTURE;
2691 priv->chains[2]->type=vbi;
2692 priv->chains[2]->majortype=&MEDIATYPE_VBI;
2693 priv->chains[2]->pin_category=&PIN_CATEGORY_VBI;
2696 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL,
2697 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2698 (void **) &priv->pGraph);
2699 if(FAILED(hr)){
2700 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr);
2701 break;
2703 //Debug
2704 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2705 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister));
2708 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL,
2709 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2,
2710 (void **) &priv->pBuilder);
2711 if(FAILED(hr)){
2712 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr);
2713 break;
2716 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph);
2717 if(FAILED(hr)){
2718 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr);
2719 break;
2722 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n");
2723 priv->chains[0]->pCaptureFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory);
2724 if(!priv->chains[0]->pCaptureFilter){
2725 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoVideoCaptureDevice);
2726 break;
2728 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[0]->pCaptureFilter, NULL);
2729 if(FAILED(hr)){
2730 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2731 break;
2733 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n");
2734 if (priv->adev_index != -1) {
2735 priv->chains[1]->pCaptureFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices
2736 if(!priv->chains[1]->pCaptureFilter){
2737 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoAudioCaptureDevice);
2738 break;
2741 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[1]->pCaptureFilter, NULL);
2742 if(FAILED(hr)){
2743 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2744 break;
2746 } else
2747 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[1]->pCaptureFilter);
2749 /* increase refrence counter for capture filter ad store pointer into vbi chain structure too */
2750 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[2]->pCaptureFilter);
2752 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp);
2753 if (FAILED(hr) && hr != E_NOINTERFACE)
2754 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr);
2756 if (hr != S_OK) {
2757 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_VideoAdjustigNotSupported);
2758 priv->pVideoProcAmp = NULL;
2761 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2762 &PIN_CATEGORY_CAPTURE,
2763 priv->chains[0]->majortype,
2764 priv->chains[0]->pCaptureFilter,
2765 &IID_IAMCrossbar, (void **) &(priv->pCrossbar));
2766 if (FAILED(hr)) {
2767 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_SelectingInputNotSupported);
2768 priv->pCrossbar = NULL;
2771 if (priv->tv_param->amode >= 0) {
2772 IAMTVAudio *pTVAudio;
2773 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->chains[0]->pCaptureFilter,&IID_IAMTVAudio, (void *) &pTVAudio);
2774 if (hr == S_OK) {
2775 switch (priv->tv_param->amode) {
2776 case 0:
2777 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO);
2778 break;
2779 case 1:
2780 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO);
2781 break;
2782 case 2:
2783 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2784 AMTVAUDIO_MODE_LANG_A);
2785 break;
2786 case 3:
2787 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2788 AMTVAUDIO_MODE_LANG_B);
2789 break;
2791 OLE_RELEASE_SAFE(pTVAudio);
2792 if (FAILED(hr))
2793 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_UnableSetAudioMode, priv->tv_param->amode,(unsigned int)hr);
2797 // Video chain initialization
2798 hr = init_chain_common(priv->pBuilder, priv->chains[0]);
2799 if(FAILED(hr))
2800 break;
2803 Audio chain initialization
2804 Since absent audio stream is not fatal,
2805 at least one NULL pointer should be kept in format arrays
2806 (to avoid another additional check everywhere for array presence).
2808 hr = init_chain_common(priv->pBuilder, priv->chains[1]);
2809 if(FAILED(hr))
2811 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize audio chain (Error:0x%x). Audio disabled\n", (unsigned long)hr);
2812 priv->chains[1]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2813 priv->chains[1]->arStreamCaps=calloc(1, sizeof(void*));
2817 VBI chain initialization
2818 Since absent VBI stream is not fatal,
2819 at least one NULL pointer should be kept in format arrays
2820 (to avoid another additional check everywhere for array presence).
2822 hr = init_chain_common(priv->pBuilder, priv->chains[2]);
2823 if(FAILED(hr))
2825 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize VBI chain (Error:0x%x). Teletext disabled\n", (unsigned long)hr);
2826 priv->chains[2]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2827 priv->chains[2]->arStreamCaps=calloc(1, sizeof(void*));
2830 if (!priv->chains[0]->pStreamConfig)
2831 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_ChangingWidthHeightNotSupported);
2833 if (!priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]
2834 || !extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
2835 &(priv->fcc), &(priv->width),
2836 &(priv->height))) {
2837 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingVideoFormatStruct);
2838 break;
2841 if (priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]) {
2842 if (!extract_audio_format(priv->chains[1]->pmt, &(priv->samplerate), NULL, NULL)) {
2843 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingAudioFormatStruct);
2844 DisplayMediaType("audio format failed",priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]);
2845 break;
2849 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl);
2850 if(FAILED(hr)){
2851 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)hr);
2852 break;
2854 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2855 &PIN_CATEGORY_CAPTURE, NULL,
2856 priv->chains[0]->pCaptureFilter,
2857 &IID_IAMTVTuner, (void **) &(priv->pTVTuner));
2859 if (!priv->pTVTuner) {
2860 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr);
2863 // shows Tuner capabilities
2864 get_capabilities(priv);
2866 if (priv->pTVTuner) {
2867 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode,
2868 chanlist2country(priv->tv_param->chanlist));
2869 if(FAILED(hr)){
2870 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr);
2873 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV);
2874 if(FAILED(hr)){
2875 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr);
2876 break;
2879 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
2880 if(FAILED(hr)){
2881 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr);
2882 break;
2885 /* small hack */
2886 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna;
2888 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput);
2889 if(FAILED(hr)){
2890 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr);
2891 break;
2897 for VIVO cards we should check if preview pin is available on video capture device.
2898 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter.
2899 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph
2901 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
2902 (IUnknown *) priv->chains[0]->pCaptureFilter,
2903 PINDIR_OUTPUT,
2904 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE,
2905 0, (IPin **) & pVPOutPin);
2906 if (SUCCEEDED(hr)) {
2907 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin);
2908 OLE_RELEASE_SAFE(pVPOutPin);
2910 if (FAILED(hr)) {
2911 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableTerminateVPPin, (unsigned int)hr);
2912 break;
2916 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
2917 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
2918 LPVIDEOWINDOW pVideoWindow;
2919 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
2920 if (SUCCEEDED(hr))
2922 if(priv->tv_param->hidden_vp_renderer){
2923 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
2924 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
2925 }else
2927 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter);
2929 OLE_RELEASE_SAFE(pVideoWindow);
2931 OLE_RELEASE_SAFE(pFilter);
2933 OLE_RELEASE_SAFE(pEnum);
2934 if(priv->tv_param->system_clock)
2936 LPREFERENCECLOCK rc;
2937 IBaseFilter* pBF;
2938 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL,
2939 CLSCTX_INPROC_SERVER, &IID_IReferenceClock,
2940 (void *) &rc);
2942 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF);
2943 OLE_CALL_ARGS(pBF,SetSyncSource,rc);
2945 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE)
2946 break;
2947 result = 1;
2948 } while(0);
2950 if (!result){
2951 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_GraphInitFailure);
2952 uninit(priv);
2954 return result;
2958 * \brief chain uninitialization
2959 * \param chain chain data structure
2961 static void destroy_chain(chain_t *chain)
2963 int i;
2965 if(!chain)
2966 return;
2968 OLE_RELEASE_SAFE(chain->pStreamConfig);
2969 OLE_RELEASE_SAFE(chain->pCaptureFilter);
2970 OLE_RELEASE_SAFE(chain->pCSGCB);
2971 OLE_RELEASE_SAFE(chain->pCapturePin);
2972 OLE_RELEASE_SAFE(chain->pSGIn);
2973 OLE_RELEASE_SAFE(chain->pSG);
2974 OLE_RELEASE_SAFE(chain->pSGF);
2976 if (chain->pmt)
2977 DeleteMediaType(chain->pmt);
2979 if (chain->arpmt) {
2980 for (i = 0; chain->arpmt[i]; i++) {
2981 DeleteMediaType(chain->arpmt[i]);
2983 free(chain->arpmt);
2986 if (chain->arStreamCaps) {
2987 for (i = 0; chain->arStreamCaps[i]; i++) {
2988 free(chain->arStreamCaps[i]);
2990 free(chain->arStreamCaps);
2993 if (chain->rbuf) {
2994 destroy_ringbuffer(chain->rbuf);
2995 free(chain->rbuf);
2996 chain->rbuf = NULL;
2998 free(chain);
3001 * \brief driver uninitialization
3003 * \param priv driver's private data structure
3005 * \return always 1
3007 static int uninit(priv_t * priv)
3009 int i;
3010 if (!priv)
3011 return 1;
3012 //Debug
3013 if (priv->dwRegister) {
3014 RemoveFromRot(priv->dwRegister);
3016 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1);
3017 //stop audio grabber thread
3019 if (priv->state && priv->pMediaControl) {
3020 OLE_CALL(priv->pMediaControl, Stop);
3022 OLE_RELEASE_SAFE(priv->pMediaControl);
3023 priv->state = 0;
3025 if (priv->pGraph) {
3026 if (priv->chains[0]->pCaptureFilter)
3027 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[0]->pCaptureFilter);
3028 if (priv->chains[1]->pCaptureFilter)
3029 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[1]->pCaptureFilter);
3031 OLE_RELEASE_SAFE(priv->pCrossbar);
3032 OLE_RELEASE_SAFE(priv->pVideoProcAmp);
3033 OLE_RELEASE_SAFE(priv->pGraph);
3034 OLE_RELEASE_SAFE(priv->pBuilder);
3035 if(priv->freq_table){
3036 priv->freq_table_len=-1;
3037 free(priv->freq_table);
3038 priv->freq_table=NULL;
3041 for(i=0; i<3;i++)
3043 destroy_chain(priv->chains[i]);
3044 priv->chains[i] = NULL;
3046 CoUninitialize();
3047 return 1;
3051 * \brief driver pre-initialization
3053 * \param device string, containing device name in form "x[.y]", where x is video capture device
3054 * (default: 0, first available); y (if given) sets audio capture device
3056 * \return 1 if success,0 - otherwise
3058 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param)
3060 tvi_handle_t *h;
3061 priv_t *priv;
3062 int a;
3064 h = new_handle();
3065 if (!h)
3066 return NULL;
3068 priv = h->priv;
3070 memset(priv, 0, sizeof(priv_t));
3071 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3072 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3073 priv->adev_index = -1;
3074 priv->freq_table_len=-1;
3075 priv->tv_param=tv_param;
3077 if (tv_param->device) {
3078 if (sscanf(tv_param->device, "%d", &a) == 1) {
3079 priv->dev_index = a;
3080 } else {
3081 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceParam, tv_param->device);
3082 free_handle(h);
3083 return NULL;
3085 if (priv->dev_index < 0) {
3086 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceIndex, a);
3087 free_handle(h);
3088 return NULL;
3091 if (tv_param->adevice) {
3092 if (sscanf(tv_param->adevice, "%d", &a) == 1) {
3093 priv->adev_index = a;
3094 } else {
3095 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceParam, tv_param->adevice);
3096 free_handle(h);
3097 return NULL;
3099 if (priv->dev_index < 0) {
3100 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceIndex, a);
3101 free_handle(h);
3102 return NULL;
3105 return h;
3109 * \brief driver's ioctl handler
3111 * \param priv driver's private data structure
3112 * \param cmd ioctl command
3113 * \param arg ioct command's parameter
3115 * \return TVI_CONTROL_TRUE if success
3116 * \return TVI_CONTROL_FALSE if failure
3117 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called
3119 static int control(priv_t * priv, int cmd, void *arg)
3121 switch (cmd) {
3122 /* need rewrite */
3123 case TVI_CONTROL_VID_SET_FORMAT:
3125 int fcc, i,j;
3126 void* tmp,*tmp2;
3127 int result = TVI_CONTROL_TRUE;
3129 if (priv->state)
3130 return TVI_CONTROL_FALSE;
3131 fcc = *(int *) arg;
3133 if(!priv->chains[0]->arpmt)
3134 return TVI_CONTROL_FALSE;
3135 for (i = 0; priv->chains[0]->arpmt[i]; i++)
3136 if (check_video_format
3137 (priv->chains[0]->arpmt[i], fcc, priv->width, priv->height))
3138 break;
3139 if (!priv->chains[0]->arpmt[i])
3141 int fps = 0;
3142 VIDEOINFOHEADER* Vhdr = NULL;
3143 AM_MEDIA_TYPE *pmt;
3145 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));
3147 if (priv->chains[0]->arpmt[0])
3148 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->arpmt[0]->pbFormat;
3150 if(Vhdr && Vhdr->bmiHeader.biSizeImage)
3151 fps = Vhdr->dwBitRate / (8 * Vhdr->bmiHeader.biSizeImage);
3153 pmt=create_video_format(fcc, priv->width, priv->height, fps);
3154 if(!pmt)
3156 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to create AM_MEDIA_TYPE structure for given format\n");
3157 return TVI_CONTROL_FALSE;
3159 priv->chains[0]->arpmt=realloc(priv->chains[0]->arpmt, (i+2)*sizeof(AM_MEDIA_TYPE*));
3160 priv->chains[0]->arpmt[i+1] = NULL;
3161 priv->chains[0]->arpmt[i] = pmt;
3163 priv->chains[0]->arStreamCaps=realloc(priv->chains[0]->arStreamCaps, (i+2)*sizeof(void*));
3164 priv->chains[0]->arpmt[i+1] = NULL;
3166 result = TVI_CONTROL_FALSE;
3170 tmp=priv->chains[0]->arpmt[i];
3171 tmp2=priv->chains[0]->arStreamCaps[i];
3172 for(j=i; j>0; j--)
3174 priv->chains[0]->arpmt[j] = priv->chains[0]->arpmt[j-1];
3175 priv->chains[0]->arStreamCaps[j] = priv->chains[0]->arStreamCaps[j-1];
3177 priv->chains[0]->arpmt[0] = tmp;
3178 priv->chains[0]->arStreamCaps[0] = tmp2;
3180 priv->chains[0]->nFormatUsed = 0;
3182 if (priv->chains[0]->pmt)
3183 DeleteMediaType(priv->chains[0]->pmt);
3184 priv->chains[0]->pmt =
3185 CreateMediaType(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]);
3186 DisplayMediaType("VID_SET_FORMAT", priv->chains[0]->pmt);
3188 Setting width & height to preferred by driver values
3190 extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
3191 &(priv->fcc), &(priv->width),
3192 &(priv->height));
3193 return result;
3195 case TVI_CONTROL_VID_GET_FORMAT:
3197 if(!priv->chains[0]->pmt)
3198 return TVI_CONTROL_FALSE;
3200 Build video chain (for video format negotiation).
3201 If this was done before, routine will do nothing.
3203 build_video_chain(priv);
3204 DisplayMediaType("VID_GET_FORMAT", priv->chains[0]->pmt);
3205 if (priv->fcc) {
3206 *(int *) arg = priv->fcc;
3207 return TVI_CONTROL_TRUE;
3208 } else
3209 return TVI_CONTROL_FALSE;
3211 case TVI_CONTROL_VID_SET_WIDTH:
3213 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3214 VIDEOINFOHEADER *Vhdr;
3215 int width = *(int *) arg;
3216 if (priv->state)
3217 return TVI_CONTROL_FALSE;
3219 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3220 if (!pCaps)
3221 return TVI_CONTROL_FALSE;
3222 if (width < pCaps->MinOutputSize.cx
3223 || width > pCaps->MaxOutputSize.cx)
3224 return TVI_CONTROL_FALSE;
3226 if (width % pCaps->OutputGranularityX)
3227 return TVI_CONTROL_FALSE;
3229 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3230 return TVI_CONTROL_FALSE;
3231 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3232 Vhdr->bmiHeader.biWidth = width;
3233 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3234 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3235 Vhdr->bmiHeader.biHeight) >> 3;
3237 priv->width = width;
3239 return TVI_CONTROL_TRUE;
3241 case TVI_CONTROL_VID_GET_WIDTH:
3243 if (priv->width) {
3244 *(int *) arg = priv->width;
3245 return TVI_CONTROL_TRUE;
3246 } else
3247 return TVI_CONTROL_FALSE;
3249 case TVI_CONTROL_VID_CHK_WIDTH:
3251 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3252 int width = *(int *) arg;
3253 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3254 if (!pCaps)
3255 return TVI_CONTROL_FALSE;
3256 if (width < pCaps->MinOutputSize.cx
3257 || width > pCaps->MaxOutputSize.cx)
3258 return TVI_CONTROL_FALSE;
3260 if (width % pCaps->OutputGranularityX)
3261 return TVI_CONTROL_FALSE;
3262 return TVI_CONTROL_TRUE;
3264 case TVI_CONTROL_VID_SET_HEIGHT:
3266 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3267 VIDEOINFOHEADER *Vhdr;
3268 int height = *(int *) arg;
3269 if (priv->state)
3270 return TVI_CONTROL_FALSE;
3272 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3273 if (!pCaps)
3274 return TVI_CONTROL_FALSE;
3275 if (height < pCaps->MinOutputSize.cy
3276 || height > pCaps->MaxOutputSize.cy)
3277 return TVI_CONTROL_FALSE;
3279 if (height % pCaps->OutputGranularityY)
3280 return TVI_CONTROL_FALSE;
3282 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3283 return TVI_CONTROL_FALSE;
3284 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3286 if (Vhdr->bmiHeader.biHeight < 0)
3287 Vhdr->bmiHeader.biHeight = -height;
3288 else
3289 Vhdr->bmiHeader.biHeight = height;
3290 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3291 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3292 Vhdr->bmiHeader.biHeight) >> 3;
3294 priv->height = height;
3295 return TVI_CONTROL_TRUE;
3297 case TVI_CONTROL_VID_GET_HEIGHT:
3299 if (priv->height) {
3300 *(int *) arg = priv->height;
3301 return TVI_CONTROL_TRUE;
3302 } else
3303 return TVI_CONTROL_FALSE;
3305 case TVI_CONTROL_VID_CHK_HEIGHT:
3307 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3308 int height = *(int *) arg;
3309 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3310 if (!pCaps)
3311 return TVI_CONTROL_FALSE;
3312 if (height < pCaps->MinOutputSize.cy
3313 || height > pCaps->MaxOutputSize.cy)
3314 return TVI_CONTROL_FALSE;
3316 if (height % pCaps->OutputGranularityY)
3317 return TVI_CONTROL_FALSE;
3319 return TVI_CONTROL_TRUE;
3321 case TVI_CONTROL_IS_AUDIO:
3322 if (!priv->chains[1]->pmt)
3323 return TVI_CONTROL_FALSE;
3324 else
3325 return TVI_CONTROL_TRUE;
3326 case TVI_CONTROL_IS_VIDEO:
3327 return TVI_CONTROL_TRUE;
3328 case TVI_CONTROL_AUD_GET_FORMAT:
3330 *(int *) arg = AF_FORMAT_S16_LE;
3331 if (!priv->chains[1]->pmt)
3332 return TVI_CONTROL_FALSE;
3333 else
3334 return TVI_CONTROL_TRUE;
3336 case TVI_CONTROL_AUD_GET_CHANNELS:
3338 *(int *) arg = priv->channels;
3339 if (!priv->chains[1]->pmt)
3340 return TVI_CONTROL_FALSE;
3341 else
3342 return TVI_CONTROL_TRUE;
3344 case TVI_CONTROL_AUD_SET_SAMPLERATE:
3346 int i, samplerate;
3347 if (priv->state)
3348 return TVI_CONTROL_FALSE;
3349 if (!priv->chains[1]->arpmt[0])
3350 return TVI_CONTROL_FALSE;
3352 samplerate = *(int *) arg;
3354 for (i = 0; priv->chains[1]->arpmt[i]; i++)
3355 if (check_audio_format
3356 (priv->chains[1]->arpmt[i], samplerate, 16, priv->channels))
3357 break;
3358 if (!priv->chains[1]->arpmt[i]) {
3359 //request not found. failing back to first available
3360 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_SamplerateNotsupported, samplerate);
3361 i = 0;
3363 if (priv->chains[1]->pmt)
3364 DeleteMediaType(priv->chains[1]->pmt);
3365 priv->chains[1]->pmt = CreateMediaType(priv->chains[1]->arpmt[i]);
3366 extract_audio_format(priv->chains[1]->arpmt[i], &(priv->samplerate),
3367 NULL, &(priv->channels));
3368 return TVI_CONTROL_TRUE;
3370 case TVI_CONTROL_AUD_GET_SAMPLERATE:
3372 *(int *) arg = priv->samplerate;
3373 if (!priv->samplerate)
3374 return TVI_CONTROL_FALSE;
3375 if (!priv->chains[1]->pmt)
3376 return TVI_CONTROL_FALSE;
3377 else
3378 return TVI_CONTROL_TRUE;
3380 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
3382 WAVEFORMATEX *pWF;
3383 if (!priv->chains[1]->pmt)
3384 return TVI_CONTROL_FALSE;
3385 if (!priv->chains[1]->pmt->pbFormat)
3386 return TVI_CONTROL_FALSE;
3387 pWF = (WAVEFORMATEX *) priv->chains[1]->pmt->pbFormat;
3388 *(int *) arg = pWF->wBitsPerSample / 8;
3389 return TVI_CONTROL_TRUE;
3391 case TVI_CONTROL_IS_TUNER:
3393 if (!priv->pTVTuner)
3394 return TVI_CONTROL_FALSE;
3396 return TVI_CONTROL_TRUE;
3398 case TVI_CONTROL_TUN_SET_NORM:
3400 IAMAnalogVideoDecoder *pVD;
3401 long lAnalogFormat;
3402 int i;
3403 HRESULT hr;
3405 i = *(int *) arg;
3406 i--;
3407 if (i < 0 || i >= tv_available_norms_count)
3408 return TVI_CONTROL_FALSE;
3409 lAnalogFormat = tv_norms[tv_available_norms[i]].index;
3411 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3412 if (hr != S_OK)
3413 return TVI_CONTROL_FALSE;
3414 hr = OLE_CALL_ARGS(pVD, put_TVFormat, lAnalogFormat);
3415 OLE_RELEASE_SAFE(pVD);
3416 if (FAILED(hr))
3417 return TVI_CONTROL_FALSE;
3418 else
3419 return TVI_CONTROL_TRUE;
3421 case TVI_CONTROL_TUN_GET_NORM:
3423 long lAnalogFormat;
3424 int i;
3425 HRESULT hr;
3426 IAMAnalogVideoDecoder *pVD;
3428 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3429 if (hr == S_OK) {
3430 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat);
3431 OLE_RELEASE_SAFE(pVD);
3434 if (FAILED(hr)) { //trying another method
3435 if (!priv->pTVTuner)
3436 return TVI_CONTROL_FALSE;
3437 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat);
3438 if (FAILED(hr))
3439 return TVI_CONTROL_FALSE;
3441 for (i = 0; i < tv_available_norms_count; i++) {
3442 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) {
3443 *(int *) arg = i + 1;
3444 return TVI_CONTROL_TRUE;
3447 return TVI_CONTROL_FALSE;
3449 case TVI_CONTROL_SPC_GET_NORMID:
3451 int i;
3452 if (!priv->pTVTuner)
3453 return TVI_CONTROL_FALSE;
3454 for (i = 0; i < tv_available_norms_count; i++) {
3455 if (!strcasecmp
3456 (tv_norms[tv_available_norms[i]].name, (char *) arg)) {
3457 *(int *) arg = i + 1;
3458 return TVI_CONTROL_TRUE;
3461 return TVI_CONTROL_FALSE;
3463 case TVI_CONTROL_SPC_SET_INPUT:
3465 return set_crossbar_input(priv, *(int *) arg);
3467 case TVI_CONTROL_TUN_GET_FREQ:
3469 unsigned long lFreq;
3470 int ret;
3471 if (!priv->pTVTuner)
3472 return TVI_CONTROL_FALSE;
3474 ret = get_frequency(priv, &lFreq);
3475 lFreq = lFreq / (1000000/16); //convert from Hz to 1/16 MHz units
3477 *(unsigned long *) arg = lFreq;
3478 return ret;
3480 case TVI_CONTROL_TUN_SET_FREQ:
3482 unsigned long nFreq = *(unsigned long *) arg;
3483 if (!priv->pTVTuner)
3484 return TVI_CONTROL_FALSE;
3485 //convert to Hz
3486 nFreq = (1000000/16) * nFreq; //convert from 1/16 MHz units to Hz
3487 return set_frequency(priv, nFreq);
3489 case TVI_CONTROL_VID_SET_HUE:
3490 return set_control(priv, VideoProcAmp_Hue, *(int *) arg);
3491 case TVI_CONTROL_VID_GET_HUE:
3492 return get_control(priv, VideoProcAmp_Hue, (int *) arg);
3493 case TVI_CONTROL_VID_SET_CONTRAST:
3494 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg);
3495 case TVI_CONTROL_VID_GET_CONTRAST:
3496 return get_control(priv, VideoProcAmp_Contrast, (int *) arg);
3497 case TVI_CONTROL_VID_SET_SATURATION:
3498 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg);
3499 case TVI_CONTROL_VID_GET_SATURATION:
3500 return get_control(priv, VideoProcAmp_Saturation, (int *) arg);
3501 case TVI_CONTROL_VID_SET_BRIGHTNESS:
3502 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg);
3503 case TVI_CONTROL_VID_GET_BRIGHTNESS:
3504 return get_control(priv, VideoProcAmp_Brightness, (int *) arg);
3506 case TVI_CONTROL_VID_GET_FPS:
3508 VIDEOINFOHEADER *Vhdr;
3509 if (!priv->chains[0]->pmt)
3510 return TVI_CONTROL_FALSE;
3511 if (!priv->chains[0]->pmt->pbFormat)
3512 return TVI_CONTROL_FALSE;
3513 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3514 *(float *) arg =
3515 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8);
3516 return TVI_CONTROL_TRUE;
3518 case TVI_CONTROL_IMMEDIATE:
3519 priv->immediate_mode = 1;
3520 return TVI_CONTROL_TRUE;
3521 case TVI_CONTROL_VBI_INIT:
3523 void* ptr;
3524 ptr=&(priv->tsp);
3525 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==VBI_CONTROL_TRUE)
3526 priv->priv_vbi=ptr;
3527 else
3528 priv->priv_vbi=NULL;
3529 return TVI_CONTROL_TRUE;
3531 case TVI_CONTROL_GET_VBI_PTR:
3532 *(void **)arg=priv->priv_vbi;
3533 return TVI_CONTROL_TRUE;
3535 return TVI_CONTROL_UNKNOWN;