Add explanatory comments to the #endif part of multiple inclusion guards.
[mplayer/greg.git] / stream / tvi_dshow.c
blobe1580484110902cd42f805cfae9e45374e2651d1
1 /*
2 * TV support under Win32
3 *
4 * (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>.
6 * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c code .
8 * -------------------------------------------------------------------------------
10 * This program 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 * This program 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
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 * -------------------------------------------------------------------------------
28 * WARNING: This is alpha code!
30 * Abilities:
31 * * Watching TV under Win32 using WDM Capture driver and DirectShow
32 * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow)
33 * * If device driver provides IAMStreamConfig interface, user can choose width/height with "-tv height=<h>:width=<w>"
34 * * Adjusting BRIGHTNESS,HUE,SATURATION,CONTRAST if supported by device
35 * * Selecting Tuner,S-Video,... as media source
36 * * User can select used video capture device, passing -tv device=<dev#>
37 * * User can select used audio input, passing -tv audioid=<input#>
39 * options which will not be implemented (probably sometime in future, if possible):
40 * * alsa
41 * * mjpeg
42 * * decimation=<1|2|4>
43 * * quality=<0\-100>
44 * * forceaudio
45 * * forcechan=<1\-2>
46 * * [volume|bass|treble|balance]
48 * Works with:
49 * - LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov
50 * Partially works with:
51 * - ATI 9200 VIVO based card
52 * - ATI AIW 7500
53 * - nVidia Ti-4400
55 * Known bugs:
56 * * stream goes with 24.93 FPS (NTSC), while reporting 25 FPS (PAL) ?!
57 * * direct set frequency call does not work ('Insufficient Buffer' error)
58 * * audio stream goes with about 1 sample/sec rate when capturing sound from audio card
60 * TODO:
61 * * check audio with small buffer on vivo !!!
62 * * norm for IAMVideoDecoder and for IAMTVtuner - differs !!
63 * * check how to change TVFormat on VIVO card without tuner
64 * * Flip image upside-down for RGB formats.
65 * *
66 * * remove debug sleep()
67 * * Add some notes to methods' parameters
68 * * refactor console messages
69 * * check using header files and keep only needed
70 * * add additional comments to methods' bodies
75 /// \ingroup tvi_dshow
77 #include "config.h"
79 #include <stdio.h>
80 #include "libmpcodecs/img_format.h"
81 #include "libaf/af_format.h"
82 #include "help_mp.h"
83 #include "osdep/timer.h"
86 #include "tv.h"
87 #include "mp_msg.h"
88 #include "frequencies.h"
91 #include "tvi_dshow.h"
93 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param);
96 *---------------------------------------------------------------------------------------
98 * Data structures
100 *---------------------------------------------------------------------------------------
102 /**
103 information about this file
105 tvi_info_t tvi_info_dshow = {
106 tvi_init_dshow,
107 "DirectShow TV",
108 "dshow",
109 "Vladimir Voroshilov",
110 "Very experimental!! Use with caution"
115 ringbuffer related info
117 typedef struct {
118 CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex)
119 char **ringbuffer; ///< ringbuffer array
120 double*dpts; ///< samples' timestamps
122 int buffersize; ///< size of buffer in blocks
123 int blocksize; ///< size of individual block
124 int head; ///< index of first valid sample
125 int tail; ///< index of last valid sample
126 int count; ///< count of valid samples in ringbuffer
127 double tStart; ///< pts of first sample (first sample should have pts 0)
128 } grabber_ringbuffer_t;
130 typedef enum { unknown, video, audio, vbi } stream_type;
133 CSampleGrabberCD definition
135 typedef struct CSampleGrabberCB {
136 ISampleGrabberCBVtbl *lpVtbl;
137 int refcount;
138 GUID interfaces[2];
139 grabber_ringbuffer_t *pbuf;
140 } CSampleGrabberCB;
143 Chain related structure
145 typedef struct {
146 stream_type type; ///< stream type
147 const GUID* majortype; ///< GUID of major mediatype (video/audio/vbi)
148 const GUID* pin_category; ///< pin category (pointer to one of PIN_CATEGORY_*)
150 IBaseFilter *pCaptureFilter; ///< capture device filter
151 IAMStreamConfig *pStreamConfig; ///< for configuring stream
152 ISampleGrabber *pSG; ///< ISampleGrabber interface of SampleGrabber filter
153 IBaseFilter *pSGF; ///< IBaseFilter interface of SampleGrabber filter
154 IPin *pCapturePin; ///< output capture pin
155 IPin *pSGIn; ///< input pin of SampleGrabber filter
157 grabber_ringbuffer_t *rbuf; ///< sample frabber data
158 CSampleGrabberCB* pCSGCB; ///< callback object
160 AM_MEDIA_TYPE *pmt; ///< stream properties.
161 int nFormatUsed; ///< index of used format
162 AM_MEDIA_TYPE **arpmt; ///< available formats
163 void** arStreamCaps; ///< VIDEO_STREAM_CONFIG_CAPS or AUDIO_STREAM_CONFIG_CAPS
164 } chain_t;
166 typedef struct {
167 int dev_index; ///< capture device index in device list (defaul: 0, first available device)
168 int adev_index; ///< audio capture device index in device list (default: -1, not used)
169 int immediate_mode; ///< immediate mode (no sound capture)
170 int state; ///< state: 1-filter graph running, 0-filter graph stopped
171 int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency
172 int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency
174 int fcc; ///< used video format code (FourCC)
175 int width; ///< picture width (default: auto)
176 int height; ///< picture height (default: auto)
178 int channels; ///< number of audio channels (default: auto)
179 int samplerate; ///< audio samplerate (default: auto)
181 long *freq_table; ///< frequency table (in Hz)
182 int freq_table_len; ///< length of freq table
183 int first_channel; ///< channel number of first entry in freq table
184 int input; ///< used input
186 chain_t* chains[3]; ///< chains' data (0-video, 1-audio, 2-vbi)
188 IAMTVTuner *pTVTuner; ///< interface for tuner device
189 IGraphBuilder *pGraph; ///< filter graph
190 ICaptureGraphBuilder2 *pBuilder; ///< graph builder
191 IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...)
192 IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc
193 IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...)
194 DWORD dwRegister; ///< allow graphedit to connect to our graph
195 void *priv_vbi; ///< private VBI data structure
196 tt_stream_props tsp; ///< data for VBI initialization
198 tv_param_t* tv_param; ///< TV parameters
199 } priv_t;
201 #include "tvi_def.h"
204 country table entry structure (for loading freq table stored in kstvtuner.ax
206 \note
207 structure have to be 2-byte aligned and have 10-byte length!!
209 typedef struct __attribute__((__packed__)) {
210 WORD CountryCode; ///< Country code
211 WORD CableFreqTable; ///< index of resource with frequencies for cable channels
212 WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels
213 DWORD VideoStandard; ///< used video standard
214 } TRCCountryList;
216 information about image formats
218 typedef struct {
219 uint32_t fmt; ///< FourCC
220 const GUID *subtype; ///< DirectShow's subtype
221 int nBits; ///< number of bits
222 int nCompression; ///< complression
223 int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure
224 } img_fmt;
227 *---------------------------------------------------------------------------------------
229 * Methods forward declaration
231 *---------------------------------------------------------------------------------------
233 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
234 int blocksize);
235 static HRESULT show_filter_info(IBaseFilter * pFilter);
236 #if 0
237 //defined in current MinGW release
238 HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **);
239 HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **);
240 #endif
241 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
242 pbuf);
243 static int set_crossbar_input(priv_t * priv, int input);
244 static int subtype2imgfmt(const GUID * subtype);
247 *---------------------------------------------------------------------------------------
249 * Global constants and variables
251 *---------------------------------------------------------------------------------------
254 lookup tables for physical connector types
256 static const struct {
257 long type;
258 char *name;
259 } tv_physcon_types[]={
260 {PhysConn_Video_Tuner, "Tuner" },
261 {PhysConn_Video_Composite, "Composite" },
262 {PhysConn_Video_SVideo, "S-Video" },
263 {PhysConn_Video_RGB, "RGB" },
264 {PhysConn_Video_YRYBY, "YRYBY" },
265 {PhysConn_Video_SerialDigital, "SerialDigital" },
266 {PhysConn_Video_ParallelDigital, "ParallelDigital"},
267 {PhysConn_Video_VideoDecoder, "VideoDecoder" },
268 {PhysConn_Video_VideoEncoder, "VideoEncoder" },
269 {PhysConn_Video_SCART, "SCART" },
270 {PhysConn_Video_Black, "Blaack" },
271 {PhysConn_Audio_Tuner, "Tuner" },
272 {PhysConn_Audio_Line, "Line" },
273 {PhysConn_Audio_Mic, "Mic" },
274 {PhysConn_Audio_AESDigital, "AESDiital" },
275 {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" },
276 {PhysConn_Audio_AudioDecoder, "AudioDecoder" },
277 {PhysConn_Audio_SCSI, "SCSI" },
278 {PhysConn_Video_SCSI, "SCSI" },
279 {PhysConn_Audio_AUX, "AUX" },
280 {PhysConn_Video_AUX, "AUX" },
281 {PhysConn_Audio_1394, "1394" },
282 {PhysConn_Video_1394, "1394" },
283 {PhysConn_Audio_USB, "USB" },
284 {PhysConn_Video_USB, "USB" },
285 {-1, NULL }
288 static const struct {
289 char *chanlist_name;
290 int country_code;
291 } tv_chanlist2country[]={
292 {"us-bcast", 1},
293 {"russia", 7},
294 {"argentina", 54},
295 {"japan-bcast", 81},
296 {"china-bcast", 86},
297 {"southafrica", 27},
298 {"australia", 61},
299 {"ireland", 353},
300 {"france", 33},
301 {"italy", 39},
302 {"newzealand", 64},
303 //directshow table uses eastern europe freq table for russia
304 {"europe-east", 7},
305 //directshow table uses western europe freq table for germany
306 {"europe-west", 49},
307 /* cable channels */
308 {"us-cable", 1},
309 {"us-cable-hrc", 1},
310 {"japan-cable", 81},
311 //default is USA
312 {NULL, 1}
316 array, contains information about various supported (i hope) image formats
318 static const img_fmt img_fmt_list[] = {
319 {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 16, IMGFMT_YUY2, 0},
320 {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 12, IMGFMT_YV12, 0},
321 {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 12, IMGFMT_IYUV, 0},
322 {IMGFMT_I420, &MEDIASUBTYPE_I420, 12, IMGFMT_I420, 0},
323 {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 16, IMGFMT_UYVY, 0},
324 {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 16, IMGFMT_YVYU, 0},
325 {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 9, IMGFMT_YVU9, 0},
326 {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0},
327 {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0},
328 {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12},
329 {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12},
330 {0, &GUID_NULL, 0, 0, 0}
333 #define TV_NORMS_COUNT 19
334 static const struct {
335 long index;
336 char *name;
337 } tv_norms[TV_NORMS_COUNT] = {
339 AnalogVideo_NTSC_M, "ntsc-m"}, {
340 AnalogVideo_NTSC_M_J, "ntsc-mj"}, {
341 AnalogVideo_NTSC_433, "ntsc-433"}, {
342 AnalogVideo_PAL_B, "pal-b"}, {
343 AnalogVideo_PAL_D, "pal-d"}, {
344 AnalogVideo_PAL_G, "pal-g"}, {
345 AnalogVideo_PAL_H, "pal-h"}, {
346 AnalogVideo_PAL_I, "pal-i"}, {
347 AnalogVideo_PAL_M, "pal-m"}, {
348 AnalogVideo_PAL_N, "pal-n"}, {
349 AnalogVideo_PAL_60, "pal-60"}, {
350 AnalogVideo_SECAM_B, "secam-b"}, {
351 AnalogVideo_SECAM_D, "secam-d"}, {
352 AnalogVideo_SECAM_G, "secam-g"}, {
353 AnalogVideo_SECAM_H, "secam-h"}, {
354 AnalogVideo_SECAM_K, "secam-k"}, {
355 AnalogVideo_SECAM_K1, "secam-k1"}, {
356 AnalogVideo_SECAM_L, "secam-l"}, {
357 AnalogVideo_SECAM_L1, "secam-l1"}
359 static long tv_available_norms[TV_NORMS_COUNT];
360 static int tv_available_norms_count = 0;
363 static long *tv_available_inputs;
364 static int tv_available_inputs_count = 0;
367 *---------------------------------------------------------------------------------------
369 * Various GUID definitions
371 *---------------------------------------------------------------------------------------
373 /// CLSID definitions (used for CoCreateInstance call)
374 DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B,
375 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
376 DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B,
377 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
378 DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B,
379 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
380 DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3,
381 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
382 DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0,
383 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
384 DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0,
385 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
386 DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53,
387 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
388 DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53,
389 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
390 #ifdef NOT_USED
391 DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3,
392 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
393 DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a,
394 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2);
395 DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20,
396 0xaf, 0x0b, 0xa7, 0x70);
397 DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3,
398 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5);
399 DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
400 0x20, 0xaf, 0x0b, 0xa7, 0x70);
401 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
402 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
403 #endif
405 /// IID definitions (used for QueryInterface call)
406 DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a,
407 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
408 DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0,
409 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
410 DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa,
411 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
412 DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD,
413 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F);
414 DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0,
415 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85);
416 DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,
417 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d);
418 DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b,
419 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
420 DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a,
421 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
422 DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C,
423 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
424 DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
425 0x20, 0xaf, 0x0b, 0xa7, 0x70);
426 DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a,
427 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
428 DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00,
429 0xAA, 0x00, 0xBD, 0x83, 0x39);
430 DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00,
431 0xa0, 0xc9, 0x11, 0x89, 0x56);
432 DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c,
433 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
434 DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0,
435 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
436 DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00,
437 0xA0, 0xC9, 0x56, 0x02, 0x66);
438 DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1,
439 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
440 DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00,
441 0xaa, 0x00, 0x4b, 0xb8, 0x51);
442 DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f,
443 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
444 DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f,
445 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
446 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
447 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
448 DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f,
449 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
450 DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00,
451 0xa0, 0xc9, 0x11, 0x89, 0x56);
452 DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00,
453 0x00, 0xc0, 0xcc, 0x16, 0xba);
455 #define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
456 #define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
458 #define DEVICE_NAME_MAX_LEN 2000
460 /*---------------------------------------------------------------------------------------
461 * Methods, called only from this file
462 *---------------------------------------------------------------------------------------*/
464 void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){
465 ALLOCATOR_PROPERTIES prop;
466 IAMBufferNegotiation* pBN;
467 HRESULT hr;
469 prop.cbAlign = -1;
470 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
471 if (!prop.cbBuffer)
472 prop.cbBuffer = 1;
473 prop.cbBuffer += pWF->nBlockAlign - 1;
474 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
475 prop.cbPrefix = -1;
476 prop.cBuffers = -1;
478 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
479 if(FAILED(hr))
480 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
481 else{
482 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
483 if(FAILED(hr))
484 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
485 OLE_RELEASE_SAFE(pBN);
487 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
488 if(FAILED(hr))
489 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
490 else{
491 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
492 if(FAILED(hr))
493 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
494 OLE_RELEASE_SAFE(pBN);
498 *---------------------------------------------------------------------------------------
500 * CSampleGrabberCD class. Used for receiving samples from DirectShow.
502 *---------------------------------------------------------------------------------------
504 /// CSampleGrabberCD destructor
505 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
507 free(This->lpVtbl);
508 free(This);
511 /// CSampleGrabberCD IUnknown interface methods implementation
512 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
513 This,
514 const GUID * riid,
515 void **ppvObject)
517 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
518 GUID *r;
519 unsigned int i = 0;
520 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
521 if (!ppvObject)
522 return E_POINTER;
523 for (r = me->interfaces;
524 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
525 if (!memcmp(r, riid, sizeof(*r))) {
526 OLE_CALL(This, AddRef);
527 *ppvObject = This;
528 return 0;
530 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
531 return E_NOINTERFACE;
534 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
536 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
537 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
538 me->refcount);
539 return ++(me->refcount);
542 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
544 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
545 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
546 This, me->refcount - 1);
547 if (--(me->refcount) == 0)
548 CSampleGrabberCB_Destroy(me);
549 return 0;
553 HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This,
554 double SampleTime,
555 BYTE * pBuffer, long lBufferLen)
557 CSampleGrabberCB *this = (CSampleGrabberCB *) This;
558 grabber_ringbuffer_t *rb = this->pbuf;
560 if (!lBufferLen)
561 return E_FAIL;
563 if (!rb->ringbuffer) {
564 rb->buffersize /= lBufferLen;
565 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
566 return E_FAIL;
568 mp_msg(MSGT_TV, MSGL_DBG4,
569 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
570 EnterCriticalSection(rb->pMutex);
571 if (rb->count >= rb->buffersize) {
572 rb->head = (rb->head + 1) % rb->buffersize;
573 rb->count--;
576 memcpy(rb->ringbuffer[rb->tail], pBuffer,
577 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
578 rb->dpts[rb->tail] = SampleTime;
579 rb->tail = (rb->tail + 1) % rb->buffersize;
580 rb->count++;
581 LeaveCriticalSection(rb->pMutex);
583 return S_OK;
586 /// wrapper. directshow does the same when BufferCB callback is requested
587 HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This,
588 double SampleTime,
589 LPMEDIASAMPLE pSample)
591 char* buf;
592 long len;
593 long long tStart,tEnd;
594 HRESULT hr;
595 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
597 len=OLE_CALL(pSample,GetSize);
598 tStart=tEnd=0;
599 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
600 if(FAILED(hr)){
601 return hr;
603 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
604 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
605 if(FAILED(hr)){
606 return hr;
608 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
609 return hr;
613 /// main grabbing routine
614 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
615 pbuf)
617 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
618 if (!This)
619 return NULL;
621 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
622 if (!This->lpVtbl) {
623 CSampleGrabberCB_Destroy(This);
624 return NULL;
626 This->refcount = 1;
627 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
628 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
629 This->lpVtbl->Release = CSampleGrabberCB_Release;
630 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
631 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
633 This->interfaces[0] = IID_IUnknown;
634 This->interfaces[1] = IID_ISampleGrabberCB;
636 This->pbuf = pbuf;
638 return This;
642 *---------------------------------------------------------------------------------------
644 * ROT related methods (register, unregister)
646 *---------------------------------------------------------------------------------------
648 /**
649 Registering graph in ROT. User will be able to connect to graph from GraphEdit.
651 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
653 IMoniker *pMoniker;
654 IRunningObjectTable *pROT;
655 WCHAR wsz[256];
656 HRESULT hr;
658 if (FAILED(GetRunningObjectTable(0, &pROT))) {
659 return E_FAIL;
661 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
662 GetCurrentProcessId());
663 hr = CreateItemMoniker(L"!", wsz, &pMoniker);
664 if (SUCCEEDED(hr)) {
665 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
666 pUnkGraph, pMoniker, pdwRegister);
667 OLE_RELEASE_SAFE(pMoniker);
669 OLE_RELEASE_SAFE(pROT);
670 return hr;
673 /// Unregistering graph in ROT
674 static void RemoveFromRot(DWORD dwRegister)
676 IRunningObjectTable *pROT;
677 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
678 OLE_CALL_ARGS(pROT, Revoke, dwRegister);
679 OLE_RELEASE_SAFE(pROT);
684 *---------------------------------------------------------------------------------------
686 * ringbuffer related methods (init, destroy)
688 *---------------------------------------------------------------------------------------
691 * \brief ringbuffer destroying routine
693 * \param rb pointer to empty (just allocated) ringbuffer structure
695 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
697 static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
699 int i;
701 if (!rb)
702 return;
704 if (rb->ringbuffer) {
705 for (i = 0; i < rb->buffersize; i++)
706 if (rb->ringbuffer[i])
707 free(rb->ringbuffer[i]);
708 free(rb->ringbuffer);
709 rb->ringbuffer = NULL;
711 if (rb->dpts) {
712 free(rb->dpts);
713 rb->dpts = NULL;
715 if (rb->pMutex) {
716 DeleteCriticalSection(rb->pMutex);
717 free(rb->pMutex);
718 rb->pMutex = NULL;
721 rb->blocksize = 0;
722 rb->buffersize = 0;
723 rb->head = 0;
724 rb->tail = 0;
725 rb->count = 0;
729 * \brief ringbuffer initialization
731 * \param rb pointer to empty (just allocated) ringbuffer structure
732 * \param buffersize size of buffer in blocks
733 * \param blocksize size of buffer's block
735 * \return S_OK if success
736 * \return E_OUTOFMEMORY not enough memory
738 * \note routine does not allocates memory for grabber_rinbuffer_s structure
740 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
741 int blocksize)
743 int i;
745 if (!rb)
746 return E_OUTOFMEMORY;
748 rb->buffersize = buffersize < 2 ? 2 : buffersize;
749 rb->blocksize = blocksize;
751 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
752 rb->buffersize, rb->blocksize);
754 rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *));
755 if (!rb)
756 return E_POINTER;
757 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
759 for (i = 0; i < rb->buffersize; i++) {
760 rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char));
761 if (!rb->ringbuffer[i]) {
762 destroy_ringbuffer(rb);
763 return E_OUTOFMEMORY;
766 rb->dpts = (double*) malloc(rb->buffersize * sizeof(double));
767 if (!rb->dpts) {
768 destroy_ringbuffer(rb);
769 return E_OUTOFMEMORY;
771 rb->head = 0;
772 rb->tail = 0;
773 rb->count = 0;
774 rb->tStart = -1;
775 rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
776 if (!rb->pMutex) {
777 destroy_ringbuffer(rb);
778 return E_OUTOFMEMORY;
780 InitializeCriticalSection(rb->pMutex);
781 return S_OK;
785 *---------------------------------------------------------------------------------------
787 * Tuner related methods (frequency, capabilities, etc
789 *---------------------------------------------------------------------------------------
792 * \brief returns string with name for givend PsysCon_* constant
794 * \param lPhysicalType constant from PhysicalConnectorType enumeration
796 * \return pointer to string with apropriate name
798 * \note
799 * Caller should not free returned pointer
801 static char *physcon2str(const long lPhysicalType)
803 int i;
804 for(i=0; tv_physcon_types[i].name; i++)
805 if(tv_physcon_types[i].type==lPhysicalType)
806 return tv_physcon_types[i].name;
807 return "Unknown";
811 * \brief converts MPlayer's chanlist to system country code.
813 * \param chanlist MPlayer's chanlist name
815 * \return system country code
817 * \remarks
818 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
819 * country (which is usually larger then MPlayer's one, so workaround will work fine).
821 * \todo
822 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
824 static int chanlist2country(char *chanlist)
826 int i;
827 for(i=0; tv_chanlist2country[i].chanlist_name; i++)
828 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
829 break;
830 return tv_chanlist2country[i].country_code;
834 * \brief loads specified resource from module and return pointer to it
836 * \param hDLL valid module desriptor
837 * \param index index of resource. resource with name "#<index>" will be loaded
839 * \return pointer to loader resource or NULL if error occured
841 static void *GetRC(HMODULE hDLL, int index)
843 char szRCDATA[10];
844 char szName[10];
845 HRSRC hRes;
846 HGLOBAL hTable;
848 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
849 snprintf(szName, 10, "#%d", index);
851 hRes = FindResource(hDLL, szName, szRCDATA);
852 if (!hRes) {
853 return NULL;
855 hTable = LoadResource(hDLL, hRes);
856 if (!hTable) {
857 return NULL;
859 return LockResource(hTable);
863 * \brief loads frequency table for given country from kstvtune.ax
865 * \param[in] nCountry - country code
866 * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
867 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
868 * \param[out] pnLen length of array
869 * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
871 * \return S_OK if success
872 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
873 * \return E_FAIL error occured during load
875 * \remarks
876 * - array must be freed by caller
877 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
879 static HRESULT load_freq_table(int nCountry, int nInputType,
880 long **pplFreqTable, int *pnLen,
881 int *pnFirst)
883 HMODULE hDLL;
884 long *plFreqTable;
885 TRCCountryList *pCountryList;
886 int i, index;
888 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%d)\n",nCountry,nInputType);
889 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
891 if (!pplFreqTable || !pnFirst || !pnLen)
892 return E_POINTER;
893 if (!nCountry)
894 return E_FAIL;
896 hDLL = LoadLibrary("kstvtune.ax");
897 if (!hDLL) {
898 return E_FAIL;
900 pCountryList = GetRC(hDLL, 9999);
901 if (!pCountryList) {
902 FreeLibrary(hDLL);
903 return E_FAIL;
905 for (i = 0; pCountryList[i].CountryCode != 0; i++)
906 if (pCountryList[i].CountryCode == nCountry)
907 break;
908 if (pCountryList[i].CountryCode == 0) {
909 FreeLibrary(hDLL);
910 return E_FAIL;
912 if (nInputType == TunerInputCable)
913 index = pCountryList[i].CableFreqTable;
914 else
915 index = pCountryList[i].BroadcastFreqTable;
917 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
918 if (!plFreqTable) {
919 FreeLibrary(hDLL);
920 return E_FAIL;
922 *pnFirst = plFreqTable[0];
923 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
924 *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long));
925 if (!*pplFreqTable) {
926 FreeLibrary(hDLL);
927 return E_FAIL;
929 for (i = 0; i < *pnLen; i++) {
930 (*pplFreqTable)[i] = plFreqTable[i + 2];
932 FreeLibrary(hDLL);
933 return S_OK;
937 * \brief tunes to given frequency through IKsPropertySet call
939 * \param pTVTuner IAMTVTuner interface of capture device
940 * \param lFreq frequency to tune (in Hz)
942 * \return S_OK success
943 * \return apropriate error code otherwise
945 * \note
946 * Due to either bug in driver or error in following code calll to IKsProperty::Set
947 * in this methods always fail with error 0x8007007a.
949 * \todo test code on other machines and an error
951 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
953 HRESULT hr;
954 DWORD dwSupported = 0;
955 DWORD cbBytes = 0;
956 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
957 KSPROPERTY_TUNER_FREQUENCY_S frequency;
958 IKsPropertySet *pKSProp;
960 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
962 memset(&mode_caps, 0, sizeof(mode_caps));
963 memset(&frequency, 0, sizeof(frequency));
965 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
966 if (FAILED(hr))
967 return hr; //no IKsPropertySet interface
969 mode_caps.Mode = AMTUNER_MODE_TV;
970 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
971 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
972 if (FAILED(hr)) {
973 OLE_RELEASE_SAFE(pKSProp);
974 return hr;
977 if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
978 OLE_RELEASE_SAFE(pKSProp);
979 return E_FAIL; //PROPSETID_TINER not supported
982 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
983 KSPROPERTY_TUNER_MODE_CAPS,
984 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
985 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
986 &mode_caps, sizeof(mode_caps), &cbBytes);
988 frequency.Frequency = lFreq;
990 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
991 frequency.TuningFlags = KS_TUNER_TUNING_FINE;
992 else
993 frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
995 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
996 OLE_RELEASE_SAFE(pKSProp);
997 return E_FAIL;
1000 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
1001 KSPROPERTY_TUNER_FREQUENCY,
1002 INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
1003 INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
1004 &frequency, sizeof(frequency));
1005 if (FAILED(hr)) {
1006 OLE_RELEASE_SAFE(pKSProp);
1007 return hr;
1010 OLE_RELEASE_SAFE(pKSProp);
1012 return S_OK;
1016 * \brief find channel with nearest frequency and set it
1018 * \param priv driver's private data
1019 * \param lFreq frequency in Hz
1021 * \return S_OK if success
1022 * \return E_FAIL if error occured
1024 static HRESULT set_nearest_freq(priv_t * priv, long lFreq)
1026 HRESULT hr;
1027 int i;
1028 long lFreqDiff=-1;
1029 int nChannel;
1030 TunerInputType tunerInput;
1031 long lInput;
1033 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called\n");
1034 if(priv->freq_table_len == -1 && !priv->freq_table) {
1036 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
1037 if(FAILED(hr)){ //Falling back to 0
1038 lInput=0;
1041 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput);
1043 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME
1044 priv->freq_table_len=0;
1045 priv->freq_table=NULL;
1046 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableExtractFreqTable);
1047 return E_FAIL;
1049 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_FreqTableLoaded, tunerInput == TunerInputAntenna ? "broadcast" : "cable",
1050 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len);
1053 if (priv->freq_table_len <= 0)
1054 return E_FAIL;
1056 //FIXME: rewrite search algo
1057 nChannel = -1;
1058 for (i = 0; i < priv->freq_table_len; i++) {
1059 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) {
1060 nChannel = priv->first_channel + i;
1061 lFreqDiff = labs(lFreq - priv->freq_table[i]);
1064 if (nChannel == -1) {
1065 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableFindNearestChannel);
1066 return E_FAIL;
1068 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel,
1069 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
1070 if (FAILED(hr)) {
1071 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableToSetChannel, (unsigned int)hr);
1072 return E_FAIL;
1074 return S_OK;
1078 * \brief setting frequency. decides whether use direct call/workaround
1080 * \param priv driver's private data
1081 * \param lFreq frequency in Hz
1083 * \return TVI_CONTROL_TRUE if success
1084 * \return TVI_CONTROL_FALSE if error occured
1086 * \todo check for freq boundary
1088 static int set_frequency(priv_t * priv, long lFreq)
1090 HRESULT hr;
1092 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called\n");
1093 if (!priv->pTVTuner)
1094 return TVI_CONTROL_FALSE;
1095 if (priv->direct_setfreq_call) { //using direct call to set frequency
1096 hr = set_frequency_direct(priv->pTVTuner, lFreq);
1097 if (FAILED(hr)) {
1098 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DirectSetFreqFailed);
1099 priv->direct_setfreq_call = 0;
1102 if (!priv->direct_setfreq_call) {
1103 hr = set_nearest_freq(priv, lFreq);
1105 if (FAILED(hr))
1106 return TVI_CONTROL_FALSE;
1107 #ifdef DEPRECATED
1108 priv->pGrabber->ClearBuffer(priv->pGrabber);
1109 #endif
1110 return TVI_CONTROL_TRUE;
1114 * \brief return current frequency from tuner (in Hz)
1116 * \param pTVTuner IAMTVTuner interface of tuner
1117 * \param plFreq address of variable that receives current frequency
1119 * \return S_OK success
1120 * \return E_POINTER pTVTuner==NULL || plFreq==NULL
1121 * \return apropriate error code otherwise
1123 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq)
1125 HRESULT hr;
1126 KSPROPERTY_TUNER_STATUS_S TunerStatus;
1127 DWORD cbBytes;
1128 IKsPropertySet *pKSProp;
1129 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n");
1131 if (!plFreq)
1132 return E_POINTER;
1134 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1135 if (FAILED(hr)) {
1136 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n");
1137 return hr;
1140 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1141 KSPROPERTY_TUNER_STATUS,
1142 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus),
1143 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus),
1144 &TunerStatus, sizeof(TunerStatus), &cbBytes);
1145 if (FAILED(hr)) {
1146 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n");
1147 return hr;
1149 *plFreq = TunerStatus.CurrentFrequency;
1150 return S_OK;
1154 * \brief gets current frequency
1156 * \param priv driver's private data structure
1157 * \param plFreq - pointer to long int to store frequency to (in Hz)
1159 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
1161 static int get_frequency(priv_t * priv, long *plFreq)
1163 HRESULT hr;
1165 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n");
1167 if (!plFreq || !priv->pTVTuner)
1168 return TVI_CONTROL_FALSE;
1170 if (priv->direct_getfreq_call) { //using direct call to get frequency
1171 hr = get_frequency_direct(priv->pTVTuner, plFreq);
1172 if (FAILED(hr)) {
1173 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_DirectGetFreqFailed);
1174 priv->direct_getfreq_call = 0;
1177 if (!priv->direct_getfreq_call) {
1178 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq);
1179 if (FAILED(hr))
1180 return TVI_CONTROL_FALSE;
1183 return TVI_CONTROL_TRUE;
1187 * \brief get tuner capabilities
1189 * \param priv driver's private data
1191 static void get_capabilities(priv_t * priv)
1193 long lAvailableFormats;
1194 HRESULT hr;
1195 int i;
1196 long lInputPins, lOutputPins, lRelated, lPhysicalType;
1197 IEnumPins *pEnum;
1198 char tmp[200];
1199 IPin *pPin = 0;
1200 PIN_DIRECTION ThisPinDir;
1201 PIN_INFO pi;
1202 IAMAudioInputMixer *pIAMixer;
1204 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n");
1205 if (priv->pTVTuner) {
1207 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_SupportedNorms);
1208 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats,
1209 &lAvailableFormats);
1210 if (FAILED(hr))
1211 tv_available_norms_count = 0;
1212 else {
1213 for (i = 0; i < TV_NORMS_COUNT; i++) {
1214 if (lAvailableFormats & tv_norms[i].index) {
1215 tv_available_norms[tv_available_norms_count] = i;
1216 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1217 tv_available_norms_count + 1, tv_norms[i].name);
1218 tv_available_norms_count++;
1222 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1224 if (priv->pCrossbar) {
1225 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins,
1226 &lInputPins);
1228 tv_available_inputs = (long *) malloc(sizeof(long) * lInputPins);
1229 tv_available_inputs_count = 0;
1231 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableVideoInputs);
1232 for (i = 0; i < lInputPins; i++) {
1233 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i,
1234 &lRelated, &lPhysicalType);
1236 if (lPhysicalType < 0x1000) {
1237 tv_available_inputs[tv_available_inputs_count++] = i;
1238 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1239 tv_available_inputs_count - 1,
1240 physcon2str(lPhysicalType));
1243 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1245 set_crossbar_input(priv, 0);
1248 if (priv->adev_index != -1) {
1249 hr = OLE_CALL_ARGS(priv->chains[1]->pCaptureFilter, EnumPins, &pEnum);
1250 if (FAILED(hr))
1251 return;
1252 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableAudioInputs);
1253 i = 0;
1254 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1255 memset(&pi, 0, sizeof(pi));
1256 memset(tmp, 0, 200);
1257 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1258 if (ThisPinDir == PINDIR_INPUT) {
1259 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1260 wtoa(pi.achName, tmp, 200);
1261 OLE_RELEASE_SAFE(pi.pFilter);
1262 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp);
1263 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1264 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer);
1265 if (SUCCEEDED(hr)) {
1266 if (i == priv->tv_param->audio_id) {
1267 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE);
1268 if(priv->tv_param->volume>0)
1269 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume);
1270 #if 0
1271 else
1272 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0);
1273 #endif
1274 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_InputSelected);
1275 } else {
1276 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE);
1277 #if 0
1278 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0);
1279 #endif
1281 OLE_RELEASE_SAFE(pIAMixer);
1283 mp_msg(MSGT_TV, MSGL_V, ";");
1284 OLE_RELEASE_SAFE(pPin);
1285 i++;
1288 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1289 OLE_RELEASE_SAFE(pEnum);
1294 *---------------------------------------------------------------------------------------
1296 * Filter related methods
1298 *---------------------------------------------------------------------------------------
1301 * \brief routine for reconnecting two pins with new media type
1302 * \param pGraph IGraphBuilder interface
1303 * \param chan chain data
1304 * \param pmt [in/out] new mediatype for pin connection
1306 * \return S_OK if operation successfult, error code otherwise
1307 * will also return media type of new connection into pmt variable
1309 static HRESULT reconnect_pins(IGraphBuilder *pGraph, chain_t *chain, AM_MEDIA_TYPE *pmt)
1311 AM_MEDIA_TYPE old_mt;
1312 HRESULT hr;
1314 do {
1315 /* save old media type for reconnection in case of error */
1316 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, &old_mt);
1317 if(FAILED(hr))
1318 return hr;
1320 hr = OLE_CALL(chain->pCapturePin, Disconnect);
1321 if(FAILED(hr))
1322 return hr;
1324 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, pmt);
1325 if(FAILED(hr))
1326 return hr;
1328 hr = OLE_CALL_ARGS(pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1329 if(FAILED(hr))
1331 OLE_CALL_ARGS(chain->pSG, SetMediaType, &old_mt);
1332 OLE_CALL_ARGS(pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1333 break;
1335 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, &old_mt);
1337 hr = S_OK;
1338 } while(0);
1340 FreeMediaType(pmt);
1341 CopyMediaType(pmt, &old_mt);
1342 FreeMediaType(&old_mt);
1343 return hr;
1347 * \brief building in graph audio/video capture chain
1349 * \param priv driver's private data
1350 * \param pCaptureFilter pointer to capture device's IBaseFilter interface
1351 * \param pbuf ringbuffer data structure
1352 * \param pmt media type for chain (AM_MEDIA_TYPE)
1354 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
1356 static HRESULT build_sub_graph(priv_t * priv, chain_t * chain, const GUID* ppin_category)
1358 HRESULT hr;
1359 int nFormatProbed = 0;
1361 IPin *pSGOut;
1362 IPin *pNRIn=NULL;
1364 IBaseFilter *pNR = NULL;
1366 hr=S_OK;
1368 //No supported formats
1369 if(!chain->arpmt[0])
1370 return E_FAIL;
1373 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
1374 (IUnknown *) chain->pCaptureFilter,
1375 PINDIR_OUTPUT, ppin_category,
1376 chain->majortype, FALSE, 0, &chain->pCapturePin);
1377 if(FAILED(hr)){
1378 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
1379 break;
1381 /* Addinf SampleGrabber filter for video stream */
1382 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &chain->pSGF);
1383 if(FAILED(hr)){
1384 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1385 break;
1387 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, chain->pSGF, L"Sample Grabber");
1388 if(FAILED(hr)){
1389 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1390 break;
1392 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &chain->pSGIn);
1393 if(FAILED(hr)){
1394 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr);
1395 break;
1397 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut);
1398 if(FAILED(hr)){
1399 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr);
1400 break;
1403 /* creating ringbuffer for video samples */
1404 chain->pCSGCB = CSampleGrabberCB_Create(chain->rbuf);
1405 if(!chain->pCSGCB){
1406 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY);
1407 break;
1410 /* initializing SampleGrabber filter */
1411 hr = OLE_QUERYINTERFACE(chain->pSGF, IID_ISampleGrabber, chain->pSG);
1412 if(FAILED(hr)){
1413 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1414 break;
1416 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data
1417 hr = OLE_CALL_ARGS(chain->pSG, SetCallback, (ISampleGrabberCB *) chain->pCSGCB, 0); //we want to receive sample
1419 if(FAILED(hr)){
1420 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1421 break;
1423 hr = OLE_CALL_ARGS(chain->pSG, SetOneShot, FALSE); //... for all frames
1424 if(FAILED(hr)){
1425 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1426 break;
1428 hr = OLE_CALL_ARGS(chain->pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber
1429 if(FAILED(hr)){
1430 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1431 break;
1434 if(priv->tv_param->normalize_audio_chunks && chain->type==audio){
1435 set_buffer_preference(20,(WAVEFORMATEX*)(chain->arpmt[nFormatProbed]->pbFormat),chain->pCapturePin,chain->pSGIn);
1438 for(nFormatProbed=0; chain->arpmt[nFormatProbed]; nFormatProbed++)
1440 DisplayMediaType("Probing format", chain->arpmt[nFormatProbed]);
1441 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, chain->arpmt[nFormatProbed]); //set desired mediatype
1442 if(FAILED(hr)){
1443 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1444 continue;
1446 /* connecting filters together: VideoCapture --> SampleGrabber */
1447 hr = OLE_CALL_ARGS(priv->pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1448 if(FAILED(hr)){
1449 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr);
1450 continue;
1452 break;
1455 if(!chain->arpmt[nFormatProbed])
1457 mp_msg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to negotiate media format\n");
1458 hr = E_FAIL;
1459 break;
1462 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, chain->pmt);
1463 if(FAILED(hr))
1465 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_GetActualMediatypeFailed, (unsigned int)hr);
1468 if(priv->tv_param->hidden_video_renderer){
1469 IEnumFilters* pEnum;
1470 IBaseFilter* pFilter;
1472 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)chain->pCapturePin,NULL,NULL);
1474 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
1475 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
1476 LPVIDEOWINDOW pVideoWindow;
1477 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
1478 if (SUCCEEDED(hr))
1480 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
1481 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
1482 OLE_RELEASE_SAFE(pVideoWindow);
1484 OLE_RELEASE_SAFE(pFilter);
1486 OLE_RELEASE_SAFE(pEnum);
1487 }else
1489 #if 0
1491 Code below is disabled, because terminating chain with NullRenderer leads to jerky video.
1492 Perhaps, this happens because NullRenderer filter discards each received
1493 frame while discarded frames causes live source filter to dramatically reduce frame rate.
1495 /* adding sink for video stream */
1496 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR);
1497 if(FAILED(hr)){
1498 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1499 break;
1501 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer");
1502 if(FAILED(hr)){
1503 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1504 break;
1506 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn);
1507 if(FAILED(hr)){
1508 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr);
1509 break;
1512 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection
1514 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){
1515 /* connecting filters together: SampleGrabber --> NullRenderer */
1516 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn);
1517 if(FAILED(hr)){
1518 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr);
1519 break;
1522 #endif
1525 hr = S_OK;
1526 } while(0);
1528 OLE_RELEASE_SAFE(pSGOut);
1529 OLE_RELEASE_SAFE(pNR);
1530 OLE_RELEASE_SAFE(pNRIn);
1532 return hr;
1536 * \brief configures crossbar for grabbing video stream from given input
1538 * \param priv driver's private data
1539 * \param input index of available video input to get data from
1541 * \return TVI_CONTROL_TRUE success
1542 * \return TVI_CONTROL_FALSE error
1544 static int set_crossbar_input(priv_t * priv, int input)
1546 HRESULT hr;
1547 int i, nVideoDecoder, nAudioDecoder;
1548 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins,
1549 lInputPins;
1551 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n");
1552 if (!priv->pCrossbar || input < 0
1553 || input >= tv_available_inputs_count)
1554 return TVI_CONTROL_FALSE;
1556 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins);
1558 lInput = tv_available_inputs[input];
1560 if (lInput < 0 || lInput >= lInputPins)
1561 return TVI_CONTROL_FALSE;
1563 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput,
1564 &lInputRelated, &lPhysicalType);
1566 nVideoDecoder = nAudioDecoder = -1;
1567 for (i = 0; i < lOutputPins; i++) {
1568 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i,
1569 &lRelated, &lPhysicalType);
1570 if (lPhysicalType == PhysConn_Video_VideoDecoder)
1571 nVideoDecoder = i;
1572 if (lPhysicalType == PhysConn_Audio_AudioDecoder)
1573 nAudioDecoder = i;
1575 if (nVideoDecoder >= 0) {
1576 //connecting given input with video decoder
1577 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput);
1578 if (hr != S_OK) {
1579 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputVideoDecoder, (unsigned int)hr);
1580 return TVI_CONTROL_FALSE;
1583 if (nAudioDecoder >= 0 && lInputRelated >= 0) {
1584 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder,
1585 lInputRelated);
1586 if (hr != S_OK) {
1587 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputAudioDecoder, (unsigned int)hr);
1588 return TVI_CONTROL_FALSE;
1591 return TVI_CONTROL_TRUE;
1595 * \brief adjusts video control (hue,saturation,contrast,brightess)
1597 * \param priv driver's private data
1598 * \param control which control to adjust
1599 * \param value new value for control (0-100)
1601 * \return TVI_CONTROL_TRUE success
1602 * \return TVI_CONTROL_FALSE error
1604 static int set_control(priv_t * priv, int control, int value)
1606 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1607 HRESULT hr;
1609 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n");
1610 if (value < -100 || value > 100 || !priv->pVideoProcAmp)
1611 return TVI_CONTROL_FALSE;
1613 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1614 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1615 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual)
1616 return TVI_CONTROL_FALSE;
1618 lValue = lMin + (value + 100) * (lMax - lMin) / 200;
1620 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256
1622 if (lStepping > lMax) {
1623 mp_msg(MSGT_TV, MSGL_DBG3,
1624 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n",
1625 lStepping, lMax,control);
1626 lStepping = 1;
1628 lValue -= lValue % lStepping;
1629 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue,
1630 VideoProcAmp_Flags_Manual);
1631 if (FAILED(hr))
1632 return TVI_CONTROL_FALSE;
1634 return TVI_CONTROL_TRUE;
1638 * \brief get current value of video control (hue,saturation,contrast,brightess)
1640 * \param priv driver's private data
1641 * \param control which control to adjust
1642 * \param pvalue address of variable thar receives current value
1644 * \return TVI_CONTROL_TRUE success
1645 * \return TVI_CONTROL_FALSE error
1647 static int get_control(priv_t * priv, int control, int *pvalue)
1649 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1650 HRESULT hr;
1652 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n");
1653 if (!pvalue || !priv->pVideoProcAmp)
1654 return TVI_CONTROL_FALSE;
1656 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1657 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1658 if (FAILED(hr))
1659 return TVI_CONTROL_FALSE;
1660 if (lMin == lMax) {
1661 *pvalue = lMin;
1662 return TVI_CONTROL_TRUE;
1665 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags);
1666 if (FAILED(hr))
1667 return TVI_CONTROL_FALSE;
1669 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100;
1671 return TVI_CONTROL_TRUE;
1675 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps
1676 * \param fcc FourCC code for video format
1677 * \param width picture width
1678 * \param height pciture height
1679 * \param fps frames per second (required for bitrate calculation)
1681 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise
1683 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps)
1685 int i;
1686 AM_MEDIA_TYPE mt;
1687 VIDEOINFOHEADER vHdr;
1689 /* Check given fcc in lookup table*/
1690 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */;
1691 if(!img_fmt_list[i].fmt)
1692 return NULL;
1694 memset(&mt, 0, sizeof(AM_MEDIA_TYPE));
1695 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER));
1697 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader);
1698 vHdr.bmiHeader.biWidth = width;
1699 vHdr.bmiHeader.biHeight = height;
1700 //FIXME: is biPlanes required too?
1701 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes;
1702 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits;
1703 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression;
1704 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8;
1705 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps;
1707 mt.pbFormat = (char*)&vHdr;
1708 mt.cbFormat = sizeof(vHdr);
1710 mt.majortype = MEDIATYPE_Video;
1711 mt.subtype = *img_fmt_list[i].subtype;
1712 mt.formattype = FORMAT_VideoInfo;
1714 mt.bFixedSizeSamples = 1;
1715 mt.bTemporalCompression = 0;
1716 mt.lSampleSize = vHdr.bmiHeader.biSizeImage;
1718 return CreateMediaType(&mt);
1722 * \brief extracts fcc,width,height from AM_MEDIA_TYPE
1724 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1725 * \param pfcc address of variable that receives FourCC
1726 * \param pwidth address of variable that receives width
1727 * \param pheight address of variable that recevies height
1729 * \return 1 if data extracted successfully, 0 - otherwise
1731 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc,
1732 int *pwidth, int *pheight)
1734 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n");
1735 if (!pmt)
1736 return 0;
1737 if (!pmt->pbFormat)
1738 return 0;
1739 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0)
1740 return 0;
1741 if (pfcc)
1742 *pfcc = subtype2imgfmt(&(pmt->subtype));
1743 if (pwidth)
1744 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth;
1745 if (pheight)
1746 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight;
1747 return 1;
1751 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE
1753 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1754 * \param pfcc address of variable that receives samplerate
1755 * \param pwidth address of variable that receives number of bits per sample
1756 * \param pheight address of variable that recevies number of channels
1758 * \return 1 if data extracted successfully, 0 - otherwise
1760 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate,
1761 int *pbits, int *pchannels)
1763 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n");
1764 if (!pmt)
1765 return 0;
1766 if (!pmt->pbFormat)
1767 return 0;
1768 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1769 return 0;
1770 if (psamplerate)
1771 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec;
1772 if (pbits)
1773 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample;
1774 if (pchannels)
1775 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels;
1776 return 1;
1780 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels
1782 * \param pmt pointer to AM_MEDIA_TYPE for check
1783 * \param samplerate audio samplerate
1784 * \param bits bits per sample
1785 * \param channels number of audio channels
1787 * \return 1 if AM_MEDIA_TYPE compatible
1788 * \return 0 if not
1790 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate,
1791 int bits, int channels)
1793 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n");
1794 if (!pmt)
1795 return 0;
1796 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0)
1797 return 0;
1798 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0)
1799 return 0;
1800 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1801 return 0;
1802 if (!pmt->pbFormat)
1803 return 0;
1804 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate)
1805 return 0;
1806 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits)
1807 return 0;
1808 if (channels > 0
1809 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels)
1810 return 0;
1812 return 1;
1816 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height
1818 * \param pmt pointer to AM_MEDIA_TYPE for check
1819 * \param fcc FourCC (compression)
1820 * \param width width of picture
1821 * \param height height of picture
1823 * \return 1 if AM_MEDIA_TYPE compatible
1824 & \return 0 if not
1826 * \note
1827 * width and height are currently not used
1829 * \todo
1830 * add width/height check
1832 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width,
1833 int height)
1835 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n");
1836 if (!pmt)
1837 return 0;
1838 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0)
1839 return 0;
1840 if (subtype2imgfmt(&(pmt->subtype)) != fcc)
1841 return 0;
1842 return 1;
1846 * \brief converts DirectShow subtype to MPlayer's IMGFMT
1848 * \param subtype DirectShow subtype for video format
1850 * \return MPlayer's IMGFMT or 0 if error occured
1852 static int subtype2imgfmt(const GUID * subtype)
1854 int i;
1855 for (i = 0; img_fmt_list[i].fmt; i++) {
1856 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0)
1857 return img_fmt_list[i].fmt;
1859 return 0;
1863 * \brief prints filter name and it pins
1865 * \param pFilter - IBaseFilter to get data from
1867 * \return S_OK if success, error code otherwise
1869 static HRESULT show_filter_info(IBaseFilter * pFilter)
1871 char tmp[200];
1872 FILTER_INFO fi;
1873 LPENUMPINS pEnum = 0;
1874 IPin *pPin = 0;
1875 PIN_DIRECTION ThisPinDir;
1876 PIN_INFO pi;
1877 HRESULT hr;
1878 int i;
1880 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n");
1881 memset(&fi, 0, sizeof(fi));
1882 memset(tmp, 0, 200);
1884 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi);
1885 OLE_RELEASE_SAFE(fi.pGraph);
1886 wtoa(fi.achName, tmp, 200);
1887 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:",
1888 pFilter, tmp, fi.pGraph);
1889 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum);
1890 if (FAILED(hr))
1891 return hr;
1892 i = 0;
1893 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1894 memset(&pi, 0, sizeof(pi));
1895 memset(tmp, 0, 200);
1896 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1897 if (ThisPinDir == PINDIR_OUTPUT) {
1898 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1899 wtoa(pi.achName, tmp, 200);
1900 OLE_RELEASE_SAFE(pi.pFilter);
1901 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp);
1902 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1903 mp_msg(MSGT_TV, MSGL_DBG2, ";");
1904 OLE_RELEASE_SAFE(pPin);
1905 i++;
1908 mp_msg(MSGT_TV, MSGL_DBG2, "\n");
1909 OLE_RELEASE_SAFE(pEnum);
1910 return S_OK;
1914 * \brief gets device's frendly in ANSI encoding
1916 * \param pM IMoniker interface, got in enumeration process
1917 * \param category device category
1919 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise
1921 static int get_device_name(IMoniker * pM, char *pBuf, int nLen)
1923 HRESULT hr;
1924 VARIANT var;
1925 IPropertyBag *pPropBag;
1926 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag);
1927 if (FAILED(hr)) {
1928 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n");
1929 return TVI_CONTROL_FALSE;
1931 var.vt = VT_BSTR;
1932 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var,
1933 NULL);
1934 if (FAILED(hr)) {
1935 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var,
1936 NULL);
1938 OLE_RELEASE_SAFE(pPropBag);
1939 if (SUCCEEDED(hr)) {
1940 wtoa(var.bstrVal, pBuf, nLen);
1941 return TVI_CONTROL_TRUE;
1943 return TVI_CONTROL_FALSE;
1947 * \brief find capture device at given index
1949 * \param index device index to search for (-1 mean only print available)
1950 * \param category device category
1952 * \return IBaseFilter interface for capture device with given index
1954 * Sample values for category:
1955 * CLSID_VideoInputDeviceCategory - Video Capture Sources
1956 * CLSID_AudioInputDeviceCategory - Audio Capture Sources
1957 * See DirectShow SDK documentation for other possible values
1959 static IBaseFilter *find_capture_device(int index, REFCLSID category)
1961 IBaseFilter *pFilter = NULL;
1962 ICreateDevEnum *pDevEnum = NULL;
1963 IEnumMoniker *pClassEnum = NULL;
1964 IMoniker *pM;
1965 HRESULT hr;
1966 ULONG cFetched;
1967 int i;
1968 char tmp[DEVICE_NAME_MAX_LEN + 1];
1969 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL,
1970 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
1971 (void *) &pDevEnum);
1972 if (FAILED(hr)) {
1973 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n");
1974 return NULL;
1977 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0);
1978 OLE_RELEASE_SAFE(pDevEnum);
1979 if (FAILED(hr)) {
1980 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n");
1981 return NULL;
1983 if (hr == S_FALSE) {
1984 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n");
1985 return NULL;
1988 OLE_CALL(pClassEnum,Reset);
1989 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) {
1990 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE)
1991 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableGetDeviceName, i);
1992 else
1993 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DeviceName, i, tmp);
1994 if (index != -1 && i == index) {
1995 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_UsingDevice, index, tmp);
1996 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter);
1997 if (FAILED(hr))
1998 pFilter = NULL;
2000 OLE_RELEASE_SAFE(pM);
2002 if (index != -1 && !pFilter) {
2003 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_DeviceNotFound,
2004 index);
2006 OLE_RELEASE_SAFE(pClassEnum);
2008 return pFilter;
2012 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps
2014 * \praram[in] chain chain data structure
2016 * \return S_OK success
2017 * \return E_POINTER one of parameters is NULL
2018 * \return E_FAIL required size of buffer is unknown for given media type
2019 * \return E_OUTOFMEMORY not enough memory
2020 * \return other error code from called methods
2022 * \remarks
2023 * last items of chain->arpmt and chain->arStreamCaps will be NULL
2025 static HRESULT get_available_formats_stream(chain_t *chain)
2027 AM_MEDIA_TYPE **arpmt;
2028 void **pBuf=NULL;
2030 HRESULT hr;
2031 int i, count, size;
2032 int done;
2034 mp_msg(MSGT_TV, MSGL_DBG4,
2035 "tvi_dshow: get_available_formats_stream called\n");
2037 if (!chain->pStreamConfig)
2038 return E_POINTER;
2040 hr=OLE_CALL_ARGS(chain->pStreamConfig, GetNumberOfCapabilities, &count, &size);
2041 if (FAILED(hr)) {
2042 mp_msg(MSGT_TV, MSGL_DBG4,
2043 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n");
2044 return hr;
2046 if (chain->type == video){
2047 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
2048 mp_msg(MSGT_TV, MSGL_DBG4,
2049 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2050 return E_FAIL;
2052 } else if (chain->type == audio){
2053 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) {
2054 mp_msg(MSGT_TV, MSGL_DBG4,
2055 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2056 return E_FAIL;
2058 } else {
2059 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_stream");
2060 return E_FAIL;
2062 done = 0;
2064 arpmt = (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2065 if (arpmt) {
2066 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2068 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2069 if (pBuf) {
2070 memset(pBuf, 0, (count + 1) * sizeof(void *));
2072 for (i = 0; i < count; i++) {
2073 pBuf[i] = malloc(size);
2075 if (!pBuf[i])
2076 break;
2078 hr = OLE_CALL_ARGS(chain->pStreamConfig, GetStreamCaps, i,
2079 &(arpmt[i]), pBuf[i]);
2080 if (FAILED(hr))
2081 break;
2083 if (i == count) {
2084 chain->arpmt = arpmt;
2085 chain->arStreamCaps = pBuf;
2086 done = 1;
2090 if (!done) {
2091 for (i = 0; i < count; i++) {
2092 if (pBuf && pBuf[i])
2093 free(pBuf[i]);
2094 if (arpmt && arpmt[i])
2095 DeleteMediaType(arpmt[i]);
2097 if (pBuf)
2098 free(pBuf);
2099 if (arpmt)
2100 free(arpmt);
2101 if (hr != S_OK) {
2102 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n");
2103 return hr;
2104 } else
2105 return E_OUTOFMEMORY;
2107 return S_OK;
2111 * \brief returns allocates an array and store available media formats for given pin type to it
2113 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder
2114 * \param chain chain data structure
2116 * \return S_OK success
2117 * \return E_POINTER one of given pointers is null
2118 * \return apropriate error code otherwise
2120 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder,
2121 chain_t *chain)
2123 IEnumMediaTypes *pEnum;
2124 int i, count, size;
2125 ULONG cFetched;
2126 AM_MEDIA_TYPE *pmt;
2127 HRESULT hr;
2128 void **pBuf;
2129 AM_MEDIA_TYPE **arpmt; //This will be real array
2130 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps;
2131 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps;
2132 int p1, p2, p3;
2134 mp_msg(MSGT_TV, MSGL_DBG4,
2135 "tvi_dshow: get_available_formats_pin called\n");
2136 if (!pBuilder || !chain->pCaptureFilter)
2137 return E_POINTER;
2139 if (!chain->pCapturePin)
2141 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2142 (IUnknown *) chain->pCaptureFilter,
2143 PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE,
2144 chain->majortype, FALSE, 0, &chain->pCapturePin);
2146 if (!chain->pCapturePin)
2147 return E_POINTER;
2149 if (chain->type == video) {
2150 size = sizeof(VIDEO_STREAM_CONFIG_CAPS);
2151 } else if (chain->type == audio) {
2152 size = sizeof(AUDIO_STREAM_CONFIG_CAPS);
2153 } else {
2154 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_pin");
2155 return E_FAIL;
2158 hr = OLE_CALL_ARGS(chain->pCapturePin, EnumMediaTypes, &pEnum);
2159 if (FAILED(hr)) {
2160 mp_msg(MSGT_TV, MSGL_DBG4,
2161 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n");
2162 return hr;
2164 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) {
2165 if (!pmt)
2166 break;
2168 OLE_CALL(pEnum,Reset);
2170 count = i;
2171 arpmt =
2172 (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2173 if (!arpmt)
2174 return E_OUTOFMEMORY;
2175 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2177 for (i = 0;
2178 i < count
2179 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK;
2180 i++);
2182 OLE_RELEASE_SAFE(pEnum);
2185 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2186 if (!pBuf) {
2187 for (i = 0; i < count; i++)
2188 if (arpmt[i])
2189 DeleteMediaType(arpmt[i]);
2190 free(arpmt);
2191 return E_OUTOFMEMORY;
2193 memset(pBuf, 0, (count + 1) * sizeof(void *));
2195 for (i = 0; i < count; i++) {
2196 pBuf[i] = malloc(size);
2197 if (!pBuf[i])
2198 break;
2199 memset(pBuf[i], 0, size);
2201 if (chain->type == video) {
2202 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i];
2203 extract_video_format(arpmt[i], NULL, &p1, &p2);
2204 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx =
2206 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy =
2208 } else {
2209 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i];
2210 extract_audio_format(arpmt[i], &p1, &p2, &p3);
2211 pAudioCaps->MaximumSampleFrequency =
2212 pAudioCaps->MinimumSampleFrequency = p1;
2213 pAudioCaps->MaximumBitsPerSample =
2214 pAudioCaps->MinimumBitsPerSample = p2;
2215 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3;
2219 if (i != count) {
2220 for (i = 0; i < count; i++) {
2221 if (arpmt[i])
2222 DeleteMediaType(arpmt[i]);
2223 if (pBuf[i])
2224 free(pBuf[i]);
2226 free(arpmt);
2227 free(pBuf);
2228 return E_OUTOFMEMORY;
2230 chain->arpmt = arpmt;
2231 chain->arStreamCaps = pBuf;
2233 return S_OK;
2237 *---------------------------------------------------------------------------------------
2239 * Public methods
2241 *---------------------------------------------------------------------------------------
2244 * \brief fills given buffer with audio data (usually one block)
2246 * \param priv driver's private data structure
2247 * \param buffer buffer to store data to
2248 * \param len buffer's size in bytes (usually one block size)
2250 * \return audio pts if audio present, 1 - otherwise
2252 static double grab_audio_frame(priv_t * priv, char *buffer, int len)
2254 int bytes = 0;
2255 int i;
2256 double pts;
2257 grabber_ringbuffer_t *rb = priv->chains[1]->rbuf;
2258 grabber_ringbuffer_t *vrb = priv->chains[0]->rbuf;
2260 if (!rb || !rb->ringbuffer)
2261 return 1;
2263 if(vrb && vrb->tStart<0){
2264 memset(buffer,0,len);
2265 return 0;
2267 if(vrb && rb->tStart<0)
2268 rb->tStart=vrb->tStart;
2270 if (len < rb->blocksize)
2271 bytes = len;
2272 else
2273 bytes = rb->blocksize;
2275 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n",
2276 rb->count, len);
2277 if(!rb->count){
2278 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2279 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2280 if(!rb->count){
2281 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2282 return 0;
2284 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2287 EnterCriticalSection(rb->pMutex);
2288 pts=rb->dpts[rb->head]-rb->tStart;
2289 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2290 rb->head = (rb->head + 1) % rb->buffersize;
2291 rb->count--;
2292 LeaveCriticalSection(rb->pMutex);
2293 return pts;
2297 * \brief returns audio frame size
2299 * \param priv driver's private data structure
2301 * \return audio block size if audio enabled and 1 - otherwise
2303 static int get_audio_framesize(priv_t * priv)
2305 if (!priv->chains[1]->rbuf)
2306 return 1; //no audio
2307 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->chains[1]->rbuf->blocksize);
2308 return priv->chains[1]->rbuf->blocksize;
2311 #ifdef HAVE_TV_TELETEXT
2312 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp)
2314 if(!priv || !ptsp)
2315 return TVI_CONTROL_FALSE;
2317 //STUBS!!!
2318 ptsp->interlaced=0;
2319 ptsp->offset=256;
2321 ptsp->sampling_rate=27e6;
2322 ptsp->samples_per_line=720;
2324 ptsp->count[0]=16;
2325 ptsp->count[1]=16;
2326 //END STUBS!!!
2327 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]);
2329 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",
2330 ptsp->sampling_rate,
2331 ptsp->offset,
2332 ptsp->samples_per_line,
2333 ptsp->interlaced?"Yes":"No",
2334 ptsp->count[0],
2335 ptsp->count[1]);
2337 return TVI_CONTROL_TRUE;
2340 static void vbi_grabber(priv_t* priv)
2342 grabber_ringbuffer_t *rb = priv->chains[2]->rbuf;
2343 int i;
2344 unsigned char* buf;
2345 if (!rb || !rb->ringbuffer)
2346 return;
2348 buf=calloc(1,rb->blocksize);
2349 for(i=0; i<23 && rb->count; i++){
2350 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize);
2351 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf);
2352 rb->head = (rb->head + 1) % rb->buffersize;
2353 rb->count--;
2355 free(buf);
2357 #endif //HAVE_TV_TELETEXT
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 #ifdef HAVE_TV_TELETEXT
2403 vbi_grabber(priv);
2404 #endif
2405 return pts;
2409 * \brief returns frame size
2411 * \param priv driver's private data structure
2413 * \return frame size if video present, 0 - otherwise
2415 static int get_video_framesize(priv_t * priv)
2417 // if(!priv->pmtVideo) return 1; //no video
2418 // return(priv->pmtVideo->lSampleSize);
2419 if (!priv->chains[0]->rbuf)
2420 return 1; //no video
2421 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->chains[0]->rbuf->blocksize);
2422 return priv->chains[0]->rbuf->blocksize;
2426 * \brief calculate audio buffer size
2427 * \param video_buf_size size of video buffer in bytes
2428 * \param video_bitrate video bit rate
2429 * \param audio_bitrate audio bit rate
2430 * \return audio buffer isze in bytes
2432 * \remarks length of video buffer and resulted audio buffer calculated in
2433 * seconds will be the same.
2435 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate)
2437 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate);
2438 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n",
2439 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size);
2440 return audio_buf_size;
2444 * \brief common chain initialization routine
2445 * \param chain chain data structure
2447 * \note pCaptureFilter member should be initialized before call to this routine
2449 static HRESULT init_chain_common(ICaptureGraphBuilder2 *pBuilder, chain_t *chain)
2451 HRESULT hr;
2452 int i;
2454 if(!chain->pCaptureFilter)
2455 return E_POINTER;
2457 show_filter_info(chain->pCaptureFilter);
2459 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2460 (IUnknown *) chain->pCaptureFilter,
2461 PINDIR_OUTPUT, chain->pin_category,
2462 chain->majortype, FALSE, 0, &chain->pCapturePin);
2464 if (FAILED(hr)) {
2465 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
2466 return hr;
2469 hr = OLE_CALL_ARGS(pBuilder, FindInterface,
2470 chain->pin_category,
2471 chain->majortype,
2472 chain->pCaptureFilter,
2473 &IID_IAMStreamConfig,
2474 (void **) &(chain->pStreamConfig));
2475 if (FAILED(hr))
2476 chain->pStreamConfig = NULL;
2479 Getting available video formats (last pointer in array will be NULL)
2480 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as
2481 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default
2482 min/max values.
2484 hr = get_available_formats_stream(chain);
2485 if (FAILED(hr)) {
2486 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr);
2487 hr = get_available_formats_pin(pBuilder, chain);
2488 if(FAILED(hr)){
2489 return hr;
2492 chain->nFormatUsed = 0;
2494 //If argument to CreateMediaType is NULL then result will be NULL too.
2495 chain->pmt = CreateMediaType(chain->arpmt[0]);
2497 for (i = 0; chain->arpmt[i]; i++)
2498 DisplayMediaType("Available format", chain->arpmt[i]);
2500 return S_OK;
2503 * \brief build video stream chain in graph
2504 * \param priv private data structure
2506 * \return S_OK if chain was built successfully, apropriate error code otherwise
2508 static HRESULT build_video_chain(priv_t *priv)
2510 HRESULT hr;
2512 if(priv->chains[0]->rbuf)
2513 return S_OK;
2515 if (priv->chains[0]->pStreamConfig) {
2516 hr = OLE_CALL_ARGS(priv->chains[0]->pStreamConfig, SetFormat, priv->chains[0]->pmt);
2517 if (FAILED(hr)) {
2518 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectVideoFormat, (unsigned int)hr);
2522 priv->chains[0]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2523 if(!priv->chains[0]->rbuf)
2524 return E_OUTOFMEMORY;
2526 if (priv->tv_param->buffer_size >= 0) {
2527 priv->chains[0]->rbuf->buffersize = priv->tv_param->buffer_size;
2528 } else {
2529 priv->chains[0]->rbuf->buffersize = 16;
2532 priv->chains[0]->rbuf->buffersize *= 1024 * 1024;
2533 hr=build_sub_graph(priv, priv->chains[0], &PIN_CATEGORY_CAPTURE);
2534 if(FAILED(hr)){
2535 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVideoSubGraph,(unsigned int)hr);
2536 return hr;
2538 return S_OK;
2542 * \brief build audio stream chain in graph
2543 * \param priv private data structure
2545 * \return S_OK if chain was built successfully, apropriate error code otherwise
2547 static HRESULT build_audio_chain(priv_t *priv)
2549 HRESULT hr;
2551 if(priv->chains[1]->rbuf)
2552 return S_OK;
2554 if(priv->immediate_mode)
2555 return S_OK;
2557 if (priv->chains[1]->pStreamConfig) {
2558 hr = OLE_CALL_ARGS(priv->chains[1]->pStreamConfig, SetFormat,
2559 priv->chains[1]->pmt);
2560 if (FAILED(hr)) {
2561 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectAudioFormat, (unsigned int)hr);
2565 if(priv->chains[1]->pmt){
2566 priv->chains[1]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2567 if(!priv->chains[1]->rbuf)
2568 return E_OUTOFMEMORY;
2570 /* let the audio buffer be the same size (in seconds) than video one */
2571 priv->chains[1]->rbuf->buffersize=audio_buf_size_from_video(
2572 priv->chains[0]->rbuf->buffersize,
2573 (((VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat)->dwBitRate),
2574 (((WAVEFORMATEX *) (priv->chains[1]->pmt->pbFormat))->nAvgBytesPerSec));
2576 hr=build_sub_graph(priv, priv->chains[1],&PIN_CATEGORY_CAPTURE);
2577 if(FAILED(hr)){
2578 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildAudioSubGraph,(unsigned int)hr);
2579 return 0;
2582 return S_OK;
2586 * \brief build VBI stream chain in graph
2587 * \param priv private data structure
2589 * \return S_OK if chain was built successfully, apropriate error code otherwise
2591 static HRESULT build_vbi_chain(priv_t *priv)
2593 #ifdef HAVE_TV_TELETEXT
2594 HRESULT hr;
2596 if(priv->chains[2]->rbuf)
2597 return S_OK;
2599 if(priv->tv_param->tdevice)
2601 priv->chains[2]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2602 if(!priv->chains[2]->rbuf)
2603 return E_OUTOFMEMORY;
2605 init_ringbuffer(priv->chains[2]->rbuf,24,priv->tsp.bufsize);
2607 hr=build_sub_graph(priv, priv->chains[2],&PIN_CATEGORY_VBI);
2608 if(FAILED(hr)){
2609 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVBISubGraph,(unsigned int)hr);
2610 return 0;
2613 #endif
2614 return S_OK;
2618 * \brief playback/capture real start
2620 * \param priv driver's private data structure
2622 * \return 1 if success, 0 - otherwise
2624 * TODO: move some code from init() here
2626 static int start(priv_t * priv)
2628 HRESULT hr;
2630 hr = build_video_chain(priv);
2631 if(FAILED(hr))
2632 return 0;
2634 hr = build_audio_chain(priv);
2635 if(FAILED(hr))
2636 return 0;
2638 hr = build_vbi_chain(priv);
2639 if(FAILED(hr))
2640 return 0;
2643 Graph is ready to capture. Starting graph.
2645 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2646 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n");
2647 usec_sleep(10000000);
2648 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n");
2650 if (!priv->pMediaControl) {
2651 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)E_POINTER);
2652 return 0;
2654 hr = OLE_CALL(priv->pMediaControl, Run);
2655 if (FAILED(hr)) {
2656 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableStartGraph, (unsigned int)hr);
2657 return 0;
2659 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n");
2660 priv->state = 1;
2662 return (1);
2666 * \brief driver initialization
2668 * \param priv driver's private data structure
2670 * \return 1 if success, 0 - otherwise
2672 static int init(priv_t * priv)
2674 HRESULT hr;
2675 int result = 0;
2676 long lInput, lTunerInput;
2677 IEnumFilters *pEnum;
2678 IBaseFilter *pFilter;
2679 IPin *pVPOutPin;
2680 int i;
2682 priv->state=0;
2684 CoInitialize(NULL);
2686 for(i=0; i<3;i++)
2687 priv->chains[i] = calloc(1, sizeof(chain_t));
2689 priv->chains[0]->type=video;
2690 priv->chains[0]->majortype=&MEDIATYPE_Video;
2691 priv->chains[0]->pin_category=&PIN_CATEGORY_CAPTURE;
2692 priv->chains[1]->type=audio;
2693 priv->chains[1]->majortype=&MEDIATYPE_Audio;
2694 priv->chains[1]->pin_category=&PIN_CATEGORY_CAPTURE;
2695 priv->chains[2]->type=vbi;
2696 priv->chains[2]->majortype=&MEDIATYPE_VBI;
2697 priv->chains[2]->pin_category=&PIN_CATEGORY_VBI;
2700 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL,
2701 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2702 (void **) &priv->pGraph);
2703 if(FAILED(hr)){
2704 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr);
2705 break;
2707 //Debug
2708 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2709 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister));
2712 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL,
2713 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2,
2714 (void **) &priv->pBuilder);
2715 if(FAILED(hr)){
2716 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr);
2717 break;
2720 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph);
2721 if(FAILED(hr)){
2722 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr);
2723 break;
2726 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n");
2727 priv->chains[0]->pCaptureFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory);
2728 if(!priv->chains[0]->pCaptureFilter){
2729 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoVideoCaptureDevice);
2730 break;
2732 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[0]->pCaptureFilter, NULL);
2733 if(FAILED(hr)){
2734 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2735 break;
2737 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n");
2738 if (priv->adev_index != -1) {
2739 priv->chains[1]->pCaptureFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices
2740 if(!priv->chains[1]->pCaptureFilter){
2741 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoAudioCaptureDevice);
2742 break;
2745 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[1]->pCaptureFilter, NULL);
2746 if(FAILED(hr)){
2747 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2748 break;
2750 } else
2751 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[1]->pCaptureFilter);
2753 /* increase refrence counter for capture filter ad store pointer into vbi chain structure too */
2754 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[2]->pCaptureFilter);
2756 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp);
2757 if (FAILED(hr) && hr != E_NOINTERFACE)
2758 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr);
2760 if (hr != S_OK) {
2761 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_VideoAdjustigNotSupported);
2762 priv->pVideoProcAmp = NULL;
2765 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2766 &PIN_CATEGORY_CAPTURE,
2767 priv->chains[0]->majortype,
2768 priv->chains[0]->pCaptureFilter,
2769 &IID_IAMCrossbar, (void **) &(priv->pCrossbar));
2770 if (FAILED(hr)) {
2771 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_SelectingInputNotSupported);
2772 priv->pCrossbar = NULL;
2775 if (priv->tv_param->amode >= 0) {
2776 IAMTVAudio *pTVAudio;
2777 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->chains[0]->pCaptureFilter,&IID_IAMTVAudio, (void *) &pTVAudio);
2778 if (hr == S_OK) {
2779 switch (priv->tv_param->amode) {
2780 case 0:
2781 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO);
2782 break;
2783 case 1:
2784 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO);
2785 break;
2786 case 2:
2787 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2788 AMTVAUDIO_MODE_LANG_A);
2789 break;
2790 case 3:
2791 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2792 AMTVAUDIO_MODE_LANG_B);
2793 break;
2795 OLE_RELEASE_SAFE(pTVAudio);
2796 if (FAILED(hr))
2797 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_UnableSetAudioMode, priv->tv_param->amode,(unsigned int)hr);
2801 // Video chain initialization
2802 hr = init_chain_common(priv->pBuilder, priv->chains[0]);
2803 if(FAILED(hr))
2804 break;
2807 Audio chain initialization
2808 Since absent audio stream is not fatal,
2809 at least one NULL pointer should be kept in format arrays
2810 (to avoid another additional check everywhere for array presence).
2812 hr = init_chain_common(priv->pBuilder, priv->chains[1]);
2813 if(FAILED(hr))
2815 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize audio chain (Error:0x%x). Audio disabled\n", (unsigned long)hr);
2816 priv->chains[1]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2817 priv->chains[1]->arStreamCaps=calloc(1, sizeof(void*));
2821 VBI chain initialization
2822 Since absent VBI stream is not fatal,
2823 at least one NULL pointer should be kept in format arrays
2824 (to avoid another additional check everywhere for array presence).
2826 hr = init_chain_common(priv->pBuilder, priv->chains[2]);
2827 if(FAILED(hr))
2829 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize VBI chain (Error:0x%x). Teletext disabled\n", (unsigned long)hr);
2830 priv->chains[2]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2831 priv->chains[2]->arStreamCaps=calloc(1, sizeof(void*));
2834 if (!priv->chains[0]->pStreamConfig)
2835 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_ChangingWidthHeightNotSupported);
2837 if (!priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]
2838 || !extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
2839 &(priv->fcc), &(priv->width),
2840 &(priv->height))) {
2841 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingVideoFormatStruct);
2842 break;
2845 if (priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]) {
2846 if (!extract_audio_format(priv->chains[1]->pmt, &(priv->samplerate), NULL, NULL)) {
2847 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingAudioFormatStruct);
2848 DisplayMediaType("audio format failed",priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]);
2849 break;
2853 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl);
2854 if(FAILED(hr)){
2855 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)hr);
2856 break;
2858 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2859 &PIN_CATEGORY_CAPTURE, NULL,
2860 priv->chains[0]->pCaptureFilter,
2861 &IID_IAMTVTuner, (void **) &(priv->pTVTuner));
2863 if (!priv->pTVTuner) {
2864 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr);
2867 // shows Tuner capabilities
2868 get_capabilities(priv);
2870 if (priv->pTVTuner) {
2871 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode,
2872 chanlist2country(priv->tv_param->chanlist));
2873 if(FAILED(hr)){
2874 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr);
2877 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV);
2878 if(FAILED(hr)){
2879 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr);
2880 break;
2883 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
2884 if(FAILED(hr)){
2885 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr);
2886 break;
2889 /* small hack */
2890 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna;
2892 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput);
2893 if(FAILED(hr)){
2894 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr);
2895 break;
2901 for VIVO cards we should check if preview pin is available on video capture device.
2902 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter.
2903 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph
2905 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
2906 (IUnknown *) priv->chains[0]->pCaptureFilter,
2907 PINDIR_OUTPUT,
2908 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE,
2909 0, (IPin **) & pVPOutPin);
2910 if (SUCCEEDED(hr)) {
2911 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin);
2912 OLE_RELEASE_SAFE(pVPOutPin);
2914 if (FAILED(hr)) {
2915 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableTerminateVPPin, (unsigned int)hr);
2916 break;
2920 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
2921 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
2922 LPVIDEOWINDOW pVideoWindow;
2923 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
2924 if (SUCCEEDED(hr))
2926 if(priv->tv_param->hidden_vp_renderer){
2927 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
2928 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
2929 }else
2931 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter);
2933 OLE_RELEASE_SAFE(pVideoWindow);
2935 OLE_RELEASE_SAFE(pFilter);
2937 OLE_RELEASE_SAFE(pEnum);
2938 if(priv->tv_param->system_clock)
2940 LPREFERENCECLOCK rc;
2941 IBaseFilter* pBF;
2942 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL,
2943 CLSCTX_INPROC_SERVER, &IID_IReferenceClock,
2944 (void *) &rc);
2946 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF);
2947 OLE_CALL_ARGS(pBF,SetSyncSource,rc);
2949 #ifdef HAVE_TV_TELETEXT
2950 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE)
2951 break;
2952 #endif
2953 result = 1;
2954 } while(0);
2956 if (!result){
2957 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_GraphInitFailure);
2958 uninit(priv);
2960 return result;
2964 * \brief chain uninitialization
2965 * \param chain chain data structure
2967 static void destroy_chain(chain_t *chain)
2969 int i;
2971 if(!chain)
2972 return;
2974 OLE_RELEASE_SAFE(chain->pStreamConfig);
2975 OLE_RELEASE_SAFE(chain->pCaptureFilter);
2976 OLE_RELEASE_SAFE(chain->pCSGCB);
2977 OLE_RELEASE_SAFE(chain->pCapturePin);
2978 OLE_RELEASE_SAFE(chain->pSGIn);
2979 OLE_RELEASE_SAFE(chain->pSG);
2980 OLE_RELEASE_SAFE(chain->pSGF);
2982 if (chain->pmt)
2983 DeleteMediaType(chain->pmt);
2985 if (chain->arpmt) {
2986 for (i = 0; chain->arpmt[i]; i++) {
2987 DeleteMediaType(chain->arpmt[i]);
2989 free(chain->arpmt);
2992 if (chain->arStreamCaps) {
2993 for (i = 0; chain->arStreamCaps[i]; i++) {
2994 free(chain->arStreamCaps[i]);
2996 free(chain->arStreamCaps);
2999 if (chain->rbuf) {
3000 destroy_ringbuffer(chain->rbuf);
3001 free(chain->rbuf);
3002 chain->rbuf = NULL;
3004 free(chain);
3007 * \brief driver uninitialization
3009 * \param priv driver's private data structure
3011 * \return always 1
3013 static int uninit(priv_t * priv)
3015 int i;
3016 if (!priv)
3017 return (1);
3018 //Debug
3019 if (priv->dwRegister) {
3020 RemoveFromRot(priv->dwRegister);
3022 #ifdef HAVE_TV_TELETEXT
3023 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1);
3024 #endif
3025 //stop audio grabber thread
3027 if (priv->state && priv->pMediaControl) {
3028 OLE_CALL(priv->pMediaControl, Stop);
3030 OLE_RELEASE_SAFE(priv->pMediaControl);
3031 priv->state = 0;
3033 if (priv->pGraph) {
3034 if (priv->chains[0]->pCaptureFilter)
3035 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[0]->pCaptureFilter);
3036 if (priv->chains[1]->pCaptureFilter)
3037 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[1]->pCaptureFilter);
3039 OLE_RELEASE_SAFE(priv->pCrossbar);
3040 OLE_RELEASE_SAFE(priv->pVideoProcAmp);
3041 OLE_RELEASE_SAFE(priv->pGraph);
3042 OLE_RELEASE_SAFE(priv->pBuilder);
3043 if(priv->freq_table){
3044 priv->freq_table_len=-1;
3045 free(priv->freq_table);
3046 priv->freq_table=NULL;
3049 for(i=0; i<3;i++)
3051 destroy_chain(priv->chains[i]);
3052 priv->chains[i] = NULL;
3054 CoUninitialize();
3055 return (1);
3059 * \brief driver pre-initialization
3061 * \param device string, containing device name in form "x[.y]", where x is video capture device
3062 * (default: 0, first available); y (if given) sets audio capture device
3064 * \return 1 if success,0 - otherwise
3066 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param)
3068 tvi_handle_t *h;
3069 priv_t *priv;
3070 int a;
3072 h = new_handle();
3073 if (!h)
3074 return (NULL);
3076 priv = h->priv;
3078 memset(priv, 0, sizeof(priv_t));
3079 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3080 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3081 priv->adev_index = -1;
3082 priv->freq_table_len=-1;
3083 priv->tv_param=tv_param;
3085 if (tv_param->device) {
3086 if (sscanf(tv_param->device, "%d", &a) == 1) {
3087 priv->dev_index = a;
3088 } else {
3089 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceParam, tv_param->device);
3090 free_handle(h);
3091 return NULL;
3093 if (priv->dev_index < 0) {
3094 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceIndex, a);
3095 free_handle(h);
3096 return NULL;
3099 if (tv_param->adevice) {
3100 if (sscanf(tv_param->adevice, "%d", &a) == 1) {
3101 priv->adev_index = a;
3102 } else {
3103 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceParam, tv_param->adevice);
3104 free_handle(h);
3105 return NULL;
3107 if (priv->dev_index < 0) {
3108 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceIndex, a);
3109 free_handle(h);
3110 return NULL;
3113 return h;
3117 * \brief driver's ioctl handler
3119 * \param priv driver's private data structure
3120 * \param cmd ioctl command
3121 * \param arg ioct command's parameter
3123 * \return TVI_CONTROL_TRUE if success
3124 * \return TVI_CONTROL_FALSE if failure
3125 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called
3127 static int control(priv_t * priv, int cmd, void *arg)
3129 switch (cmd) {
3130 /* need rewrite */
3131 case TVI_CONTROL_VID_SET_FORMAT:
3133 int fcc, i,j;
3134 void* tmp,*tmp2;
3135 int result = TVI_CONTROL_TRUE;
3137 if (priv->state)
3138 return TVI_CONTROL_FALSE;
3139 fcc = *(int *) arg;
3141 if(!priv->chains[0]->arpmt)
3142 return TVI_CONTROL_FALSE;
3143 for (i = 0; priv->chains[0]->arpmt[i]; i++)
3144 if (check_video_format
3145 (priv->chains[0]->arpmt[i], fcc, priv->width, priv->height))
3146 break;
3147 if (!priv->chains[0]->arpmt[i])
3149 int fps = 0;
3150 VIDEOINFOHEADER* Vhdr = NULL;
3151 AM_MEDIA_TYPE *pmt;
3153 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));
3155 if (priv->chains[0]->arpmt[0])
3156 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->arpmt[0]->pbFormat;
3158 if(Vhdr && Vhdr->bmiHeader.biSizeImage)
3159 fps = Vhdr->dwBitRate / (8 * Vhdr->bmiHeader.biSizeImage);
3161 pmt=create_video_format(fcc, priv->width, priv->height, fps);
3162 if(!pmt)
3164 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to create AM_MEDIA_TYPE structure for given format\n");
3165 return TVI_CONTROL_FALSE;
3167 priv->chains[0]->arpmt=realloc(priv->chains[0]->arpmt, (i+2)*sizeof(AM_MEDIA_TYPE*));
3168 priv->chains[0]->arpmt[i+1] = NULL;
3169 priv->chains[0]->arpmt[i] = pmt;
3171 priv->chains[0]->arStreamCaps=realloc(priv->chains[0]->arStreamCaps, (i+2)*sizeof(void*));
3172 priv->chains[0]->arpmt[i+1] = NULL;
3174 result = TVI_CONTROL_FALSE;
3178 tmp=priv->chains[0]->arpmt[i];
3179 tmp2=priv->chains[0]->arStreamCaps[i];
3180 for(j=i; j>0; j--)
3182 priv->chains[0]->arpmt[j] = priv->chains[0]->arpmt[j-1];
3183 priv->chains[0]->arStreamCaps[j] = priv->chains[0]->arStreamCaps[j-1];
3185 priv->chains[0]->arpmt[0] = tmp;
3186 priv->chains[0]->arStreamCaps[0] = tmp2;
3188 priv->chains[0]->nFormatUsed = 0;
3190 if (priv->chains[0]->pmt)
3191 DeleteMediaType(priv->chains[0]->pmt);
3192 priv->chains[0]->pmt =
3193 CreateMediaType(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]);
3194 DisplayMediaType("VID_SET_FORMAT", priv->chains[0]->pmt);
3196 Setting width & height to preferred by driver values
3198 extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
3199 &(priv->fcc), &(priv->width),
3200 &(priv->height));
3201 return result;
3203 case TVI_CONTROL_VID_GET_FORMAT:
3205 if(!priv->chains[0]->pmt)
3206 return TVI_CONTROL_FALSE;
3208 Build video chain (for video format negotiation).
3209 If this was done before, routine will do nothing.
3211 build_video_chain(priv);
3212 DisplayMediaType("VID_GET_FORMAT", priv->chains[0]->pmt);
3213 if (priv->fcc) {
3214 *(int *) arg = priv->fcc;
3215 return (TVI_CONTROL_TRUE);
3216 } else
3217 return (TVI_CONTROL_FALSE);
3219 case TVI_CONTROL_VID_SET_WIDTH:
3221 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3222 VIDEOINFOHEADER *Vhdr;
3223 int width = *(int *) arg;
3224 if (priv->state)
3225 return TVI_CONTROL_FALSE;
3227 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3228 if (!pCaps)
3229 return TVI_CONTROL_FALSE;
3230 if (width < pCaps->MinOutputSize.cx
3231 || width > pCaps->MaxOutputSize.cx)
3232 return TVI_CONTROL_FALSE;
3234 if (width % pCaps->OutputGranularityX)
3235 return TVI_CONTROL_FALSE;
3237 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3238 return TVI_CONTROL_FALSE;
3239 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3240 Vhdr->bmiHeader.biWidth = width;
3241 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3242 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3243 Vhdr->bmiHeader.biHeight) >> 3;
3245 priv->width = width;
3247 return (TVI_CONTROL_TRUE);
3249 case TVI_CONTROL_VID_GET_WIDTH:
3251 if (priv->width) {
3252 *(int *) arg = priv->width;
3253 return (TVI_CONTROL_TRUE);
3254 } else
3255 return TVI_CONTROL_FALSE;
3257 case TVI_CONTROL_VID_CHK_WIDTH:
3259 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3260 int width = *(int *) arg;
3261 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3262 if (!pCaps)
3263 return TVI_CONTROL_FALSE;
3264 if (width < pCaps->MinOutputSize.cx
3265 || width > pCaps->MaxOutputSize.cx)
3266 return TVI_CONTROL_FALSE;
3268 if (width % pCaps->OutputGranularityX)
3269 return TVI_CONTROL_FALSE;
3270 return (TVI_CONTROL_TRUE);
3272 case TVI_CONTROL_VID_SET_HEIGHT:
3274 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3275 VIDEOINFOHEADER *Vhdr;
3276 int height = *(int *) arg;
3277 if (priv->state)
3278 return TVI_CONTROL_FALSE;
3280 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3281 if (!pCaps)
3282 return TVI_CONTROL_FALSE;
3283 if (height < pCaps->MinOutputSize.cy
3284 || height > pCaps->MaxOutputSize.cy)
3285 return TVI_CONTROL_FALSE;
3287 if (height % pCaps->OutputGranularityY)
3288 return TVI_CONTROL_FALSE;
3290 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3291 return TVI_CONTROL_FALSE;
3292 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3294 if (Vhdr->bmiHeader.biHeight < 0)
3295 Vhdr->bmiHeader.biHeight = -height;
3296 else
3297 Vhdr->bmiHeader.biHeight = height;
3298 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3299 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3300 Vhdr->bmiHeader.biHeight) >> 3;
3302 priv->height = height;
3303 return (TVI_CONTROL_TRUE);
3305 case TVI_CONTROL_VID_GET_HEIGHT:
3307 if (priv->height) {
3308 *(int *) arg = priv->height;
3309 return (TVI_CONTROL_TRUE);
3310 } else
3311 return TVI_CONTROL_FALSE;
3313 case TVI_CONTROL_VID_CHK_HEIGHT:
3315 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3316 int height = *(int *) arg;
3317 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3318 if (!pCaps)
3319 return TVI_CONTROL_FALSE;
3320 if (height < pCaps->MinOutputSize.cy
3321 || height > pCaps->MaxOutputSize.cy)
3322 return TVI_CONTROL_FALSE;
3324 if (height % pCaps->OutputGranularityY)
3325 return TVI_CONTROL_FALSE;
3327 return (TVI_CONTROL_TRUE);
3329 case TVI_CONTROL_IS_AUDIO:
3330 if (!priv->chains[1]->pmt)
3331 return TVI_CONTROL_FALSE;
3332 else
3333 return TVI_CONTROL_TRUE;
3334 case TVI_CONTROL_IS_VIDEO:
3335 return TVI_CONTROL_TRUE;
3336 case TVI_CONTROL_AUD_GET_FORMAT:
3338 *(int *) arg = AF_FORMAT_S16_LE;
3339 if (!priv->chains[1]->pmt)
3340 return TVI_CONTROL_FALSE;
3341 else
3342 return TVI_CONTROL_TRUE;
3344 case TVI_CONTROL_AUD_GET_CHANNELS:
3346 *(int *) arg = priv->channels;
3347 if (!priv->chains[1]->pmt)
3348 return TVI_CONTROL_FALSE;
3349 else
3350 return TVI_CONTROL_TRUE;
3352 case TVI_CONTROL_AUD_SET_SAMPLERATE:
3354 int i, samplerate;
3355 if (priv->state)
3356 return TVI_CONTROL_FALSE;
3357 if (!priv->chains[1]->arpmt[0])
3358 return TVI_CONTROL_FALSE;
3360 samplerate = *(int *) arg;;
3362 for (i = 0; priv->chains[1]->arpmt[i]; i++)
3363 if (check_audio_format
3364 (priv->chains[1]->arpmt[i], samplerate, 16, priv->channels))
3365 break;
3366 if (!priv->chains[1]->arpmt[i]) {
3367 //request not found. failing back to first available
3368 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_SamplerateNotsupported, samplerate);
3369 i = 0;
3371 if (priv->chains[1]->pmt)
3372 DeleteMediaType(priv->chains[1]->pmt);
3373 priv->chains[1]->pmt = CreateMediaType(priv->chains[1]->arpmt[i]);
3374 extract_audio_format(priv->chains[1]->arpmt[i], &(priv->samplerate),
3375 NULL, &(priv->channels));
3376 return TVI_CONTROL_TRUE;
3378 case TVI_CONTROL_AUD_GET_SAMPLERATE:
3380 *(int *) arg = priv->samplerate;
3381 if (!priv->samplerate)
3382 return TVI_CONTROL_FALSE;
3383 if (!priv->chains[1]->pmt)
3384 return TVI_CONTROL_FALSE;
3385 else
3386 return TVI_CONTROL_TRUE;
3388 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
3390 WAVEFORMATEX *pWF;
3391 if (!priv->chains[1]->pmt)
3392 return TVI_CONTROL_FALSE;
3393 if (!priv->chains[1]->pmt->pbFormat)
3394 return TVI_CONTROL_FALSE;
3395 pWF = (WAVEFORMATEX *) priv->chains[1]->pmt->pbFormat;
3396 *(int *) arg = pWF->wBitsPerSample / 8;
3397 return TVI_CONTROL_TRUE;
3399 case TVI_CONTROL_IS_TUNER:
3401 if (!priv->pTVTuner)
3402 return TVI_CONTROL_FALSE;
3404 return (TVI_CONTROL_TRUE);
3406 case TVI_CONTROL_TUN_SET_NORM:
3408 IAMAnalogVideoDecoder *pVD;
3409 long lAnalogFormat;
3410 int i;
3411 HRESULT hr;
3413 i = *(int *) arg;
3414 i--;
3415 if (i < 0 || i >= tv_available_norms_count)
3416 return TVI_CONTROL_FALSE;
3417 lAnalogFormat = tv_norms[tv_available_norms[i]].index;
3419 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3420 if (hr != S_OK)
3421 return TVI_CONTROL_FALSE;
3422 hr = OLE_CALL_ARGS(pVD, put_TVFormat, lAnalogFormat);
3423 OLE_RELEASE_SAFE(pVD);
3424 if (FAILED(hr))
3425 return (TVI_CONTROL_FALSE);
3426 else
3427 return (TVI_CONTROL_TRUE);
3429 case TVI_CONTROL_TUN_GET_NORM:
3431 long lAnalogFormat;
3432 int i;
3433 HRESULT hr;
3434 IAMAnalogVideoDecoder *pVD;
3436 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3437 if (hr == S_OK) {
3438 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat);
3439 OLE_RELEASE_SAFE(pVD);
3442 if (FAILED(hr)) { //trying another method
3443 if (!priv->pTVTuner)
3444 return TVI_CONTROL_FALSE;
3445 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat);
3446 if (FAILED(hr))
3447 return TVI_CONTROL_FALSE;
3449 for (i = 0; i < tv_available_norms_count; i++) {
3450 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) {
3451 *(int *) arg = i + 1;
3452 return TVI_CONTROL_TRUE;
3455 return (TVI_CONTROL_FALSE);
3457 case TVI_CONTROL_SPC_GET_NORMID:
3459 int i;
3460 if (!priv->pTVTuner)
3461 return TVI_CONTROL_FALSE;
3462 for (i = 0; i < tv_available_norms_count; i++) {
3463 if (!strcasecmp
3464 (tv_norms[tv_available_norms[i]].name, (char *) arg)) {
3465 *(int *) arg = i + 1;
3466 return TVI_CONTROL_TRUE;
3469 return TVI_CONTROL_FALSE;
3471 case TVI_CONTROL_SPC_SET_INPUT:
3473 return set_crossbar_input(priv, *(int *) arg);
3475 case TVI_CONTROL_TUN_GET_FREQ:
3477 unsigned long lFreq;
3478 int ret;
3479 if (!priv->pTVTuner)
3480 return TVI_CONTROL_FALSE;
3482 ret = get_frequency(priv, &lFreq);
3483 lFreq = lFreq * 16 / 1000000; //convert from Hz to 1/16 MHz units
3485 *(unsigned long *) arg = lFreq;
3486 return ret;
3488 case TVI_CONTROL_TUN_SET_FREQ:
3490 unsigned long nFreq = *(unsigned long *) arg;
3491 if (!priv->pTVTuner)
3492 return TVI_CONTROL_FALSE;
3493 //convert to Hz
3494 nFreq = 1000000 * nFreq / 16; //convert from 1/16 MHz units to Hz
3495 return set_frequency(priv, nFreq);
3497 case TVI_CONTROL_VID_SET_HUE:
3498 return set_control(priv, VideoProcAmp_Hue, *(int *) arg);
3499 case TVI_CONTROL_VID_GET_HUE:
3500 return get_control(priv, VideoProcAmp_Hue, (int *) arg);
3501 case TVI_CONTROL_VID_SET_CONTRAST:
3502 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg);
3503 case TVI_CONTROL_VID_GET_CONTRAST:
3504 return get_control(priv, VideoProcAmp_Contrast, (int *) arg);
3505 case TVI_CONTROL_VID_SET_SATURATION:
3506 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg);
3507 case TVI_CONTROL_VID_GET_SATURATION:
3508 return get_control(priv, VideoProcAmp_Saturation, (int *) arg);
3509 case TVI_CONTROL_VID_SET_BRIGHTNESS:
3510 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg);
3511 case TVI_CONTROL_VID_GET_BRIGHTNESS:
3512 return get_control(priv, VideoProcAmp_Brightness, (int *) arg);
3514 case TVI_CONTROL_VID_GET_FPS:
3516 VIDEOINFOHEADER *Vhdr;
3517 if (!priv->chains[0]->pmt)
3518 return TVI_CONTROL_FALSE;
3519 if (!priv->chains[0]->pmt->pbFormat)
3520 return TVI_CONTROL_FALSE;
3521 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3522 *(float *) arg =
3523 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8);
3524 return TVI_CONTROL_TRUE;
3526 case TVI_CONTROL_IMMEDIATE:
3527 priv->immediate_mode = 1;
3528 return TVI_CONTROL_TRUE;
3529 #ifdef HAVE_TV_TELETEXT
3530 case TVI_CONTROL_VBI_INIT:
3532 void* ptr;
3533 ptr=&(priv->tsp);
3534 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==TVI_CONTROL_TRUE)
3535 priv->priv_vbi=ptr;
3536 else
3537 priv->priv_vbi=NULL;
3538 return TVI_CONTROL_TRUE;
3540 default:
3541 return teletext_control(priv->priv_vbi,cmd,arg);
3542 #endif
3544 return (TVI_CONTROL_UNKNOWN);