Merge remote branch 'mplayer/master'
[mplayer/glamo.git] / stream / tvi_dshow.c
blob50830da848850f2fbcef40f2280f8d1fad21a1e0
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 /// CLSID definitions (used for CoCreateInstance call)
378 DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B,
379 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
380 DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B,
381 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37);
382 DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B,
383 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
384 DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3,
385 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
386 DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0,
387 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);
388 DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0,
389 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
390 DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53,
391 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
392 DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53,
393 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
394 #ifdef NOT_USED
395 DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3,
396 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
397 DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a,
398 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2);
399 DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20,
400 0xaf, 0x0b, 0xa7, 0x70);
401 DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3,
402 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5);
403 DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
404 0x20, 0xaf, 0x0b, 0xa7, 0x70);
405 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
406 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
407 #endif
409 /// IID definitions (used for QueryInterface call)
410 DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a,
411 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
412 DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0,
413 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
414 DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa,
415 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
416 DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD,
417 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F);
418 DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0,
419 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85);
420 DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab,
421 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d);
422 DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b,
423 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86);
424 DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a,
425 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
426 DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C,
427 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
428 DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00,
429 0x20, 0xaf, 0x0b, 0xa7, 0x70);
430 DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a,
431 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
432 DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00,
433 0xAA, 0x00, 0xBD, 0x83, 0x39);
434 DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00,
435 0xa0, 0xc9, 0x11, 0x89, 0x56);
436 DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c,
437 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56);
438 DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0,
439 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5);
440 DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00,
441 0xA0, 0xC9, 0x56, 0x02, 0x66);
442 DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1,
443 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56);
444 DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00,
445 0xaa, 0x00, 0x4b, 0xb8, 0x51);
446 DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f,
447 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
448 DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f,
449 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
450 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f,
451 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
452 DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f,
453 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba);
454 DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00,
455 0xa0, 0xc9, 0x11, 0x89, 0x56);
456 DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00,
457 0x00, 0xc0, 0xcc, 0x16, 0xba);
459 #define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1)
460 #define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
462 #define DEVICE_NAME_MAX_LEN 2000
464 /*---------------------------------------------------------------------------------------
465 * Methods, called only from this file
466 *---------------------------------------------------------------------------------------*/
468 void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){
469 ALLOCATOR_PROPERTIES prop;
470 IAMBufferNegotiation* pBN;
471 HRESULT hr;
473 prop.cbAlign = -1;
474 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv;
475 if (!prop.cbBuffer)
476 prop.cbBuffer = 1;
477 prop.cbBuffer += pWF->nBlockAlign - 1;
478 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign;
479 prop.cbPrefix = -1;
480 prop.cBuffers = -1;
482 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN);
483 if(FAILED(hr))
484 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr);
485 else{
486 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
487 if(FAILED(hr))
488 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
489 OLE_RELEASE_SAFE(pBN);
491 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN);
492 if(FAILED(hr))
493 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr);
494 else{
495 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop);
496 if(FAILED(hr))
497 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr);
498 OLE_RELEASE_SAFE(pBN);
502 *---------------------------------------------------------------------------------------
504 * CSampleGrabberCD class. Used for receiving samples from DirectShow.
506 *---------------------------------------------------------------------------------------
508 /// CSampleGrabberCD destructor
509 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This)
511 free(This->lpVtbl);
512 free(This);
515 /// CSampleGrabberCD IUnknown interface methods implementation
516 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB *
517 This,
518 const GUID * riid,
519 void **ppvObject)
521 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
522 GUID *r;
523 unsigned int i = 0;
524 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This);
525 if (!ppvObject)
526 return E_POINTER;
527 for (r = me->interfaces;
528 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++)
529 if (!memcmp(r, riid, sizeof(*r))) {
530 OLE_CALL(This, AddRef);
531 *ppvObject = This;
532 return 0;
534 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid);
535 return E_NOINTERFACE;
538 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This)
540 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
541 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This,
542 me->refcount);
543 return ++(me->refcount);
546 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This)
548 CSampleGrabberCB *me = (CSampleGrabberCB *) This;
549 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n",
550 This, me->refcount - 1);
551 if (--(me->refcount) == 0)
552 CSampleGrabberCB_Destroy(me);
553 return 0;
557 HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This,
558 double SampleTime,
559 BYTE * pBuffer, long lBufferLen)
561 CSampleGrabberCB *this = (CSampleGrabberCB *) This;
562 grabber_ringbuffer_t *rb = this->pbuf;
564 if (!lBufferLen)
565 return E_FAIL;
567 if (!rb->ringbuffer) {
568 rb->buffersize /= lBufferLen;
569 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK)
570 return E_FAIL;
572 mp_msg(MSGT_TV, MSGL_DBG4,
573 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime);
574 EnterCriticalSection(rb->pMutex);
575 if (rb->count >= rb->buffersize) {
576 rb->head = (rb->head + 1) % rb->buffersize;
577 rb->count--;
580 memcpy(rb->ringbuffer[rb->tail], pBuffer,
581 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize);
582 rb->dpts[rb->tail] = SampleTime;
583 rb->tail = (rb->tail + 1) % rb->buffersize;
584 rb->count++;
585 LeaveCriticalSection(rb->pMutex);
587 return S_OK;
590 /// wrapper. directshow does the same when BufferCB callback is requested
591 HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This,
592 double SampleTime,
593 LPMEDIASAMPLE pSample)
595 char* buf;
596 long len;
597 long long tStart,tEnd;
598 HRESULT hr;
599 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf;
601 len=OLE_CALL(pSample,GetSize);
602 tStart=tEnd=0;
603 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd);
604 if(FAILED(hr)){
605 return hr;
607 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart);
608 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf);
609 if(FAILED(hr)){
610 return hr;
612 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len);
613 return hr;
617 /// main grabbing routine
618 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t *
619 pbuf)
621 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB));
622 if (!This)
623 return NULL;
625 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl));
626 if (!This->lpVtbl) {
627 CSampleGrabberCB_Destroy(This);
628 return NULL;
630 This->refcount = 1;
631 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface;
632 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef;
633 This->lpVtbl->Release = CSampleGrabberCB_Release;
634 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB;
635 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB;
637 This->interfaces[0] = IID_IUnknown;
638 This->interfaces[1] = IID_ISampleGrabberCB;
640 This->pbuf = pbuf;
642 return This;
646 *---------------------------------------------------------------------------------------
648 * ROT related methods (register, unregister)
650 *---------------------------------------------------------------------------------------
653 Registering graph in ROT. User will be able to connect to graph from GraphEdit.
655 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister)
657 IMoniker *pMoniker;
658 IRunningObjectTable *pROT;
659 WCHAR wsz[256];
660 HRESULT hr;
662 if (FAILED(GetRunningObjectTable(0, &pROT))) {
663 return E_FAIL;
665 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph,
666 GetCurrentProcessId());
667 hr = CreateItemMoniker(L"!", wsz, &pMoniker);
668 if (SUCCEEDED(hr)) {
669 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE,
670 pUnkGraph, pMoniker, pdwRegister);
671 OLE_RELEASE_SAFE(pMoniker);
673 OLE_RELEASE_SAFE(pROT);
674 return hr;
677 /// Unregistering graph in ROT
678 static void RemoveFromRot(DWORD dwRegister)
680 IRunningObjectTable *pROT;
681 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
682 OLE_CALL_ARGS(pROT, Revoke, dwRegister);
683 OLE_RELEASE_SAFE(pROT);
688 *---------------------------------------------------------------------------------------
690 * ringbuffer related methods (init, destroy)
692 *---------------------------------------------------------------------------------------
695 * \brief ringbuffer destroying routine
697 * \param rb pointer to empty (just allocated) ringbuffer structure
699 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
701 static void destroy_ringbuffer(grabber_ringbuffer_t * rb)
703 int i;
705 if (!rb)
706 return;
708 if (rb->ringbuffer) {
709 for (i = 0; i < rb->buffersize; i++)
710 if (rb->ringbuffer[i])
711 free(rb->ringbuffer[i]);
712 free(rb->ringbuffer);
713 rb->ringbuffer = NULL;
715 if (rb->dpts) {
716 free(rb->dpts);
717 rb->dpts = NULL;
719 if (rb->pMutex) {
720 DeleteCriticalSection(rb->pMutex);
721 free(rb->pMutex);
722 rb->pMutex = NULL;
725 rb->blocksize = 0;
726 rb->buffersize = 0;
727 rb->head = 0;
728 rb->tail = 0;
729 rb->count = 0;
733 * \brief ringbuffer initialization
735 * \param rb pointer to empty (just allocated) ringbuffer structure
736 * \param buffersize size of buffer in blocks
737 * \param blocksize size of buffer's block
739 * \return S_OK if success
740 * \return E_OUTOFMEMORY not enough memory
742 * \note routine does not allocates memory for grabber_rinbuffer_s structure
744 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize,
745 int blocksize)
747 int i;
749 if (!rb)
750 return E_OUTOFMEMORY;
752 rb->buffersize = buffersize < 2 ? 2 : buffersize;
753 rb->blocksize = blocksize;
755 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n",
756 rb->buffersize, rb->blocksize);
758 rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *));
759 if (!rb)
760 return E_POINTER;
761 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *));
763 for (i = 0; i < rb->buffersize; i++) {
764 rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char));
765 if (!rb->ringbuffer[i]) {
766 destroy_ringbuffer(rb);
767 return E_OUTOFMEMORY;
770 rb->dpts = (double*) malloc(rb->buffersize * sizeof(double));
771 if (!rb->dpts) {
772 destroy_ringbuffer(rb);
773 return E_OUTOFMEMORY;
775 rb->head = 0;
776 rb->tail = 0;
777 rb->count = 0;
778 rb->tStart = -1;
779 rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION));
780 if (!rb->pMutex) {
781 destroy_ringbuffer(rb);
782 return E_OUTOFMEMORY;
784 InitializeCriticalSection(rb->pMutex);
785 return S_OK;
789 *---------------------------------------------------------------------------------------
791 * Tuner related methods (frequency, capabilities, etc
793 *---------------------------------------------------------------------------------------
796 * \brief returns string with name for givend PsysCon_* constant
798 * \param lPhysicalType constant from PhysicalConnectorType enumeration
800 * \return pointer to string with apropriate name
802 * \note
803 * Caller should not free returned pointer
805 static char *physcon2str(const long lPhysicalType)
807 int i;
808 for(i=0; tv_physcon_types[i].name; i++)
809 if(tv_physcon_types[i].type==lPhysicalType)
810 return tv_physcon_types[i].name;
811 return "Unknown";
815 * \brief converts MPlayer's chanlist to system country code.
817 * \param chanlist MPlayer's chanlist name
819 * \return system country code
821 * \remarks
822 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified
823 * country (which is usually larger then MPlayer's one, so workaround will work fine).
825 * \todo
826 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes.
828 static int chanlist2country(char *chanlist)
830 int i;
831 for(i=0; tv_chanlist2country[i].chanlist_name; i++)
832 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name))
833 break;
834 return tv_chanlist2country[i].country_code;
838 * \brief loads specified resource from module and return pointer to it
840 * \param hDLL valid module desriptor
841 * \param index index of resource. resource with name "#<index>" will be loaded
843 * \return pointer to loader resource or NULL if error occured
845 static void *GetRC(HMODULE hDLL, int index)
847 char szRCDATA[10];
848 char szName[10];
849 HRSRC hRes;
850 HGLOBAL hTable;
852 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA);
853 snprintf(szName, 10, "#%d", index);
855 hRes = FindResource(hDLL, szName, szRCDATA);
856 if (!hRes) {
857 return NULL;
859 hTable = LoadResource(hDLL, hRes);
860 if (!hTable) {
861 return NULL;
863 return LockResource(hTable);
867 * \brief loads frequency table for given country from kstvtune.ax
869 * \param[in] nCountry - country code
870 * \param[in] nInputType (TunerInputCable or TunerInputAntenna)
871 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies
872 * \param[out] pnLen length of array
873 * \param[out] pnFirst - channel number of first entry in array (nChannelMax)
875 * \return S_OK if success
876 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL
877 * \return E_FAIL error occured during load
879 * \remarks
880 * - array must be freed by caller
881 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL
883 static HRESULT load_freq_table(int nCountry, int nInputType,
884 long **pplFreqTable, int *pnLen,
885 int *pnFirst)
887 HMODULE hDLL;
888 long *plFreqTable;
889 TRCCountryList *pCountryList;
890 int i, index;
892 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%s)\n",nCountry,nInputType == TunerInputAntenna ? "broadcast" : "cable");
893 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */
895 if (!pplFreqTable || !pnFirst || !pnLen)
896 return E_POINTER;
897 if (!nCountry)
898 return E_FAIL;
900 hDLL = LoadLibrary("kstvtune.ax");
901 if (!hDLL) {
902 return E_FAIL;
904 pCountryList = GetRC(hDLL, 9999);
905 if (!pCountryList) {
906 FreeLibrary(hDLL);
907 return E_FAIL;
909 for (i = 0; pCountryList[i].CountryCode != 0; i++)
910 if (pCountryList[i].CountryCode == nCountry)
911 break;
912 if (pCountryList[i].CountryCode == 0) {
913 FreeLibrary(hDLL);
914 return E_FAIL;
916 if (nInputType == TunerInputCable)
917 index = pCountryList[i].CableFreqTable;
918 else
919 index = pCountryList[i].BroadcastFreqTable;
921 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel
922 if (!plFreqTable) {
923 FreeLibrary(hDLL);
924 return E_FAIL;
926 *pnFirst = plFreqTable[0];
927 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1);
928 *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long));
929 if (!*pplFreqTable) {
930 FreeLibrary(hDLL);
931 return E_FAIL;
933 for (i = 0; i < *pnLen; i++) {
934 (*pplFreqTable)[i] = plFreqTable[i + 2];
935 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table #%d => (%ld)\n",i+*pnFirst,(*pplFreqTable)[i]);
937 FreeLibrary(hDLL);
938 return S_OK;
942 * \brief tunes to given frequency through IKsPropertySet call
944 * \param pTVTuner IAMTVTuner interface of capture device
945 * \param lFreq frequency to tune (in Hz)
947 * \return S_OK success
948 * \return apropriate error code otherwise
950 * \note
951 * Due to either bug in driver or error in following code calll to IKsProperty::Set
952 * in this methods always fail with error 0x8007007a.
954 * \todo test code on other machines and an error
956 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq)
958 HRESULT hr;
959 DWORD dwSupported = 0;
960 DWORD cbBytes = 0;
961 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps;
962 KSPROPERTY_TUNER_FREQUENCY_S frequency;
963 IKsPropertySet *pKSProp;
965 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n");
967 memset(&mode_caps, 0, sizeof(mode_caps));
968 memset(&frequency, 0, sizeof(frequency));
970 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
971 if (FAILED(hr))
972 return hr; //no IKsPropertySet interface
974 mode_caps.Mode = AMTUNER_MODE_TV;
975 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER,
976 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported);
977 if (FAILED(hr)) {
978 OLE_RELEASE_SAFE(pKSProp);
979 return hr;
982 if (!dwSupported & KSPROPERTY_SUPPORT_GET) {
983 OLE_RELEASE_SAFE(pKSProp);
984 return E_FAIL; //PROPSETID_TINER not supported
987 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
988 KSPROPERTY_TUNER_MODE_CAPS,
989 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps),
990 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps),
991 &mode_caps, sizeof(mode_caps), &cbBytes);
993 frequency.Frequency = lFreq;
995 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES)
996 frequency.TuningFlags = KS_TUNER_TUNING_FINE;
997 else
998 frequency.TuningFlags = KS_TUNER_TUNING_EXACT;
1000 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) {
1001 OLE_RELEASE_SAFE(pKSProp);
1002 return E_FAIL;
1005 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER,
1006 KSPROPERTY_TUNER_FREQUENCY,
1007 INSTANCEDATA_OF_PROPERTY_PTR(&frequency),
1008 INSTANCEDATA_OF_PROPERTY_SIZE(frequency),
1009 &frequency, sizeof(frequency));
1010 if (FAILED(hr)) {
1011 OLE_RELEASE_SAFE(pKSProp);
1012 return hr;
1015 OLE_RELEASE_SAFE(pKSProp);
1017 return S_OK;
1021 * \brief find channel with nearest frequency and set it
1023 * \param priv driver's private data
1024 * \param lFreq frequency in Hz
1026 * \return S_OK if success
1027 * \return E_FAIL if error occured
1029 static HRESULT set_nearest_freq(priv_t * priv, long lFreq)
1031 HRESULT hr;
1032 int i;
1033 long lFreqDiff=-1;
1034 int nChannel;
1035 TunerInputType tunerInput;
1036 long lInput;
1038 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called: %ld\n", lFreq);
1039 if(priv->freq_table_len == -1 && !priv->freq_table) {
1041 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
1042 if(FAILED(hr)){ //Falling back to 0
1043 lInput=0;
1046 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput);
1048 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME
1049 priv->freq_table_len=0;
1050 priv->freq_table=NULL;
1051 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to load frequency table from kstvtune.ax\n");
1052 return E_FAIL;
1054 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: loaded system (%s) frequency table for country id=%d (channels:%d).\n", tunerInput == TunerInputAntenna ? "broadcast" : "cable",
1055 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len);
1058 if (priv->freq_table_len <= 0)
1059 return E_FAIL;
1061 //FIXME: rewrite search algo
1062 nChannel = -1;
1063 for (i = 0; i < priv->freq_table_len; i++) {
1064 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) {
1065 nChannel = priv->first_channel + i;
1066 lFreqDiff = labs(lFreq - priv->freq_table[i]);
1068 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);
1070 if (nChannel == -1) {
1071 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find nearest channel in system frequency table\n");
1072 return E_FAIL;
1074 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: set_nearest_freq #%d (%ld)\n",nChannel,priv->freq_table[nChannel - priv->first_channel]);
1075 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel,
1076 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT);
1077 if (FAILED(hr)) {
1078 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to switch to nearest channel from system frequency table. Error:0x%x\n", (unsigned int)hr);
1079 return E_FAIL;
1081 return S_OK;
1085 * \brief setting frequency. decides whether use direct call/workaround
1087 * \param priv driver's private data
1088 * \param lFreq frequency in Hz
1090 * \return TVI_CONTROL_TRUE if success
1091 * \return TVI_CONTROL_FALSE if error occured
1093 * \todo check for freq boundary
1095 static int set_frequency(priv_t * priv, long lFreq)
1097 HRESULT hr;
1099 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called: %ld\n", lFreq);
1100 if (!priv->pTVTuner)
1101 return TVI_CONTROL_FALSE;
1102 if (priv->direct_setfreq_call) { //using direct call to set frequency
1103 hr = set_frequency_direct(priv->pTVTuner, lFreq);
1104 if (FAILED(hr)) {
1105 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to set frequency directly. OS built-in channels table will be used.\n");
1106 priv->direct_setfreq_call = 0;
1109 if (!priv->direct_setfreq_call) {
1110 hr = set_nearest_freq(priv, lFreq);
1112 if (FAILED(hr))
1113 return TVI_CONTROL_FALSE;
1114 #ifdef DEPRECATED
1115 priv->pGrabber->ClearBuffer(priv->pGrabber);
1116 #endif
1117 return TVI_CONTROL_TRUE;
1121 * \brief return current frequency from tuner (in Hz)
1123 * \param pTVTuner IAMTVTuner interface of tuner
1124 * \param plFreq address of variable that receives current frequency
1126 * \return S_OK success
1127 * \return E_POINTER pTVTuner==NULL || plFreq==NULL
1128 * \return apropriate error code otherwise
1130 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq)
1132 HRESULT hr;
1133 KSPROPERTY_TUNER_STATUS_S TunerStatus;
1134 DWORD cbBytes;
1135 IKsPropertySet *pKSProp;
1136 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n");
1138 if (!plFreq)
1139 return E_POINTER;
1141 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp);
1142 if (FAILED(hr)) {
1143 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n");
1144 return hr;
1147 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER,
1148 KSPROPERTY_TUNER_STATUS,
1149 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus),
1150 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus),
1151 &TunerStatus, sizeof(TunerStatus), &cbBytes);
1152 if (FAILED(hr)) {
1153 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n");
1154 return hr;
1156 *plFreq = TunerStatus.CurrentFrequency;
1157 return S_OK;
1161 * \brief gets current frequency
1163 * \param priv driver's private data structure
1164 * \param plFreq - pointer to long int to store frequency to (in Hz)
1166 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise
1168 static int get_frequency(priv_t * priv, long *plFreq)
1170 HRESULT hr;
1172 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n");
1174 if (!plFreq || !priv->pTVTuner)
1175 return TVI_CONTROL_FALSE;
1177 if (priv->direct_getfreq_call) { //using direct call to get frequency
1178 hr = get_frequency_direct(priv->pTVTuner, plFreq);
1179 if (FAILED(hr)) {
1180 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Unable to get frequency directly. OS built-in channels table will be used.\n");
1181 priv->direct_getfreq_call = 0;
1184 if (!priv->direct_getfreq_call) {
1185 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq);
1186 if (FAILED(hr))
1187 return TVI_CONTROL_FALSE;
1190 return TVI_CONTROL_TRUE;
1194 * \brief get tuner capabilities
1196 * \param priv driver's private data
1198 static void get_capabilities(priv_t * priv)
1200 long lAvailableFormats;
1201 HRESULT hr;
1202 int i;
1203 long lInputPins, lOutputPins, lRelated, lPhysicalType;
1204 IEnumPins *pEnum;
1205 char tmp[200];
1206 IPin *pPin = 0;
1207 PIN_DIRECTION ThisPinDir;
1208 PIN_INFO pi;
1209 IAMAudioInputMixer *pIAMixer;
1211 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n");
1212 if (priv->pTVTuner) {
1214 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: supported norms:");
1215 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats,
1216 &lAvailableFormats);
1217 if (FAILED(hr))
1218 tv_available_norms_count = 0;
1219 else {
1220 for (i = 0; i < TV_NORMS_COUNT; i++) {
1221 if (lAvailableFormats & tv_norms[i].index) {
1222 tv_available_norms[tv_available_norms_count] = i;
1223 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1224 tv_available_norms_count + 1, tv_norms[i].name);
1225 tv_available_norms_count++;
1229 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1231 if (priv->pCrossbar) {
1232 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins,
1233 &lInputPins);
1235 tv_available_inputs = (long *) malloc(sizeof(long) * lInputPins);
1236 tv_available_inputs_count = 0;
1238 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available video inputs:");
1239 for (i = 0; i < lInputPins; i++) {
1240 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i,
1241 &lRelated, &lPhysicalType);
1243 if (lPhysicalType < 0x1000) {
1244 tv_available_inputs[tv_available_inputs_count++] = i;
1245 mp_msg(MSGT_TV, MSGL_V, " %d=%s;",
1246 tv_available_inputs_count - 1,
1247 physcon2str(lPhysicalType));
1250 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1252 set_crossbar_input(priv, 0);
1255 if (priv->adev_index != -1) {
1256 hr = OLE_CALL_ARGS(priv->chains[1]->pCaptureFilter, EnumPins, &pEnum);
1257 if (FAILED(hr))
1258 return;
1259 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: available audio inputs:");
1260 i = 0;
1261 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1262 memset(&pi, 0, sizeof(pi));
1263 memset(tmp, 0, 200);
1264 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1265 if (ThisPinDir == PINDIR_INPUT) {
1266 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1267 wtoa(pi.achName, tmp, 200);
1268 OLE_RELEASE_SAFE(pi.pFilter);
1269 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp);
1270 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1271 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer);
1272 if (SUCCEEDED(hr)) {
1273 if (i == priv->tv_param->audio_id) {
1274 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE);
1275 if(priv->tv_param->volume>0)
1276 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume);
1277 #if 0
1278 else
1279 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0);
1280 #endif
1281 mp_tmsg(MSGT_TV, MSGL_V, "(selected)");
1282 } else {
1283 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE);
1284 #if 0
1285 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0);
1286 #endif
1288 OLE_RELEASE_SAFE(pIAMixer);
1290 mp_msg(MSGT_TV, MSGL_V, ";");
1291 OLE_RELEASE_SAFE(pPin);
1292 i++;
1295 mp_msg(MSGT_TV, MSGL_INFO, "\n");
1296 OLE_RELEASE_SAFE(pEnum);
1301 *---------------------------------------------------------------------------------------
1303 * Filter related methods
1305 *---------------------------------------------------------------------------------------
1308 * \brief building in graph audio/video capture chain
1310 * \param priv driver's private data
1311 * \param pCaptureFilter pointer to capture device's IBaseFilter interface
1312 * \param pbuf ringbuffer data structure
1313 * \param pmt media type for chain (AM_MEDIA_TYPE)
1315 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure
1317 static HRESULT build_sub_graph(priv_t * priv, chain_t * chain, const GUID* ppin_category)
1319 HRESULT hr;
1320 int nFormatProbed = 0;
1322 IPin *pSGOut;
1323 IPin *pNRIn=NULL;
1325 IBaseFilter *pNR = NULL;
1327 hr=S_OK;
1329 //No supported formats
1330 if(!chain->arpmt[0])
1331 return E_FAIL;
1334 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
1335 (IUnknown *) chain->pCaptureFilter,
1336 PINDIR_OUTPUT, ppin_category,
1337 chain->majortype, FALSE, 0, &chain->pCapturePin);
1338 if(FAILED(hr)){
1339 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
1340 break;
1342 /* Addinf SampleGrabber filter for video stream */
1343 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &chain->pSGF);
1344 if(FAILED(hr)){
1345 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1346 break;
1348 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, chain->pSGF, L"Sample Grabber");
1349 if(FAILED(hr)){
1350 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1351 break;
1353 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &chain->pSGIn);
1354 if(FAILED(hr)){
1355 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr);
1356 break;
1358 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) chain->pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut);
1359 if(FAILED(hr)){
1360 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr);
1361 break;
1364 /* creating ringbuffer for video samples */
1365 chain->pCSGCB = CSampleGrabberCB_Create(chain->rbuf);
1366 if(!chain->pCSGCB){
1367 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY);
1368 break;
1371 /* initializing SampleGrabber filter */
1372 hr = OLE_QUERYINTERFACE(chain->pSGF, IID_ISampleGrabber, chain->pSG);
1373 if(FAILED(hr)){
1374 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr);
1375 break;
1377 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data
1378 hr = OLE_CALL_ARGS(chain->pSG, SetCallback, (ISampleGrabberCB *) chain->pCSGCB, 0); //we want to receive sample
1380 if(FAILED(hr)){
1381 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1382 break;
1384 hr = OLE_CALL_ARGS(chain->pSG, SetOneShot, FALSE); //... for all frames
1385 if(FAILED(hr)){
1386 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1387 break;
1389 hr = OLE_CALL_ARGS(chain->pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber
1390 if(FAILED(hr)){
1391 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1392 break;
1395 if(priv->tv_param->normalize_audio_chunks && chain->type==audio){
1396 set_buffer_preference(20,(WAVEFORMATEX*)(chain->arpmt[nFormatProbed]->pbFormat),chain->pCapturePin,chain->pSGIn);
1399 for(nFormatProbed=0; chain->arpmt[nFormatProbed]; nFormatProbed++)
1401 DisplayMediaType("Probing format", chain->arpmt[nFormatProbed]);
1402 hr = OLE_CALL_ARGS(chain->pSG, SetMediaType, chain->arpmt[nFormatProbed]); //set desired mediatype
1403 if(FAILED(hr)){
1404 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr);
1405 continue;
1407 /* connecting filters together: VideoCapture --> SampleGrabber */
1408 hr = OLE_CALL_ARGS(priv->pGraph, Connect, chain->pCapturePin, chain->pSGIn);
1409 if(FAILED(hr)){
1410 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr);
1411 continue;
1413 break;
1416 if(!chain->arpmt[nFormatProbed])
1418 mp_msg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to negotiate media format\n");
1419 hr = E_FAIL;
1420 break;
1423 hr = OLE_CALL_ARGS(chain->pCapturePin, ConnectionMediaType, chain->pmt);
1424 if(FAILED(hr))
1426 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to get actual mediatype (Error:0x%x). Assuming equal to requested.\n", (unsigned int)hr);
1429 if(priv->tv_param->hidden_video_renderer){
1430 IEnumFilters* pEnum;
1431 IBaseFilter* pFilter;
1433 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)chain->pCapturePin,NULL,NULL);
1435 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
1436 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
1437 LPVIDEOWINDOW pVideoWindow;
1438 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
1439 if (SUCCEEDED(hr))
1441 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
1442 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
1443 OLE_RELEASE_SAFE(pVideoWindow);
1445 OLE_RELEASE_SAFE(pFilter);
1447 OLE_RELEASE_SAFE(pEnum);
1448 }else
1450 #if 0
1452 Code below is disabled, because terminating chain with NullRenderer leads to jerky video.
1453 Perhaps, this happens because NullRenderer filter discards each received
1454 frame while discarded frames causes live source filter to dramatically reduce frame rate.
1456 /* adding sink for video stream */
1457 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR);
1458 if(FAILED(hr)){
1459 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1460 break;
1462 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer");
1463 if(FAILED(hr)){
1464 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr);
1465 break;
1467 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn);
1468 if(FAILED(hr)){
1469 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr);
1470 break;
1473 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection
1475 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){
1476 /* connecting filters together: SampleGrabber --> NullRenderer */
1477 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn);
1478 if(FAILED(hr)){
1479 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr);
1480 break;
1483 #endif
1486 hr = S_OK;
1487 } while(0);
1489 OLE_RELEASE_SAFE(pSGOut);
1490 OLE_RELEASE_SAFE(pNR);
1491 OLE_RELEASE_SAFE(pNRIn);
1493 return hr;
1497 * \brief configures crossbar for grabbing video stream from given input
1499 * \param priv driver's private data
1500 * \param input index of available video input to get data from
1502 * \return TVI_CONTROL_TRUE success
1503 * \return TVI_CONTROL_FALSE error
1505 static int set_crossbar_input(priv_t * priv, int input)
1507 HRESULT hr;
1508 int i, nVideoDecoder, nAudioDecoder;
1509 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins,
1510 lInputPins;
1512 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n");
1513 if (!priv->pCrossbar || input < 0
1514 || input >= tv_available_inputs_count)
1515 return TVI_CONTROL_FALSE;
1517 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins);
1519 lInput = tv_available_inputs[input];
1521 if (lInput < 0 || lInput >= lInputPins)
1522 return TVI_CONTROL_FALSE;
1524 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput,
1525 &lInputRelated, &lPhysicalType);
1527 nVideoDecoder = nAudioDecoder = -1;
1528 for (i = 0; i < lOutputPins; i++) {
1529 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i,
1530 &lRelated, &lPhysicalType);
1531 if (lPhysicalType == PhysConn_Video_VideoDecoder)
1532 nVideoDecoder = i;
1533 if (lPhysicalType == PhysConn_Audio_AudioDecoder)
1534 nAudioDecoder = i;
1536 if (nVideoDecoder >= 0) {
1537 //connecting given input with video decoder
1538 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput);
1539 if (hr != S_OK) {
1540 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to video decoder. Error:0x%x\n", (unsigned int)hr);
1541 return TVI_CONTROL_FALSE;
1544 if (nAudioDecoder >= 0 && lInputRelated >= 0) {
1545 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder,
1546 lInputRelated);
1547 if (hr != S_OK) {
1548 mp_tmsg(MSGT_TV,MSGL_ERR,"Unable to connect given input to audio decoder. Error:0x%x\n", (unsigned int)hr);
1549 return TVI_CONTROL_FALSE;
1552 return TVI_CONTROL_TRUE;
1556 * \brief adjusts video control (hue,saturation,contrast,brightess)
1558 * \param priv driver's private data
1559 * \param control which control to adjust
1560 * \param value new value for control (0-100)
1562 * \return TVI_CONTROL_TRUE success
1563 * \return TVI_CONTROL_FALSE error
1565 static int set_control(priv_t * priv, int control, int value)
1567 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1568 HRESULT hr;
1570 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n");
1571 if (value < -100 || value > 100 || !priv->pVideoProcAmp)
1572 return TVI_CONTROL_FALSE;
1574 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1575 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1576 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual)
1577 return TVI_CONTROL_FALSE;
1579 lValue = lMin + (value + 100) * (lMax - lMin) / 200;
1581 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256
1583 if (lStepping > lMax) {
1584 mp_msg(MSGT_TV, MSGL_DBG3,
1585 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n",
1586 lStepping, lMax,control);
1587 lStepping = 1;
1589 lValue -= lValue % lStepping;
1590 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue,
1591 VideoProcAmp_Flags_Manual);
1592 if (FAILED(hr))
1593 return TVI_CONTROL_FALSE;
1595 return TVI_CONTROL_TRUE;
1599 * \brief get current value of video control (hue,saturation,contrast,brightess)
1601 * \param priv driver's private data
1602 * \param control which control to adjust
1603 * \param pvalue address of variable thar receives current value
1605 * \return TVI_CONTROL_TRUE success
1606 * \return TVI_CONTROL_FALSE error
1608 static int get_control(priv_t * priv, int control, int *pvalue)
1610 long lMin, lMax, lStepping, lDefault, lFlags, lValue;
1611 HRESULT hr;
1613 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n");
1614 if (!pvalue || !priv->pVideoProcAmp)
1615 return TVI_CONTROL_FALSE;
1617 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control,
1618 &lMin, &lMax, &lStepping, &lDefault, &lFlags);
1619 if (FAILED(hr))
1620 return TVI_CONTROL_FALSE;
1621 if (lMin == lMax) {
1622 *pvalue = lMin;
1623 return TVI_CONTROL_TRUE;
1626 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags);
1627 if (FAILED(hr))
1628 return TVI_CONTROL_FALSE;
1630 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100;
1632 return TVI_CONTROL_TRUE;
1636 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps
1637 * \param fcc FourCC code for video format
1638 * \param width picture width
1639 * \param height pciture height
1640 * \param fps frames per second (required for bitrate calculation)
1642 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise
1644 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps)
1646 int i;
1647 AM_MEDIA_TYPE mt;
1648 VIDEOINFOHEADER vHdr;
1650 /* Check given fcc in lookup table*/
1651 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */;
1652 if(!img_fmt_list[i].fmt)
1653 return NULL;
1655 memset(&mt, 0, sizeof(AM_MEDIA_TYPE));
1656 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER));
1658 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader);
1659 vHdr.bmiHeader.biWidth = width;
1660 vHdr.bmiHeader.biHeight = height;
1661 //FIXME: is biPlanes required too?
1662 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes;
1663 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits;
1664 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression;
1665 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8;
1666 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps;
1668 mt.pbFormat = (char*)&vHdr;
1669 mt.cbFormat = sizeof(vHdr);
1671 mt.majortype = MEDIATYPE_Video;
1672 mt.subtype = *img_fmt_list[i].subtype;
1673 mt.formattype = FORMAT_VideoInfo;
1675 mt.bFixedSizeSamples = 1;
1676 mt.bTemporalCompression = 0;
1677 mt.lSampleSize = vHdr.bmiHeader.biSizeImage;
1679 return CreateMediaType(&mt);
1683 * \brief extracts fcc,width,height from AM_MEDIA_TYPE
1685 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1686 * \param pfcc address of variable that receives FourCC
1687 * \param pwidth address of variable that receives width
1688 * \param pheight address of variable that recevies height
1690 * \return 1 if data extracted successfully, 0 - otherwise
1692 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc,
1693 int *pwidth, int *pheight)
1695 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n");
1696 if (!pmt)
1697 return 0;
1698 if (!pmt->pbFormat)
1699 return 0;
1700 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0)
1701 return 0;
1702 if (pfcc)
1703 *pfcc = subtype2imgfmt(&(pmt->subtype));
1704 if (pwidth)
1705 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth;
1706 if (pheight)
1707 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight;
1708 return 1;
1712 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE
1714 * \param pmt pointer to AM_MEDIA_TYPE to extract data from
1715 * \param pfcc address of variable that receives samplerate
1716 * \param pwidth address of variable that receives number of bits per sample
1717 * \param pheight address of variable that recevies number of channels
1719 * \return 1 if data extracted successfully, 0 - otherwise
1721 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate,
1722 int *pbits, int *pchannels)
1724 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n");
1725 if (!pmt)
1726 return 0;
1727 if (!pmt->pbFormat)
1728 return 0;
1729 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1730 return 0;
1731 if (psamplerate)
1732 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec;
1733 if (pbits)
1734 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample;
1735 if (pchannels)
1736 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels;
1737 return 1;
1741 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels
1743 * \param pmt pointer to AM_MEDIA_TYPE for check
1744 * \param samplerate audio samplerate
1745 * \param bits bits per sample
1746 * \param channels number of audio channels
1748 * \return 1 if AM_MEDIA_TYPE compatible
1749 * \return 0 if not
1751 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate,
1752 int bits, int channels)
1754 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n");
1755 if (!pmt)
1756 return 0;
1757 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0)
1758 return 0;
1759 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0)
1760 return 0;
1761 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0)
1762 return 0;
1763 if (!pmt->pbFormat)
1764 return 0;
1765 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate)
1766 return 0;
1767 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits)
1768 return 0;
1769 if (channels > 0
1770 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels)
1771 return 0;
1773 return 1;
1777 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height
1779 * \param pmt pointer to AM_MEDIA_TYPE for check
1780 * \param fcc FourCC (compression)
1781 * \param width width of picture
1782 * \param height height of picture
1784 * \return 1 if AM_MEDIA_TYPE compatible
1785 & \return 0 if not
1787 * \note
1788 * width and height are currently not used
1790 * \todo
1791 * add width/height check
1793 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width,
1794 int height)
1796 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n");
1797 if (!pmt)
1798 return 0;
1799 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0)
1800 return 0;
1801 if (subtype2imgfmt(&(pmt->subtype)) != fcc)
1802 return 0;
1803 return 1;
1807 * \brief converts DirectShow subtype to MPlayer's IMGFMT
1809 * \param subtype DirectShow subtype for video format
1811 * \return MPlayer's IMGFMT or 0 if error occured
1813 static int subtype2imgfmt(const GUID * subtype)
1815 int i;
1816 for (i = 0; img_fmt_list[i].fmt; i++) {
1817 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0)
1818 return img_fmt_list[i].fmt;
1820 return 0;
1824 * \brief prints filter name and it pins
1826 * \param pFilter - IBaseFilter to get data from
1828 * \return S_OK if success, error code otherwise
1830 static HRESULT show_filter_info(IBaseFilter * pFilter)
1832 char tmp[200];
1833 FILTER_INFO fi;
1834 LPENUMPINS pEnum = 0;
1835 IPin *pPin = 0;
1836 PIN_DIRECTION ThisPinDir;
1837 PIN_INFO pi;
1838 HRESULT hr;
1839 int i;
1841 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n");
1842 memset(&fi, 0, sizeof(fi));
1843 memset(tmp, 0, 200);
1845 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi);
1846 OLE_RELEASE_SAFE(fi.pGraph);
1847 wtoa(fi.achName, tmp, 200);
1848 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:",
1849 pFilter, tmp, fi.pGraph);
1850 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum);
1851 if (FAILED(hr))
1852 return hr;
1853 i = 0;
1854 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) {
1855 memset(&pi, 0, sizeof(pi));
1856 memset(tmp, 0, 200);
1857 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir);
1858 if (ThisPinDir == PINDIR_OUTPUT) {
1859 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi);
1860 wtoa(pi.achName, tmp, 200);
1861 OLE_RELEASE_SAFE(pi.pFilter);
1862 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp);
1863 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin);
1864 mp_msg(MSGT_TV, MSGL_DBG2, ";");
1865 OLE_RELEASE_SAFE(pPin);
1866 i++;
1869 mp_msg(MSGT_TV, MSGL_DBG2, "\n");
1870 OLE_RELEASE_SAFE(pEnum);
1871 return S_OK;
1875 * \brief gets device's frendly in ANSI encoding
1877 * \param pM IMoniker interface, got in enumeration process
1878 * \param category device category
1880 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise
1882 static int get_device_name(IMoniker * pM, char *pBuf, int nLen)
1884 HRESULT hr;
1885 VARIANT var;
1886 IPropertyBag *pPropBag;
1887 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag);
1888 if (FAILED(hr)) {
1889 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n");
1890 return TVI_CONTROL_FALSE;
1892 var.vt = VT_BSTR;
1893 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var,
1894 NULL);
1895 if (FAILED(hr)) {
1896 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var,
1897 NULL);
1899 OLE_RELEASE_SAFE(pPropBag);
1900 if (SUCCEEDED(hr)) {
1901 wtoa(var.bstrVal, pBuf, nLen);
1902 return TVI_CONTROL_TRUE;
1904 return TVI_CONTROL_FALSE;
1908 * \brief find capture device at given index
1910 * \param index device index to search for (-1 mean only print available)
1911 * \param category device category
1913 * \return IBaseFilter interface for capture device with given index
1915 * Sample values for category:
1916 * CLSID_VideoInputDeviceCategory - Video Capture Sources
1917 * CLSID_AudioInputDeviceCategory - Audio Capture Sources
1918 * See DirectShow SDK documentation for other possible values
1920 static IBaseFilter *find_capture_device(int index, REFCLSID category)
1922 IBaseFilter *pFilter = NULL;
1923 ICreateDevEnum *pDevEnum = NULL;
1924 IEnumMoniker *pClassEnum = NULL;
1925 IMoniker *pM;
1926 HRESULT hr;
1927 ULONG cFetched;
1928 int i;
1929 char tmp[DEVICE_NAME_MAX_LEN + 1];
1930 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL,
1931 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
1932 (void *) &pDevEnum);
1933 if (FAILED(hr)) {
1934 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n");
1935 return NULL;
1938 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0);
1939 OLE_RELEASE_SAFE(pDevEnum);
1940 if (FAILED(hr)) {
1941 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n");
1942 return NULL;
1944 if (hr == S_FALSE) {
1945 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n");
1946 return NULL;
1949 OLE_CALL(pClassEnum,Reset);
1950 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) {
1951 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE)
1952 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to get name for device #%d\n", i);
1953 else
1954 mp_tmsg(MSGT_TV, MSGL_V, "tvi_dshow: Device #%d: %s\n", i, tmp);
1955 if (index != -1 && i == index) {
1956 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Using device #%d: %s\n", index, tmp);
1957 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter);
1958 if (FAILED(hr))
1959 pFilter = NULL;
1961 OLE_RELEASE_SAFE(pM);
1963 if (index != -1 && !pFilter) {
1964 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Device #%d not found\n",
1965 index);
1967 OLE_RELEASE_SAFE(pClassEnum);
1969 return pFilter;
1973 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps
1975 * \praram[in] chain chain data structure
1977 * \return S_OK success
1978 * \return E_POINTER one of parameters is NULL
1979 * \return E_FAIL required size of buffer is unknown for given media type
1980 * \return E_OUTOFMEMORY not enough memory
1981 * \return other error code from called methods
1983 * \remarks
1984 * last items of chain->arpmt and chain->arStreamCaps will be NULL
1986 static HRESULT get_available_formats_stream(chain_t *chain)
1988 AM_MEDIA_TYPE **arpmt;
1989 void **pBuf=NULL;
1991 HRESULT hr;
1992 int i, count, size;
1993 int done;
1995 mp_msg(MSGT_TV, MSGL_DBG4,
1996 "tvi_dshow: get_available_formats_stream called\n");
1998 if (!chain->pStreamConfig)
1999 return E_POINTER;
2001 hr=OLE_CALL_ARGS(chain->pStreamConfig, GetNumberOfCapabilities, &count, &size);
2002 if (FAILED(hr)) {
2003 mp_msg(MSGT_TV, MSGL_DBG4,
2004 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n");
2005 return hr;
2007 if (chain->type == video){
2008 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
2009 mp_msg(MSGT_TV, MSGL_DBG4,
2010 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2011 return E_FAIL;
2013 } else if (chain->type == audio){
2014 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) {
2015 mp_msg(MSGT_TV, MSGL_DBG4,
2016 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n");
2017 return E_FAIL;
2019 } else {
2020 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_stream");
2021 return E_FAIL;
2023 done = 0;
2025 arpmt = (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2026 if (arpmt) {
2027 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2029 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2030 if (pBuf) {
2031 memset(pBuf, 0, (count + 1) * sizeof(void *));
2033 for (i = 0; i < count; i++) {
2034 pBuf[i] = malloc(size);
2036 if (!pBuf[i])
2037 break;
2039 hr = OLE_CALL_ARGS(chain->pStreamConfig, GetStreamCaps, i,
2040 &(arpmt[i]), pBuf[i]);
2041 if (FAILED(hr))
2042 break;
2044 if (i == count) {
2045 chain->arpmt = arpmt;
2046 chain->arStreamCaps = pBuf;
2047 done = 1;
2051 if (!done) {
2052 for (i = 0; i < count; i++) {
2053 if (pBuf && pBuf[i])
2054 free(pBuf[i]);
2055 if (arpmt && arpmt[i])
2056 DeleteMediaType(arpmt[i]);
2058 if (pBuf)
2059 free(pBuf);
2060 if (arpmt)
2061 free(arpmt);
2062 if (hr != S_OK) {
2063 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n");
2064 return hr;
2065 } else
2066 return E_OUTOFMEMORY;
2068 return S_OK;
2072 * \brief returns allocates an array and store available media formats for given pin type to it
2074 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder
2075 * \param chain chain data structure
2077 * \return S_OK success
2078 * \return E_POINTER one of given pointers is null
2079 * \return apropriate error code otherwise
2081 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder,
2082 chain_t *chain)
2084 IEnumMediaTypes *pEnum;
2085 int i, count, size;
2086 ULONG cFetched;
2087 AM_MEDIA_TYPE *pmt;
2088 HRESULT hr;
2089 void **pBuf;
2090 AM_MEDIA_TYPE **arpmt; //This will be real array
2091 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps;
2092 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps;
2093 int p1, p2, p3;
2095 mp_msg(MSGT_TV, MSGL_DBG4,
2096 "tvi_dshow: get_available_formats_pin called\n");
2097 if (!pBuilder || !chain->pCaptureFilter)
2098 return E_POINTER;
2100 if (!chain->pCapturePin)
2102 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2103 (IUnknown *) chain->pCaptureFilter,
2104 PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE,
2105 chain->majortype, FALSE, 0, &chain->pCapturePin);
2107 if (!chain->pCapturePin)
2108 return E_POINTER;
2110 if (chain->type == video) {
2111 size = sizeof(VIDEO_STREAM_CONFIG_CAPS);
2112 } else if (chain->type == audio) {
2113 size = sizeof(AUDIO_STREAM_CONFIG_CAPS);
2114 } else {
2115 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unsupported media type passed to %s\n","get_available_formats_pin");
2116 return E_FAIL;
2119 hr = OLE_CALL_ARGS(chain->pCapturePin, EnumMediaTypes, &pEnum);
2120 if (FAILED(hr)) {
2121 mp_msg(MSGT_TV, MSGL_DBG4,
2122 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n");
2123 return hr;
2125 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) {
2126 if (!pmt)
2127 break;
2129 OLE_CALL(pEnum,Reset);
2131 count = i;
2132 arpmt =
2133 (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *));
2134 if (!arpmt)
2135 return E_OUTOFMEMORY;
2136 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *));
2138 for (i = 0;
2139 i < count
2140 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK;
2141 i++);
2143 OLE_RELEASE_SAFE(pEnum);
2146 pBuf = (void **) malloc((count + 1) * sizeof(void *));
2147 if (!pBuf) {
2148 for (i = 0; i < count; i++)
2149 if (arpmt[i])
2150 DeleteMediaType(arpmt[i]);
2151 free(arpmt);
2152 return E_OUTOFMEMORY;
2154 memset(pBuf, 0, (count + 1) * sizeof(void *));
2156 for (i = 0; i < count; i++) {
2157 pBuf[i] = malloc(size);
2158 if (!pBuf[i])
2159 break;
2160 memset(pBuf[i], 0, size);
2162 if (chain->type == video) {
2163 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i];
2164 extract_video_format(arpmt[i], NULL, &p1, &p2);
2165 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx =
2167 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy =
2169 } else {
2170 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i];
2171 extract_audio_format(arpmt[i], &p1, &p2, &p3);
2172 pAudioCaps->MaximumSampleFrequency =
2173 pAudioCaps->MinimumSampleFrequency = p1;
2174 pAudioCaps->MaximumBitsPerSample =
2175 pAudioCaps->MinimumBitsPerSample = p2;
2176 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3;
2180 if (i != count) {
2181 for (i = 0; i < count; i++) {
2182 if (arpmt[i])
2183 DeleteMediaType(arpmt[i]);
2184 if (pBuf[i])
2185 free(pBuf[i]);
2187 free(arpmt);
2188 free(pBuf);
2189 return E_OUTOFMEMORY;
2191 chain->arpmt = arpmt;
2192 chain->arStreamCaps = pBuf;
2194 return S_OK;
2198 *---------------------------------------------------------------------------------------
2200 * Public methods
2202 *---------------------------------------------------------------------------------------
2205 * \brief fills given buffer with audio data (usually one block)
2207 * \param priv driver's private data structure
2208 * \param buffer buffer to store data to
2209 * \param len buffer's size in bytes (usually one block size)
2211 * \return audio pts if audio present, 1 - otherwise
2213 static double grab_audio_frame(priv_t * priv, char *buffer, int len)
2215 int bytes = 0;
2216 int i;
2217 double pts;
2218 grabber_ringbuffer_t *rb = priv->chains[1]->rbuf;
2219 grabber_ringbuffer_t *vrb = priv->chains[0]->rbuf;
2221 if (!rb || !rb->ringbuffer)
2222 return 1;
2224 if(vrb && vrb->tStart<0){
2225 memset(buffer,0,len);
2226 return 0;
2228 if(vrb && rb->tStart<0)
2229 rb->tStart=vrb->tStart;
2231 if (len < rb->blocksize)
2232 bytes = len;
2233 else
2234 bytes = rb->blocksize;
2236 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n",
2237 rb->count, len);
2238 if(!rb->count){
2239 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2240 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2241 if(!rb->count){
2242 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2243 return 0;
2245 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2248 EnterCriticalSection(rb->pMutex);
2249 pts=rb->dpts[rb->head]-rb->tStart;
2250 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2251 rb->head = (rb->head + 1) % rb->buffersize;
2252 rb->count--;
2253 LeaveCriticalSection(rb->pMutex);
2254 return pts;
2258 * \brief returns audio frame size
2260 * \param priv driver's private data structure
2262 * \return audio block size if audio enabled and 1 - otherwise
2264 static int get_audio_framesize(priv_t * priv)
2266 if (!priv->chains[1]->rbuf)
2267 return 1; //no audio
2268 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->chains[1]->rbuf->blocksize);
2269 return priv->chains[1]->rbuf->blocksize;
2272 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp)
2274 if(!priv || !ptsp)
2275 return TVI_CONTROL_FALSE;
2277 //STUBS!!!
2278 ptsp->interlaced=0;
2279 ptsp->offset=256;
2281 ptsp->sampling_rate=27e6;
2282 ptsp->samples_per_line=720;
2284 ptsp->count[0]=16;
2285 ptsp->count[1]=16;
2286 //END STUBS!!!
2287 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]);
2289 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",
2290 ptsp->sampling_rate,
2291 ptsp->offset,
2292 ptsp->samples_per_line,
2293 ptsp->interlaced?"Yes":"No",
2294 ptsp->count[0],
2295 ptsp->count[1]);
2297 return TVI_CONTROL_TRUE;
2300 static void vbi_grabber(priv_t* priv)
2302 grabber_ringbuffer_t *rb = priv->chains[2]->rbuf;
2303 int i;
2304 unsigned char* buf;
2305 if (!rb || !rb->ringbuffer)
2306 return;
2308 buf=calloc(1,rb->blocksize);
2309 for(i=0; i<23 && rb->count; i++){
2310 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize);
2311 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf);
2312 rb->head = (rb->head + 1) % rb->buffersize;
2313 rb->count--;
2315 free(buf);
2319 * \brief fills given buffer with video data (usually one frame)
2321 * \param priv driver's private data structure
2322 * \param buffer buffer to store data to
2323 * \param len buffer's size in bytes (usually one frame size)
2325 * \return frame size if video present, 0 - otherwise
2327 static double grab_video_frame(priv_t * priv, char *buffer, int len)
2329 int bytes = 0;
2330 int i;
2331 double pts;
2332 grabber_ringbuffer_t *rb = priv->chains[0]->rbuf;
2334 if (!rb || !rb->ringbuffer)
2335 return 1;
2336 if (len < rb->blocksize)
2337 bytes = len;
2338 else
2339 bytes = rb->blocksize;
2341 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n",
2342 rb->count, len);
2343 if(!rb->count){
2344 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n");
2345 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000);
2346 if(!rb->count){
2347 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n");
2348 return 0;
2350 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n");
2352 EnterCriticalSection(rb->pMutex);
2353 if(rb->tStart<0)
2354 rb->tStart=rb->dpts[rb->head];
2355 pts=rb->dpts[rb->head]-rb->tStart;
2356 memcpy(buffer, rb->ringbuffer[rb->head], bytes);
2357 rb->head = (rb->head + 1) % rb->buffersize;
2358 rb->count--;
2359 LeaveCriticalSection(rb->pMutex);
2361 vbi_grabber(priv);
2362 return pts;
2366 * \brief returns frame size
2368 * \param priv driver's private data structure
2370 * \return frame size if video present, 0 - otherwise
2372 static int get_video_framesize(priv_t * priv)
2374 // if(!priv->pmtVideo) return 1; //no video
2375 // return priv->pmtVideo->lSampleSize;
2376 if (!priv->chains[0]->rbuf)
2377 return 1; //no video
2378 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->chains[0]->rbuf->blocksize);
2379 return priv->chains[0]->rbuf->blocksize;
2383 * \brief calculate audio buffer size
2384 * \param video_buf_size size of video buffer in bytes
2385 * \param video_bitrate video bit rate
2386 * \param audio_bitrate audio bit rate
2387 * \return audio buffer isze in bytes
2389 * \remarks length of video buffer and resulted audio buffer calculated in
2390 * seconds will be the same.
2392 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate)
2394 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate);
2395 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n",
2396 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size);
2397 return audio_buf_size;
2401 * \brief common chain initialization routine
2402 * \param chain chain data structure
2404 * \note pCaptureFilter member should be initialized before call to this routine
2406 static HRESULT init_chain_common(ICaptureGraphBuilder2 *pBuilder, chain_t *chain)
2408 HRESULT hr;
2409 int i;
2411 if(!chain->pCaptureFilter)
2412 return E_POINTER;
2414 show_filter_info(chain->pCaptureFilter);
2416 hr = OLE_CALL_ARGS(pBuilder, FindPin,
2417 (IUnknown *) chain->pCaptureFilter,
2418 PINDIR_OUTPUT, chain->pin_category,
2419 chain->majortype, FALSE, 0, &chain->pCapturePin);
2421 if (FAILED(hr)) {
2422 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr);
2423 return hr;
2426 hr = OLE_CALL_ARGS(pBuilder, FindInterface,
2427 chain->pin_category,
2428 chain->majortype,
2429 chain->pCaptureFilter,
2430 &IID_IAMStreamConfig,
2431 (void **) &(chain->pStreamConfig));
2432 if (FAILED(hr))
2433 chain->pStreamConfig = NULL;
2436 Getting available video formats (last pointer in array will be NULL)
2437 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as
2438 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default
2439 min/max values.
2441 hr = get_available_formats_stream(chain);
2442 if (FAILED(hr)) {
2443 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr);
2444 hr = get_available_formats_pin(pBuilder, chain);
2445 if(FAILED(hr)){
2446 return hr;
2449 chain->nFormatUsed = 0;
2451 //If argument to CreateMediaType is NULL then result will be NULL too.
2452 chain->pmt = CreateMediaType(chain->arpmt[0]);
2454 for (i = 0; chain->arpmt[i]; i++)
2455 DisplayMediaType("Available format", chain->arpmt[i]);
2457 return S_OK;
2460 * \brief build video stream chain in graph
2461 * \param priv private data structure
2463 * \return S_OK if chain was built successfully, apropriate error code otherwise
2465 static HRESULT build_video_chain(priv_t *priv)
2467 HRESULT hr;
2469 if(priv->chains[0]->rbuf)
2470 return S_OK;
2472 if (priv->chains[0]->pStreamConfig) {
2473 hr = OLE_CALL_ARGS(priv->chains[0]->pStreamConfig, SetFormat, priv->chains[0]->pmt);
2474 if (FAILED(hr)) {
2475 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select video format. Error:0x%x\n", (unsigned int)hr);
2479 priv->chains[0]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2480 if(!priv->chains[0]->rbuf)
2481 return E_OUTOFMEMORY;
2483 if (priv->tv_param->buffer_size >= 0) {
2484 priv->chains[0]->rbuf->buffersize = priv->tv_param->buffer_size;
2485 } else {
2486 priv->chains[0]->rbuf->buffersize = 16;
2489 priv->chains[0]->rbuf->buffersize *= 1024 * 1024;
2490 hr=build_sub_graph(priv, priv->chains[0], &PIN_CATEGORY_CAPTURE);
2491 if(FAILED(hr)){
2492 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build video chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2493 return hr;
2495 return S_OK;
2499 * \brief build audio stream chain in graph
2500 * \param priv private data structure
2502 * \return S_OK if chain was built successfully, apropriate error code otherwise
2504 static HRESULT build_audio_chain(priv_t *priv)
2506 HRESULT hr;
2508 if(priv->chains[1]->rbuf)
2509 return S_OK;
2511 if(priv->immediate_mode)
2512 return S_OK;
2514 if (priv->chains[1]->pStreamConfig) {
2515 hr = OLE_CALL_ARGS(priv->chains[1]->pStreamConfig, SetFormat,
2516 priv->chains[1]->pmt);
2517 if (FAILED(hr)) {
2518 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to select audio format. Error:0x%x\n", (unsigned int)hr);
2522 if(priv->chains[1]->pmt){
2523 priv->chains[1]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2524 if(!priv->chains[1]->rbuf)
2525 return E_OUTOFMEMORY;
2527 /* let the audio buffer be the same size (in seconds) than video one */
2528 priv->chains[1]->rbuf->buffersize=audio_buf_size_from_video(
2529 priv->chains[0]->rbuf->buffersize,
2530 (((VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat)->dwBitRate),
2531 (((WAVEFORMATEX *) (priv->chains[1]->pmt->pbFormat))->nAvgBytesPerSec));
2533 hr=build_sub_graph(priv, priv->chains[1],&PIN_CATEGORY_CAPTURE);
2534 if(FAILED(hr)){
2535 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build audio chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2536 return 0;
2539 return S_OK;
2543 * \brief build VBI stream chain in graph
2544 * \param priv private data structure
2546 * \return S_OK if chain was built successfully, apropriate error code otherwise
2548 static HRESULT build_vbi_chain(priv_t *priv)
2550 HRESULT hr;
2552 if(priv->chains[2]->rbuf)
2553 return S_OK;
2555 if(priv->tv_param->teletext.device)
2557 priv->chains[2]->rbuf=calloc(1,sizeof(grabber_ringbuffer_t));
2558 if(!priv->chains[2]->rbuf)
2559 return E_OUTOFMEMORY;
2561 init_ringbuffer(priv->chains[2]->rbuf,24,priv->tsp.bufsize);
2563 hr=build_sub_graph(priv, priv->chains[2],&PIN_CATEGORY_VBI);
2564 if(FAILED(hr)){
2565 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to build VBI chain of capture graph. Error:0x%x\n",(unsigned int)hr);
2566 return 0;
2569 return S_OK;
2573 * \brief playback/capture real start
2575 * \param priv driver's private data structure
2577 * \return 1 if success, 0 - otherwise
2579 * TODO: move some code from init() here
2581 static int start(priv_t * priv)
2583 HRESULT hr;
2585 hr = build_video_chain(priv);
2586 if(FAILED(hr))
2587 return 0;
2589 hr = build_audio_chain(priv);
2590 if(FAILED(hr))
2591 return 0;
2593 hr = build_vbi_chain(priv);
2594 if(FAILED(hr))
2595 return 0;
2598 Graph is ready to capture. Starting graph.
2600 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2601 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n");
2602 usec_sleep(10000000);
2603 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n");
2605 if (!priv->pMediaControl) {
2606 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)E_POINTER);
2607 return 0;
2609 hr = OLE_CALL(priv->pMediaControl, Run);
2610 if (FAILED(hr)) {
2611 mp_tmsg(MSGT_TV,MSGL_ERR,"tvi_dshow: Unable to start graph! Error:0x%x\n", (unsigned int)hr);
2612 return 0;
2614 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n");
2615 priv->state = 1;
2617 return 1;
2621 * \brief driver initialization
2623 * \param priv driver's private data structure
2625 * \return 1 if success, 0 - otherwise
2627 static int init(priv_t * priv)
2629 HRESULT hr;
2630 int result = 0;
2631 long lInput, lTunerInput;
2632 IEnumFilters *pEnum;
2633 IBaseFilter *pFilter;
2634 IPin *pVPOutPin;
2635 int i;
2637 priv->state=0;
2639 CoInitialize(NULL);
2641 for(i=0; i<3;i++)
2642 priv->chains[i] = calloc(1, sizeof(chain_t));
2644 priv->chains[0]->type=video;
2645 priv->chains[0]->majortype=&MEDIATYPE_Video;
2646 priv->chains[0]->pin_category=&PIN_CATEGORY_CAPTURE;
2647 priv->chains[1]->type=audio;
2648 priv->chains[1]->majortype=&MEDIATYPE_Audio;
2649 priv->chains[1]->pin_category=&PIN_CATEGORY_CAPTURE;
2650 priv->chains[2]->type=vbi;
2651 priv->chains[2]->majortype=&MEDIATYPE_VBI;
2652 priv->chains[2]->pin_category=&PIN_CATEGORY_VBI;
2655 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL,
2656 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder,
2657 (void **) &priv->pGraph);
2658 if(FAILED(hr)){
2659 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr);
2660 break;
2662 //Debug
2663 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) {
2664 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister));
2667 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL,
2668 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2,
2669 (void **) &priv->pBuilder);
2670 if(FAILED(hr)){
2671 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr);
2672 break;
2675 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph);
2676 if(FAILED(hr)){
2677 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr);
2678 break;
2681 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n");
2682 priv->chains[0]->pCaptureFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory);
2683 if(!priv->chains[0]->pCaptureFilter){
2684 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find video capture device\n");
2685 break;
2687 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[0]->pCaptureFilter, NULL);
2688 if(FAILED(hr)){
2689 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2690 break;
2692 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n");
2693 if (priv->adev_index != -1) {
2694 priv->chains[1]->pCaptureFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices
2695 if(!priv->chains[1]->pCaptureFilter){
2696 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to find audio capture device\n");
2697 break;
2700 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->chains[1]->pCaptureFilter, NULL);
2701 if(FAILED(hr)){
2702 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr);
2703 break;
2705 } else
2706 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[1]->pCaptureFilter);
2708 /* increase refrence counter for capture filter ad store pointer into vbi chain structure too */
2709 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IBaseFilter, priv->chains[2]->pCaptureFilter);
2711 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp);
2712 if (FAILED(hr) && hr != E_NOINTERFACE)
2713 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr);
2715 if (hr != S_OK) {
2716 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Adjusting of brightness/hue/saturation/contrast is not supported by device\n");
2717 priv->pVideoProcAmp = NULL;
2720 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2721 &PIN_CATEGORY_CAPTURE,
2722 priv->chains[0]->majortype,
2723 priv->chains[0]->pCaptureFilter,
2724 &IID_IAMCrossbar, (void **) &(priv->pCrossbar));
2725 if (FAILED(hr)) {
2726 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Selection of capture source is not supported by device\n");
2727 priv->pCrossbar = NULL;
2730 if (priv->tv_param->amode >= 0) {
2731 IAMTVAudio *pTVAudio;
2732 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->chains[0]->pCaptureFilter,&IID_IAMTVAudio, (void *) &pTVAudio);
2733 if (hr == S_OK) {
2734 switch (priv->tv_param->amode) {
2735 case 0:
2736 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO);
2737 break;
2738 case 1:
2739 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO);
2740 break;
2741 case 2:
2742 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2743 AMTVAUDIO_MODE_LANG_A);
2744 break;
2745 case 3:
2746 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode,
2747 AMTVAUDIO_MODE_LANG_B);
2748 break;
2750 OLE_RELEASE_SAFE(pTVAudio);
2751 if (FAILED(hr))
2752 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Unable to set audio mode %d. Error:0x%x\n", priv->tv_param->amode,(unsigned int)hr);
2756 // Video chain initialization
2757 hr = init_chain_common(priv->pBuilder, priv->chains[0]);
2758 if(FAILED(hr))
2759 break;
2762 Audio chain initialization
2763 Since absent audio stream is not fatal,
2764 at least one NULL pointer should be kept in format arrays
2765 (to avoid another additional check everywhere for array presence).
2767 hr = init_chain_common(priv->pBuilder, priv->chains[1]);
2768 if(FAILED(hr))
2770 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize audio chain (Error:0x%x). Audio disabled\n", (unsigned long)hr);
2771 priv->chains[1]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2772 priv->chains[1]->arStreamCaps=calloc(1, sizeof(void*));
2776 VBI chain initialization
2777 Since absent VBI stream is not fatal,
2778 at least one NULL pointer should be kept in format arrays
2779 (to avoid another additional check everywhere for array presence).
2781 hr = init_chain_common(priv->pBuilder, priv->chains[2]);
2782 if(FAILED(hr))
2784 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to initialize VBI chain (Error:0x%x). Teletext disabled\n", (unsigned long)hr);
2785 priv->chains[2]->arpmt=calloc(1, sizeof(AM_MEDIA_TYPE*));
2786 priv->chains[2]->arStreamCaps=calloc(1, sizeof(void*));
2789 if (!priv->chains[0]->pStreamConfig)
2790 mp_tmsg(MSGT_TV, MSGL_INFO, "tvi_dshow: Changing video width/height is not supported by device.\n");
2792 if (!priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]
2793 || !extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
2794 &(priv->fcc), &(priv->width),
2795 &(priv->height))) {
2796 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse video format structure.\n");
2797 break;
2800 if (priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]) {
2801 if (!extract_audio_format(priv->chains[1]->pmt, &(priv->samplerate), NULL, NULL)) {
2802 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Unable to parse audio format structure.\n");
2803 DisplayMediaType("audio format failed",priv->chains[1]->arpmt[priv->chains[1]->nFormatUsed]);
2804 break;
2808 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl);
2809 if(FAILED(hr)){
2810 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n",(unsigned int)hr);
2811 break;
2813 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface,
2814 &PIN_CATEGORY_CAPTURE, NULL,
2815 priv->chains[0]->pCaptureFilter,
2816 &IID_IAMTVTuner, (void **) &(priv->pTVTuner));
2818 if (!priv->pTVTuner) {
2819 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr);
2822 // shows Tuner capabilities
2823 get_capabilities(priv);
2825 if (priv->pTVTuner) {
2826 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode,
2827 chanlist2country(priv->tv_param->chanlist));
2828 if(FAILED(hr)){
2829 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr);
2832 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV);
2833 if(FAILED(hr)){
2834 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr);
2835 break;
2838 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput);
2839 if(FAILED(hr)){
2840 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr);
2841 break;
2844 /* small hack */
2845 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna;
2847 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput);
2848 if(FAILED(hr)){
2849 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr);
2850 break;
2856 for VIVO cards we should check if preview pin is available on video capture device.
2857 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter.
2858 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph
2860 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin,
2861 (IUnknown *) priv->chains[0]->pCaptureFilter,
2862 PINDIR_OUTPUT,
2863 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE,
2864 0, (IPin **) & pVPOutPin);
2865 if (SUCCEEDED(hr)) {
2866 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin);
2867 OLE_RELEASE_SAFE(pVPOutPin);
2869 if (FAILED(hr)) {
2870 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Unable to terminate VideoPort pin with any filter in graph. Error:0x%x\n", (unsigned int)hr);
2871 break;
2875 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum);
2876 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) {
2877 LPVIDEOWINDOW pVideoWindow;
2878 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow);
2879 if (SUCCEEDED(hr))
2881 if(priv->tv_param->hidden_vp_renderer){
2882 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0);
2883 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0);
2884 }else
2886 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter);
2888 OLE_RELEASE_SAFE(pVideoWindow);
2890 OLE_RELEASE_SAFE(pFilter);
2892 OLE_RELEASE_SAFE(pEnum);
2893 if(priv->tv_param->system_clock)
2895 LPREFERENCECLOCK rc;
2896 IBaseFilter* pBF;
2897 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL,
2898 CLSCTX_INPROC_SERVER, &IID_IReferenceClock,
2899 (void *) &rc);
2901 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF);
2902 OLE_CALL_ARGS(pBF,SetSyncSource,rc);
2904 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE)
2905 break;
2906 result = 1;
2907 } while(0);
2909 if (!result){
2910 mp_tmsg(MSGT_TV,MSGL_ERR, "tvi_dshow: Directshow graph initialization failure.\n");
2911 uninit(priv);
2913 return result;
2917 * \brief chain uninitialization
2918 * \param chain chain data structure
2920 static void destroy_chain(chain_t *chain)
2922 int i;
2924 if(!chain)
2925 return;
2927 OLE_RELEASE_SAFE(chain->pStreamConfig);
2928 OLE_RELEASE_SAFE(chain->pCaptureFilter);
2929 OLE_RELEASE_SAFE(chain->pCSGCB);
2930 OLE_RELEASE_SAFE(chain->pCapturePin);
2931 OLE_RELEASE_SAFE(chain->pSGIn);
2932 OLE_RELEASE_SAFE(chain->pSG);
2933 OLE_RELEASE_SAFE(chain->pSGF);
2935 if (chain->pmt)
2936 DeleteMediaType(chain->pmt);
2938 if (chain->arpmt) {
2939 for (i = 0; chain->arpmt[i]; i++) {
2940 DeleteMediaType(chain->arpmt[i]);
2942 free(chain->arpmt);
2945 if (chain->arStreamCaps) {
2946 for (i = 0; chain->arStreamCaps[i]; i++) {
2947 free(chain->arStreamCaps[i]);
2949 free(chain->arStreamCaps);
2952 if (chain->rbuf) {
2953 destroy_ringbuffer(chain->rbuf);
2954 free(chain->rbuf);
2955 chain->rbuf = NULL;
2957 free(chain);
2960 * \brief driver uninitialization
2962 * \param priv driver's private data structure
2964 * \return always 1
2966 static int uninit(priv_t * priv)
2968 int i;
2969 if (!priv)
2970 return 1;
2971 //Debug
2972 if (priv->dwRegister) {
2973 RemoveFromRot(priv->dwRegister);
2975 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1);
2976 //stop audio grabber thread
2978 if (priv->state && priv->pMediaControl) {
2979 OLE_CALL(priv->pMediaControl, Stop);
2981 OLE_RELEASE_SAFE(priv->pMediaControl);
2982 priv->state = 0;
2984 if (priv->pGraph) {
2985 if (priv->chains[0]->pCaptureFilter)
2986 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[0]->pCaptureFilter);
2987 if (priv->chains[1]->pCaptureFilter)
2988 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->chains[1]->pCaptureFilter);
2990 OLE_RELEASE_SAFE(priv->pCrossbar);
2991 OLE_RELEASE_SAFE(priv->pVideoProcAmp);
2992 OLE_RELEASE_SAFE(priv->pGraph);
2993 OLE_RELEASE_SAFE(priv->pBuilder);
2994 if(priv->freq_table){
2995 priv->freq_table_len=-1;
2996 free(priv->freq_table);
2997 priv->freq_table=NULL;
3000 for(i=0; i<3;i++)
3002 destroy_chain(priv->chains[i]);
3003 priv->chains[i] = NULL;
3005 CoUninitialize();
3006 return 1;
3010 * \brief driver pre-initialization
3012 * \param device string, containing device name in form "x[.y]", where x is video capture device
3013 * (default: 0, first available); y (if given) sets audio capture device
3015 * \return 1 if success,0 - otherwise
3017 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param)
3019 tvi_handle_t *h;
3020 priv_t *priv;
3021 int a;
3023 h = new_handle();
3024 if (!h)
3025 return NULL;
3027 priv = h->priv;
3029 memset(priv, 0, sizeof(priv_t));
3030 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3031 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled
3032 priv->adev_index = -1;
3033 priv->freq_table_len=-1;
3034 priv->tv_param=tv_param;
3036 if (tv_param->device) {
3037 if (sscanf(tv_param->device, "%d", &a) == 1) {
3038 priv->dev_index = a;
3039 } else {
3040 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device parameter: %s\n", tv_param->device);
3041 free_handle(h);
3042 return NULL;
3044 if (priv->dev_index < 0) {
3045 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong device index: %d\n", a);
3046 free_handle(h);
3047 return NULL;
3050 if (tv_param->adevice) {
3051 if (sscanf(tv_param->adevice, "%d", &a) == 1) {
3052 priv->adev_index = a;
3053 } else {
3054 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice parameter: %s\n", tv_param->adevice);
3055 free_handle(h);
3056 return NULL;
3058 if (priv->dev_index < 0) {
3059 mp_tmsg(MSGT_TV, MSGL_ERR, "tvi_dshow: Wrong adevice index: %d\n", a);
3060 free_handle(h);
3061 return NULL;
3064 return h;
3068 * \brief driver's ioctl handler
3070 * \param priv driver's private data structure
3071 * \param cmd ioctl command
3072 * \param arg ioct command's parameter
3074 * \return TVI_CONTROL_TRUE if success
3075 * \return TVI_CONTROL_FALSE if failure
3076 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called
3078 static int control(priv_t * priv, int cmd, void *arg)
3080 switch (cmd) {
3081 /* need rewrite */
3082 case TVI_CONTROL_VID_SET_FORMAT:
3084 int fcc, i,j;
3085 void* tmp,*tmp2;
3086 int result = TVI_CONTROL_TRUE;
3088 if (priv->state)
3089 return TVI_CONTROL_FALSE;
3090 fcc = *(int *) arg;
3092 if(!priv->chains[0]->arpmt)
3093 return TVI_CONTROL_FALSE;
3094 for (i = 0; priv->chains[0]->arpmt[i]; i++)
3095 if (check_video_format
3096 (priv->chains[0]->arpmt[i], fcc, priv->width, priv->height))
3097 break;
3098 if (!priv->chains[0]->arpmt[i])
3100 int fps = 0;
3101 VIDEOINFOHEADER* Vhdr = NULL;
3102 AM_MEDIA_TYPE *pmt;
3104 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));
3106 if (priv->chains[0]->arpmt[0])
3107 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->arpmt[0]->pbFormat;
3109 if(Vhdr && Vhdr->bmiHeader.biSizeImage)
3110 fps = Vhdr->dwBitRate / (8 * Vhdr->bmiHeader.biSizeImage);
3112 pmt=create_video_format(fcc, priv->width, priv->height, fps);
3113 if(!pmt)
3115 mp_msg(MSGT_TV, MSGL_V, "tvi_dshow: Unable to create AM_MEDIA_TYPE structure for given format\n");
3116 return TVI_CONTROL_FALSE;
3118 priv->chains[0]->arpmt=realloc(priv->chains[0]->arpmt, (i+2)*sizeof(AM_MEDIA_TYPE*));
3119 priv->chains[0]->arpmt[i+1] = NULL;
3120 priv->chains[0]->arpmt[i] = pmt;
3122 priv->chains[0]->arStreamCaps=realloc(priv->chains[0]->arStreamCaps, (i+2)*sizeof(void*));
3123 priv->chains[0]->arpmt[i+1] = NULL;
3125 result = TVI_CONTROL_FALSE;
3129 tmp=priv->chains[0]->arpmt[i];
3130 tmp2=priv->chains[0]->arStreamCaps[i];
3131 for(j=i; j>0; j--)
3133 priv->chains[0]->arpmt[j] = priv->chains[0]->arpmt[j-1];
3134 priv->chains[0]->arStreamCaps[j] = priv->chains[0]->arStreamCaps[j-1];
3136 priv->chains[0]->arpmt[0] = tmp;
3137 priv->chains[0]->arStreamCaps[0] = tmp2;
3139 priv->chains[0]->nFormatUsed = 0;
3141 if (priv->chains[0]->pmt)
3142 DeleteMediaType(priv->chains[0]->pmt);
3143 priv->chains[0]->pmt =
3144 CreateMediaType(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed]);
3145 DisplayMediaType("VID_SET_FORMAT", priv->chains[0]->pmt);
3147 Setting width & height to preferred by driver values
3149 extract_video_format(priv->chains[0]->arpmt[priv->chains[0]->nFormatUsed],
3150 &(priv->fcc), &(priv->width),
3151 &(priv->height));
3152 return result;
3154 case TVI_CONTROL_VID_GET_FORMAT:
3156 if(!priv->chains[0]->pmt)
3157 return TVI_CONTROL_FALSE;
3159 Build video chain (for video format negotiation).
3160 If this was done before, routine will do nothing.
3162 build_video_chain(priv);
3163 DisplayMediaType("VID_GET_FORMAT", priv->chains[0]->pmt);
3164 if (priv->fcc) {
3165 *(int *) arg = priv->fcc;
3166 return TVI_CONTROL_TRUE;
3167 } else
3168 return TVI_CONTROL_FALSE;
3170 case TVI_CONTROL_VID_SET_WIDTH:
3172 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3173 VIDEOINFOHEADER *Vhdr;
3174 int width = *(int *) arg;
3175 if (priv->state)
3176 return TVI_CONTROL_FALSE;
3178 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3179 if (!pCaps)
3180 return TVI_CONTROL_FALSE;
3181 if (width < pCaps->MinOutputSize.cx
3182 || width > pCaps->MaxOutputSize.cx)
3183 return TVI_CONTROL_FALSE;
3185 if (width % pCaps->OutputGranularityX)
3186 return TVI_CONTROL_FALSE;
3188 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3189 return TVI_CONTROL_FALSE;
3190 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3191 Vhdr->bmiHeader.biWidth = width;
3192 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3193 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3194 Vhdr->bmiHeader.biHeight) >> 3;
3196 priv->width = width;
3198 return TVI_CONTROL_TRUE;
3200 case TVI_CONTROL_VID_GET_WIDTH:
3202 if (priv->width) {
3203 *(int *) arg = priv->width;
3204 return TVI_CONTROL_TRUE;
3205 } else
3206 return TVI_CONTROL_FALSE;
3208 case TVI_CONTROL_VID_CHK_WIDTH:
3210 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3211 int width = *(int *) arg;
3212 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3213 if (!pCaps)
3214 return TVI_CONTROL_FALSE;
3215 if (width < pCaps->MinOutputSize.cx
3216 || width > pCaps->MaxOutputSize.cx)
3217 return TVI_CONTROL_FALSE;
3219 if (width % pCaps->OutputGranularityX)
3220 return TVI_CONTROL_FALSE;
3221 return TVI_CONTROL_TRUE;
3223 case TVI_CONTROL_VID_SET_HEIGHT:
3225 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3226 VIDEOINFOHEADER *Vhdr;
3227 int height = *(int *) arg;
3228 if (priv->state)
3229 return TVI_CONTROL_FALSE;
3231 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3232 if (!pCaps)
3233 return TVI_CONTROL_FALSE;
3234 if (height < pCaps->MinOutputSize.cy
3235 || height > pCaps->MaxOutputSize.cy)
3236 return TVI_CONTROL_FALSE;
3238 if (height % pCaps->OutputGranularityY)
3239 return TVI_CONTROL_FALSE;
3241 if (!priv->chains[0]->pmt || !priv->chains[0]->pmt->pbFormat)
3242 return TVI_CONTROL_FALSE;
3243 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3245 if (Vhdr->bmiHeader.biHeight < 0)
3246 Vhdr->bmiHeader.biHeight = -height;
3247 else
3248 Vhdr->bmiHeader.biHeight = height;
3249 priv->chains[0]->pmt->lSampleSize = Vhdr->bmiHeader.biSizeImage =
3250 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth *
3251 Vhdr->bmiHeader.biHeight) >> 3;
3253 priv->height = height;
3254 return TVI_CONTROL_TRUE;
3256 case TVI_CONTROL_VID_GET_HEIGHT:
3258 if (priv->height) {
3259 *(int *) arg = priv->height;
3260 return TVI_CONTROL_TRUE;
3261 } else
3262 return TVI_CONTROL_FALSE;
3264 case TVI_CONTROL_VID_CHK_HEIGHT:
3266 VIDEO_STREAM_CONFIG_CAPS *pCaps;
3267 int height = *(int *) arg;
3268 pCaps = priv->chains[0]->arStreamCaps[priv->chains[0]->nFormatUsed];
3269 if (!pCaps)
3270 return TVI_CONTROL_FALSE;
3271 if (height < pCaps->MinOutputSize.cy
3272 || height > pCaps->MaxOutputSize.cy)
3273 return TVI_CONTROL_FALSE;
3275 if (height % pCaps->OutputGranularityY)
3276 return TVI_CONTROL_FALSE;
3278 return TVI_CONTROL_TRUE;
3280 case TVI_CONTROL_IS_AUDIO:
3281 if (!priv->chains[1]->pmt)
3282 return TVI_CONTROL_FALSE;
3283 else
3284 return TVI_CONTROL_TRUE;
3285 case TVI_CONTROL_IS_VIDEO:
3286 return TVI_CONTROL_TRUE;
3287 case TVI_CONTROL_AUD_GET_FORMAT:
3289 *(int *) arg = AF_FORMAT_S16_LE;
3290 if (!priv->chains[1]->pmt)
3291 return TVI_CONTROL_FALSE;
3292 else
3293 return TVI_CONTROL_TRUE;
3295 case TVI_CONTROL_AUD_GET_CHANNELS:
3297 *(int *) arg = priv->channels;
3298 if (!priv->chains[1]->pmt)
3299 return TVI_CONTROL_FALSE;
3300 else
3301 return TVI_CONTROL_TRUE;
3303 case TVI_CONTROL_AUD_SET_SAMPLERATE:
3305 int i, samplerate;
3306 if (priv->state)
3307 return TVI_CONTROL_FALSE;
3308 if (!priv->chains[1]->arpmt[0])
3309 return TVI_CONTROL_FALSE;
3311 samplerate = *(int *) arg;
3313 for (i = 0; priv->chains[1]->arpmt[i]; i++)
3314 if (check_audio_format
3315 (priv->chains[1]->arpmt[i], samplerate, 16, priv->channels))
3316 break;
3317 if (!priv->chains[1]->arpmt[i]) {
3318 //request not found. failing back to first available
3319 mp_tmsg(MSGT_TV, MSGL_WARN, "tvi_dshow: Samplerate %d is not supported by device. Failing back to first available.\n", samplerate);
3320 i = 0;
3322 if (priv->chains[1]->pmt)
3323 DeleteMediaType(priv->chains[1]->pmt);
3324 priv->chains[1]->pmt = CreateMediaType(priv->chains[1]->arpmt[i]);
3325 extract_audio_format(priv->chains[1]->arpmt[i], &(priv->samplerate),
3326 NULL, &(priv->channels));
3327 return TVI_CONTROL_TRUE;
3329 case TVI_CONTROL_AUD_GET_SAMPLERATE:
3331 *(int *) arg = priv->samplerate;
3332 if (!priv->samplerate)
3333 return TVI_CONTROL_FALSE;
3334 if (!priv->chains[1]->pmt)
3335 return TVI_CONTROL_FALSE;
3336 else
3337 return TVI_CONTROL_TRUE;
3339 case TVI_CONTROL_AUD_GET_SAMPLESIZE:
3341 WAVEFORMATEX *pWF;
3342 if (!priv->chains[1]->pmt)
3343 return TVI_CONTROL_FALSE;
3344 if (!priv->chains[1]->pmt->pbFormat)
3345 return TVI_CONTROL_FALSE;
3346 pWF = (WAVEFORMATEX *) priv->chains[1]->pmt->pbFormat;
3347 *(int *) arg = pWF->wBitsPerSample / 8;
3348 return TVI_CONTROL_TRUE;
3350 case TVI_CONTROL_IS_TUNER:
3352 if (!priv->pTVTuner)
3353 return TVI_CONTROL_FALSE;
3355 return TVI_CONTROL_TRUE;
3357 case TVI_CONTROL_TUN_SET_NORM:
3359 IAMAnalogVideoDecoder *pVD;
3360 long lAnalogFormat;
3361 int i;
3362 HRESULT hr;
3364 i = *(int *) arg;
3365 i--;
3366 if (i < 0 || i >= tv_available_norms_count)
3367 return TVI_CONTROL_FALSE;
3368 lAnalogFormat = tv_norms[tv_available_norms[i]].index;
3370 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3371 if (hr != S_OK)
3372 return TVI_CONTROL_FALSE;
3373 hr = OLE_CALL_ARGS(pVD, put_TVFormat, lAnalogFormat);
3374 OLE_RELEASE_SAFE(pVD);
3375 if (FAILED(hr))
3376 return TVI_CONTROL_FALSE;
3377 else
3378 return TVI_CONTROL_TRUE;
3380 case TVI_CONTROL_TUN_GET_NORM:
3382 long lAnalogFormat;
3383 int i;
3384 HRESULT hr;
3385 IAMAnalogVideoDecoder *pVD;
3387 hr = OLE_QUERYINTERFACE(priv->chains[0]->pCaptureFilter,IID_IAMAnalogVideoDecoder, pVD);
3388 if (hr == S_OK) {
3389 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat);
3390 OLE_RELEASE_SAFE(pVD);
3393 if (FAILED(hr)) { //trying another method
3394 if (!priv->pTVTuner)
3395 return TVI_CONTROL_FALSE;
3396 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat);
3397 if (FAILED(hr))
3398 return TVI_CONTROL_FALSE;
3400 for (i = 0; i < tv_available_norms_count; i++) {
3401 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) {
3402 *(int *) arg = i + 1;
3403 return TVI_CONTROL_TRUE;
3406 return TVI_CONTROL_FALSE;
3408 case TVI_CONTROL_SPC_GET_NORMID:
3410 int i;
3411 if (!priv->pTVTuner)
3412 return TVI_CONTROL_FALSE;
3413 for (i = 0; i < tv_available_norms_count; i++) {
3414 if (!strcasecmp
3415 (tv_norms[tv_available_norms[i]].name, (char *) arg)) {
3416 *(int *) arg = i + 1;
3417 return TVI_CONTROL_TRUE;
3420 return TVI_CONTROL_FALSE;
3422 case TVI_CONTROL_SPC_SET_INPUT:
3424 return set_crossbar_input(priv, *(int *) arg);
3426 case TVI_CONTROL_TUN_GET_FREQ:
3428 unsigned long lFreq;
3429 int ret;
3430 if (!priv->pTVTuner)
3431 return TVI_CONTROL_FALSE;
3433 ret = get_frequency(priv, &lFreq);
3434 lFreq = lFreq / (1000000/16); //convert from Hz to 1/16 MHz units
3436 *(unsigned long *) arg = lFreq;
3437 return ret;
3439 case TVI_CONTROL_TUN_SET_FREQ:
3441 unsigned long nFreq = *(unsigned long *) arg;
3442 if (!priv->pTVTuner)
3443 return TVI_CONTROL_FALSE;
3444 //convert to Hz
3445 nFreq = (1000000/16) * nFreq; //convert from 1/16 MHz units to Hz
3446 return set_frequency(priv, nFreq);
3448 case TVI_CONTROL_VID_SET_HUE:
3449 return set_control(priv, VideoProcAmp_Hue, *(int *) arg);
3450 case TVI_CONTROL_VID_GET_HUE:
3451 return get_control(priv, VideoProcAmp_Hue, (int *) arg);
3452 case TVI_CONTROL_VID_SET_CONTRAST:
3453 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg);
3454 case TVI_CONTROL_VID_GET_CONTRAST:
3455 return get_control(priv, VideoProcAmp_Contrast, (int *) arg);
3456 case TVI_CONTROL_VID_SET_SATURATION:
3457 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg);
3458 case TVI_CONTROL_VID_GET_SATURATION:
3459 return get_control(priv, VideoProcAmp_Saturation, (int *) arg);
3460 case TVI_CONTROL_VID_SET_BRIGHTNESS:
3461 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg);
3462 case TVI_CONTROL_VID_GET_BRIGHTNESS:
3463 return get_control(priv, VideoProcAmp_Brightness, (int *) arg);
3465 case TVI_CONTROL_VID_GET_FPS:
3467 VIDEOINFOHEADER *Vhdr;
3468 if (!priv->chains[0]->pmt)
3469 return TVI_CONTROL_FALSE;
3470 if (!priv->chains[0]->pmt->pbFormat)
3471 return TVI_CONTROL_FALSE;
3472 Vhdr = (VIDEOINFOHEADER *) priv->chains[0]->pmt->pbFormat;
3473 *(float *) arg =
3474 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8);
3475 return TVI_CONTROL_TRUE;
3477 case TVI_CONTROL_IMMEDIATE:
3478 priv->immediate_mode = 1;
3479 return TVI_CONTROL_TRUE;
3480 case TVI_CONTROL_VBI_INIT:
3482 void* ptr;
3483 ptr=&(priv->tsp);
3484 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==VBI_CONTROL_TRUE)
3485 priv->priv_vbi=ptr;
3486 else
3487 priv->priv_vbi=NULL;
3488 return TVI_CONTROL_TRUE;
3490 case TVI_CONTROL_GET_VBI_PTR:
3491 *(void **)arg=priv->priv_vbi;
3492 return TVI_CONTROL_TRUE;
3494 return TVI_CONTROL_UNKNOWN;