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