Mercurial > mplayer.hg
annotate stream/tvi_dshow.c @ 25063:29260745e4fa
Pass all available formats to chain building routine and
establish connection with first of available formats.
This will make further format negotiation patch slightly
simpler.
To avoid pins connection error due to unsuported format
at top of the list, put requested video format to the
top of list. This will also useful with upcoming patch -
negotiation will be started from requested format.
author | voroshil |
---|---|
date | Sun, 18 Nov 2007 10:51:22 +0000 |
parents | 1f0eb7aa3206 |
children | f9a966f36dae |
rev | line source |
---|---|
24744 | 1 /* |
2 * TV support under Win32 | |
3 * | |
4 * (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>. | |
5 * | |
6 * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c code . | |
7 * | |
8 * ------------------------------------------------------------------------------- | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
23 * | |
24 * | |
25 * ------------------------------------------------------------------------------- | |
26 * | |
27 * | |
28 * WARNING: This is alpha code! | |
29 * | |
30 * Abilities: | |
31 * * Watching TV under Win32 using WDM Capture driver and DirectShow | |
32 * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow) | |
33 * * If device driver provides IAMStreamConfig interface, user can choose width/height with "-tv height=<h>:width=<w>" | |
34 * * Adjusting BRIGHTNESS,HUE,SATURATION,CONTRAST if supported by device | |
35 * * Selecting Tuner,S-Video,... as media source | |
36 * * User can select used video capture device, passing -tv device=<dev#> | |
37 * * User can select used audio input, passing -tv audioid=<input#> | |
38 * | |
39 * options which will not be implemented (probably sometime in future, if possible): | |
40 * * alsa | |
41 * * mjpeg | |
42 * * decimation=<1|2|4> | |
43 * * quality=<0\-100> | |
44 * * forceaudio | |
45 * * forcechan=<1\-2> | |
46 * * [volume|bass|treble|balance] | |
47 * | |
48 * Works with: | |
49 * - LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov | |
50 * Partially works with: | |
51 * - ATI 9200 VIVO based card | |
52 * - ATI AIW 7500 | |
53 * - nVidia Ti-4400 | |
54 * | |
55 * Known bugs: | |
56 * * stream goes with 24.93 FPS (NTSC), while reporting 25 FPS (PAL) ?! | |
57 * * direct set frequency call does not work ('Insufficient Buffer' error) | |
58 * * audio stream goes with about 1 sample/sec rate when capturing sound from audio card | |
59 * | |
60 * TODO: | |
61 * * check audio with small buffer on vivo !!! | |
62 * * norm for IAMVideoDecoder and for IAMTVtuner - differs !! | |
63 * * check how to change TVFormat on VIVO card without tuner | |
64 * * Flip image upside-down for RGB formats. | |
65 * * | |
66 * * remove debug sleep() | |
67 * * Add some notes to methods' parameters | |
68 * * refactor console messages | |
69 * * check using header files and keep only needed | |
70 * * add additional comments to methods' bodies | |
71 * | |
72 */ | |
73 | |
74 | |
75 /// \ingroup tvi_dshow | |
76 | |
77 #include "config.h" | |
78 | |
79 #include <stdio.h> | |
80 #include "libmpcodecs/img_format.h" | |
81 #include "libaf/af_format.h" | |
82 #include "help_mp.h" | |
83 #include "osdep/timer.h" | |
84 | |
85 | |
86 #include "tv.h" | |
87 #include "mp_msg.h" | |
88 #include "frequencies.h" | |
89 | |
90 | |
91 #include "tvi_dshow.h" | |
92 | |
93 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param); | |
94 | |
95 /* | |
96 *--------------------------------------------------------------------------------------- | |
97 * | |
98 * Data structures | |
99 * | |
100 *--------------------------------------------------------------------------------------- | |
101 */ | |
102 /** | |
103 information about this file | |
104 */ | |
105 tvi_info_t tvi_info_dshow = { | |
106 tvi_init_dshow, | |
107 "DirectShow TV", | |
108 "dshow", | |
109 "Vladimir Voroshilov", | |
110 "Very experimental!! Use with caution" | |
111 }; | |
112 | |
113 | |
114 /** | |
115 ringbuffer related info | |
116 */ | |
117 typedef struct { | |
118 CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex) | |
119 char **ringbuffer; ///< ringbuffer array | |
120 double*dpts; ///< samples' timestamps | |
121 | |
122 int buffersize; ///< size of buffer in blocks | |
123 int blocksize; ///< size of individual block | |
124 int head; ///< index of first valid sample | |
125 int tail; ///< index of last valid sample | |
126 int count; ///< count of valid samples in ringbuffer | |
127 double tStart; ///< pts of first sample (first sample should have pts 0) | |
128 } grabber_ringbuffer_t; | |
129 | |
130 /** | |
131 CSampleGrabberCD definition | |
132 */ | |
133 typedef struct CSampleGrabberCB { | |
134 ISampleGrabberCBVtbl *lpVtbl; | |
135 int refcount; | |
136 GUID interfaces[2]; | |
137 grabber_ringbuffer_t *pbuf; | |
138 } CSampleGrabberCB; | |
139 | |
140 typedef struct { | |
141 int dev_index; ///< capture device index in device list (defaul: 0, first available device) | |
142 int adev_index; ///< audio capture device index in device list (default: -1, not used) | |
143 int immediate_mode; ///< immediate mode (no sound capture) | |
144 int state; ///< state: 1-filter graph running, 0-filter graph stopped | |
145 int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency | |
146 int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency | |
147 | |
148 int fcc; ///< used video format code (FourCC) | |
149 int width; ///< picture width (default: auto) | |
150 int height; ///< picture height (default: auto) | |
151 | |
152 int channels; ///< number of audio channels (default: auto) | |
153 int samplerate; ///< audio samplerate (default: auto) | |
154 | |
155 long *freq_table; ///< frequency table (in Hz) | |
156 int freq_table_len; ///< length of freq table | |
157 int first_channel; ///< channel number of first entry in freq table | |
158 int input; ///< used input | |
159 | |
160 // video related stuff | |
161 int nVideoFormatUsed; ///< index of used video format | |
162 AM_MEDIA_TYPE **arpmtVideo; ///< available video formats | |
163 VIDEO_STREAM_CONFIG_CAPS **arVideoCaps; ///< capabilities of output video | |
164 AM_MEDIA_TYPE *pmtVideo; ///< video stream properties | |
165 grabber_ringbuffer_t *v_buf; | |
166 IBaseFilter *pVideoFilter; ///< interface for capture device | |
167 IAMStreamConfig *pVideoStreamConfig; ///< for configuring video stream | |
168 | |
169 // audio related stuff | |
170 int nAudioFormatUsed; ///< index of used audio format | |
171 AM_MEDIA_TYPE **arpmtAudio; ///< available audio formats | |
172 AUDIO_STREAM_CONFIG_CAPS **arAudioCaps; ///< capabilities of output audio | |
173 AM_MEDIA_TYPE *pmtAudio; ///< audio stream properties. | |
174 grabber_ringbuffer_t *a_buf; | |
175 IBaseFilter *pAudioFilter; ///< interface for audio capture device (if adevice passed) | |
176 IAMStreamConfig *pAudioStreamConfig; ///< for configuring audio stream | |
177 | |
178 AM_MEDIA_TYPE *pmtVBI; ///< available audio formats | |
179 grabber_ringbuffer_t *vbi_buf; | |
180 | |
181 IAMTVTuner *pTVTuner; ///< interface for tuner device | |
182 IGraphBuilder *pGraph; ///< filter graph | |
183 ICaptureGraphBuilder2 *pBuilder; ///< graph builder | |
184 IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...) | |
185 IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc | |
186 IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...) | |
187 DWORD dwRegister; ///< allow graphedit to connect to our graph | |
188 CSampleGrabberCB* pCSGCB; ///< callback object | |
189 void *priv_vbi; ///< private VBI data structure | |
190 tt_stream_props tsp; ///< data for VBI initialization | |
191 | |
192 tv_param_t* tv_param; ///< TV parameters | |
193 } priv_t; | |
194 | |
195 #include "tvi_def.h" | |
196 | |
197 /** | |
198 country table entry structure (for loading freq table stored in kstvtuner.ax | |
199 | |
200 \note | |
201 structure have to be 2-byte aligned and have 10-byte length!! | |
202 */ | |
203 typedef struct __attribute__((__packed__)) { | |
204 WORD CountryCode; ///< Country code | |
205 WORD CableFreqTable; ///< index of resource with frequencies for cable channels | |
206 WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels | |
207 DWORD VideoStandard; ///< used video standard | |
208 } TRCCountryList; | |
209 /** | |
210 information about image formats | |
211 */ | |
212 typedef struct { | |
213 uint32_t fmt; ///< FourCC | |
214 const GUID *subtype; ///< DirectShow's subtype | |
215 int nBits; ///< number of bits | |
216 int nCompression; ///< complression | |
217 int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure | |
218 } img_fmt; | |
219 | |
220 /* | |
221 *--------------------------------------------------------------------------------------- | |
222 * | |
223 * Methods forward declaration | |
224 * | |
225 *--------------------------------------------------------------------------------------- | |
226 */ | |
227 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize, | |
228 int blocksize); | |
229 static HRESULT show_filter_info(IBaseFilter * pFilter); | |
230 #if 0 | |
231 //defined in current MinGW release | |
232 HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **); | |
233 HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **); | |
234 #endif | |
235 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t * | |
236 pbuf); | |
237 static int set_crossbar_input(priv_t * priv, int input); | |
238 static int subtype2imgfmt(const GUID * subtype); | |
239 | |
240 /* | |
241 *--------------------------------------------------------------------------------------- | |
242 * | |
243 * Global constants and variables | |
244 * | |
245 *--------------------------------------------------------------------------------------- | |
246 */ | |
247 /** | |
248 lookup tables for physical connector types | |
249 */ | |
250 static const struct { | |
251 long type; | |
252 char *name; | |
253 } tv_physcon_types[]={ | |
254 {PhysConn_Video_Tuner, "Tuner" }, | |
255 {PhysConn_Video_Composite, "Composite" }, | |
256 {PhysConn_Video_SVideo, "S-Video" }, | |
257 {PhysConn_Video_RGB, "RGB" }, | |
258 {PhysConn_Video_YRYBY, "YRYBY" }, | |
259 {PhysConn_Video_SerialDigital, "SerialDigital" }, | |
260 {PhysConn_Video_ParallelDigital, "ParallelDigital"}, | |
261 {PhysConn_Video_VideoDecoder, "VideoDecoder" }, | |
262 {PhysConn_Video_VideoEncoder, "VideoEncoder" }, | |
263 {PhysConn_Video_SCART, "SCART" }, | |
264 {PhysConn_Video_Black, "Blaack" }, | |
265 {PhysConn_Audio_Tuner, "Tuner" }, | |
266 {PhysConn_Audio_Line, "Line" }, | |
267 {PhysConn_Audio_Mic, "Mic" }, | |
268 {PhysConn_Audio_AESDigital, "AESDiital" }, | |
269 {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" }, | |
270 {PhysConn_Audio_AudioDecoder, "AudioDecoder" }, | |
271 {PhysConn_Audio_SCSI, "SCSI" }, | |
272 {PhysConn_Video_SCSI, "SCSI" }, | |
273 {PhysConn_Audio_AUX, "AUX" }, | |
274 {PhysConn_Video_AUX, "AUX" }, | |
275 {PhysConn_Audio_1394, "1394" }, | |
276 {PhysConn_Video_1394, "1394" }, | |
277 {PhysConn_Audio_USB, "USB" }, | |
278 {PhysConn_Video_USB, "USB" }, | |
279 {-1, NULL } | |
280 }; | |
281 | |
282 static const struct { | |
283 char *chanlist_name; | |
284 int country_code; | |
285 } tv_chanlist2country[]={ | |
286 {"us-bcast", 1}, | |
287 {"russia", 7}, | |
288 {"argentina", 54}, | |
289 {"japan-bcast", 81}, | |
290 {"china-bcast", 86}, | |
291 {"southafrica", 27}, | |
292 {"australia", 61}, | |
293 {"ireland", 353}, | |
294 {"france", 33}, | |
295 {"italy", 39}, | |
296 {"newzealand", 64}, | |
297 //directshow table uses eastern europe freq table for russia | |
298 {"europe-east", 7}, | |
299 //directshow table uses western europe freq table for germany | |
300 {"europe-west", 49}, | |
301 /* cable channels */ | |
302 {"us-cable", 1}, | |
303 {"us-cable-hrc", 1}, | |
304 {"japan-cable", 81}, | |
305 //default is USA | |
306 {NULL, 1} | |
307 }; | |
308 | |
309 /** | |
310 array, contains information about various supported (i hope) image formats | |
311 */ | |
312 static const img_fmt img_fmt_list[] = { | |
25054 | 313 {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 16, IMGFMT_YUY2, 0}, |
314 {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 12, IMGFMT_YV12, 0}, | |
315 {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 12, IMGFMT_IYUV, 0}, | |
316 {IMGFMT_I420, &MEDIASUBTYPE_I420, 12, IMGFMT_I420, 0}, | |
317 {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 16, IMGFMT_UYVY, 0}, | |
318 {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 16, IMGFMT_YVYU, 0}, | |
319 {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 9, IMGFMT_YVU9, 0}, | |
320 {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0}, | |
321 {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0}, | |
322 {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12}, | |
323 {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12}, | |
324 {0, &GUID_NULL, 0, 0, 0} | |
24744 | 325 }; |
326 | |
327 #define TV_NORMS_COUNT 19 | |
328 static const struct { | |
329 long index; | |
330 char *name; | |
331 } tv_norms[TV_NORMS_COUNT] = { | |
332 { | |
333 AnalogVideo_NTSC_M, "ntsc-m"}, { | |
334 AnalogVideo_NTSC_M_J, "ntsc-mj"}, { | |
335 AnalogVideo_NTSC_433, "ntsc-433"}, { | |
336 AnalogVideo_PAL_B, "pal-b"}, { | |
337 AnalogVideo_PAL_D, "pal-d"}, { | |
338 AnalogVideo_PAL_G, "pal-g"}, { | |
339 AnalogVideo_PAL_H, "pal-h"}, { | |
340 AnalogVideo_PAL_I, "pal-i"}, { | |
341 AnalogVideo_PAL_M, "pal-m"}, { | |
342 AnalogVideo_PAL_N, "pal-n"}, { | |
343 AnalogVideo_PAL_60, "pal-60"}, { | |
344 AnalogVideo_SECAM_B, "secam-b"}, { | |
345 AnalogVideo_SECAM_D, "secam-d"}, { | |
346 AnalogVideo_SECAM_G, "secam-g"}, { | |
347 AnalogVideo_SECAM_H, "secam-h"}, { | |
348 AnalogVideo_SECAM_K, "secam-k"}, { | |
349 AnalogVideo_SECAM_K1, "secam-k1"}, { | |
350 AnalogVideo_SECAM_L, "secam-l"}, { | |
351 AnalogVideo_SECAM_L1, "secam-l1"} | |
352 }; | |
353 static long tv_available_norms[TV_NORMS_COUNT]; | |
354 static int tv_available_norms_count = 0; | |
355 | |
356 | |
357 static long *tv_available_inputs; | |
358 static int tv_available_inputs_count = 0; | |
359 | |
360 /* | |
361 *--------------------------------------------------------------------------------------- | |
362 * | |
363 * Various GUID definitions | |
364 * | |
365 *--------------------------------------------------------------------------------------- | |
366 */ | |
367 /// CLSID definitions (used for CoCreateInstance call) | |
368 DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B, | |
369 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37); | |
370 DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B, | |
371 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37); | |
372 DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B, | |
373 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86); | |
374 DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3, | |
375 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); | |
376 DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0, | |
377 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86); | |
378 DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0, | |
379 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86); | |
380 DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53, | |
381 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
382 DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53, | |
383 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
384 #ifdef NOT_USED | |
385 DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3, | |
386 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); | |
387 DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a, | |
388 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2); | |
389 DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, | |
390 0xaf, 0x0b, 0xa7, 0x70); | |
391 DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3, | |
392 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5); | |
393 DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, | |
394 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
395 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f, | |
396 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
397 #endif | |
398 | |
399 /// IID definitions (used for QueryInterface call) | |
400 DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a, | |
401 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
402 DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0, | |
403 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); | |
404 DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, | |
405 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); | |
406 DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD, | |
407 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F); | |
408 DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0, | |
409 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85); | |
410 DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab, | |
411 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d); | |
412 DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b, | |
413 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86); | |
414 DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a, | |
415 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
416 DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C, | |
417 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56); | |
418 DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, | |
419 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
420 DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a, | |
421 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); | |
422 DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00, | |
423 0xAA, 0x00, 0xBD, 0x83, 0x39); | |
424 DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00, | |
425 0xa0, 0xc9, 0x11, 0x89, 0x56); | |
426 DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c, | |
427 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56); | |
428 DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0, | |
429 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); | |
430 DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00, | |
431 0xA0, 0xC9, 0x56, 0x02, 0x66); | |
432 DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1, | |
433 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56); | |
434 DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00, | |
435 0xaa, 0x00, 0x4b, 0xb8, 0x51); | |
436 DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f, | |
437 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
438 DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f, | |
439 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
440 DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f, | |
441 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
442 DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f, | |
443 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
444 DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00, | |
445 0xa0, 0xc9, 0x11, 0x89, 0x56); | |
446 DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00, | |
447 0x00, 0xc0, 0xcc, 0x16, 0xba); | |
448 | |
449 #define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1) | |
450 #define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY)) | |
451 | |
452 #define DEVICE_NAME_MAX_LEN 2000 | |
453 | |
454 /*--------------------------------------------------------------------------------------- | |
455 * Methods, called only from this file | |
456 *---------------------------------------------------------------------------------------*/ | |
457 | |
458 void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){ | |
459 ALLOCATOR_PROPERTIES prop; | |
460 IAMBufferNegotiation* pBN; | |
461 HRESULT hr; | |
462 | |
463 prop.cbAlign = -1; | |
464 prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv; | |
465 if (!prop.cbBuffer) | |
466 prop.cbBuffer = 1; | |
467 prop.cbBuffer += pWF->nBlockAlign - 1; | |
468 prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign; | |
469 prop.cbPrefix = -1; | |
470 prop.cBuffers = -1; | |
471 | |
472 hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN); | |
473 if(FAILED(hr)) | |
474 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr); | |
475 else{ | |
476 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop); | |
477 if(FAILED(hr)) | |
478 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr); | |
479 OLE_RELEASE_SAFE(pBN); | |
480 } | |
481 hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN); | |
482 if(FAILED(hr)) | |
483 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr); | |
484 else{ | |
485 hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop); | |
486 if(FAILED(hr)) | |
487 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr); | |
488 OLE_RELEASE_SAFE(pBN); | |
489 } | |
490 } | |
491 /* | |
492 *--------------------------------------------------------------------------------------- | |
493 * | |
494 * CSampleGrabberCD class. Used for receiving samples from DirectShow. | |
495 * | |
496 *--------------------------------------------------------------------------------------- | |
497 */ | |
498 /// CSampleGrabberCD destructor | |
499 static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This) | |
500 { | |
501 free(This->lpVtbl); | |
502 free(This); | |
503 } | |
504 | |
505 /// CSampleGrabberCD IUnknown interface methods implementation | |
506 static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB * | |
507 This, | |
508 const GUID * riid, | |
509 void **ppvObject) | |
510 { | |
511 CSampleGrabberCB *me = (CSampleGrabberCB *) This; | |
512 GUID *r; | |
513 unsigned int i = 0; | |
514 Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This); | |
515 if (!ppvObject) | |
516 return E_POINTER; | |
517 for (r = me->interfaces; | |
518 i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++) | |
519 if (!memcmp(r, riid, sizeof(*r))) { | |
520 OLE_CALL(This, AddRef); | |
521 *ppvObject = This; | |
522 return 0; | |
523 } | |
524 Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid); | |
525 return E_NOINTERFACE; | |
526 } | |
527 | |
528 static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This) | |
529 { | |
530 CSampleGrabberCB *me = (CSampleGrabberCB *) This; | |
531 Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This, | |
532 me->refcount); | |
533 return ++(me->refcount); | |
534 } | |
535 | |
536 static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This) | |
537 { | |
538 CSampleGrabberCB *me = (CSampleGrabberCB *) This; | |
539 Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n", | |
540 This, me->refcount - 1); | |
541 if (--(me->refcount) == 0) | |
542 CSampleGrabberCB_Destroy(me); | |
543 return 0; | |
544 } | |
545 | |
546 | |
547 HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This, | |
548 double SampleTime, | |
549 BYTE * pBuffer, long lBufferLen) | |
550 { | |
551 CSampleGrabberCB *this = (CSampleGrabberCB *) This; | |
552 grabber_ringbuffer_t *rb = this->pbuf; | |
553 | |
554 if (!lBufferLen) | |
555 return E_FAIL; | |
556 | |
557 if (!rb->ringbuffer) { | |
558 rb->buffersize /= lBufferLen; | |
559 if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK) | |
560 return E_FAIL; | |
561 } | |
562 mp_msg(MSGT_TV, MSGL_DBG4, | |
563 "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime); | |
564 EnterCriticalSection(rb->pMutex); | |
565 if (rb->count >= rb->buffersize) { | |
566 rb->head = (rb->head + 1) % rb->buffersize; | |
567 rb->count--; | |
568 } | |
569 | |
570 memcpy(rb->ringbuffer[rb->tail], pBuffer, | |
571 lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize); | |
572 rb->dpts[rb->tail] = SampleTime; | |
573 rb->tail = (rb->tail + 1) % rb->buffersize; | |
574 rb->count++; | |
575 LeaveCriticalSection(rb->pMutex); | |
576 | |
577 return S_OK; | |
578 } | |
579 | |
580 /// wrapper. directshow does the same when BufferCB callback is requested | |
581 HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This, | |
582 double SampleTime, | |
583 LPMEDIASAMPLE pSample) | |
584 { | |
585 char* buf; | |
586 long len; | |
587 long long tStart,tEnd; | |
588 HRESULT hr; | |
589 grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf; | |
590 | |
591 len=OLE_CALL(pSample,GetSize); | |
592 tStart=tEnd=0; | |
593 hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd); | |
594 if(FAILED(hr)){ | |
595 return hr; | |
596 } | |
597 mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart); | |
598 hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf); | |
599 if(FAILED(hr)){ | |
600 return hr; | |
601 } | |
602 hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len); | |
603 return hr; | |
604 | |
605 } | |
606 | |
607 /// main grabbing routine | |
608 static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t * | |
609 pbuf) | |
610 { | |
611 CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB)); | |
612 if (!This) | |
613 return NULL; | |
614 | |
615 This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl)); | |
616 if (!This->lpVtbl) { | |
617 CSampleGrabberCB_Destroy(This); | |
618 return NULL; | |
619 } | |
620 This->refcount = 1; | |
621 This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface; | |
622 This->lpVtbl->AddRef = CSampleGrabberCB_AddRef; | |
623 This->lpVtbl->Release = CSampleGrabberCB_Release; | |
624 This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB; | |
625 This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB; | |
626 | |
627 This->interfaces[0] = IID_IUnknown; | |
628 This->interfaces[1] = IID_ISampleGrabberCB; | |
629 | |
630 This->pbuf = pbuf; | |
631 | |
632 return This; | |
633 } | |
634 | |
635 /* | |
636 *--------------------------------------------------------------------------------------- | |
637 * | |
638 * ROT related methods (register, unregister) | |
639 * | |
640 *--------------------------------------------------------------------------------------- | |
641 */ | |
642 /** | |
643 Registering graph in ROT. User will be able to connect to graph from GraphEdit. | |
644 */ | |
645 static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister) | |
646 { | |
647 IMoniker *pMoniker; | |
648 IRunningObjectTable *pROT; | |
649 WCHAR wsz[256]; | |
650 HRESULT hr; | |
651 | |
652 if (FAILED(GetRunningObjectTable(0, &pROT))) { | |
653 return E_FAIL; | |
654 } | |
655 wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph, | |
656 GetCurrentProcessId()); | |
657 hr = CreateItemMoniker(L"!", wsz, &pMoniker); | |
658 if (SUCCEEDED(hr)) { | |
659 hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE, | |
660 pUnkGraph, pMoniker, pdwRegister); | |
661 OLE_RELEASE_SAFE(pMoniker); | |
662 } | |
663 OLE_RELEASE_SAFE(pROT); | |
664 return hr; | |
665 } | |
666 | |
667 /// Unregistering graph in ROT | |
668 static void RemoveFromRot(DWORD dwRegister) | |
669 { | |
670 IRunningObjectTable *pROT; | |
671 if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { | |
672 OLE_CALL_ARGS(pROT, Revoke, dwRegister); | |
673 OLE_RELEASE_SAFE(pROT); | |
674 } | |
675 } | |
676 | |
677 /* | |
678 *--------------------------------------------------------------------------------------- | |
679 * | |
680 * ringbuffer related methods (init, destroy) | |
681 * | |
682 *--------------------------------------------------------------------------------------- | |
683 */ | |
684 /** | |
685 * \brief ringbuffer destroying routine | |
686 * | |
687 * \param rb pointer to empty (just allocated) ringbuffer structure | |
688 * | |
689 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure | |
690 */ | |
691 static void destroy_ringbuffer(grabber_ringbuffer_t * rb) | |
692 { | |
693 int i; | |
694 | |
695 if (!rb) | |
696 return; | |
697 | |
698 if (rb->ringbuffer) { | |
699 for (i = 0; i < rb->buffersize; i++) | |
700 if (rb->ringbuffer[i]) | |
701 free(rb->ringbuffer[i]); | |
702 free(rb->ringbuffer); | |
703 rb->ringbuffer = NULL; | |
704 } | |
705 if (rb->dpts) { | |
706 free(rb->dpts); | |
707 rb->dpts = NULL; | |
708 } | |
709 if (rb->pMutex) { | |
710 DeleteCriticalSection(rb->pMutex); | |
711 free(rb->pMutex); | |
712 rb->pMutex = NULL; | |
713 } | |
714 | |
715 rb->blocksize = 0; | |
716 rb->buffersize = 0; | |
717 rb->head = 0; | |
718 rb->tail = 0; | |
719 rb->count = 0; | |
720 } | |
721 | |
722 /** | |
723 * \brief ringbuffer initialization | |
724 * | |
725 * \param rb pointer to empty (just allocated) ringbuffer structure | |
726 * \param buffersize size of buffer in blocks | |
727 * \param blocksize size of buffer's block | |
728 * | |
729 * \return S_OK if success | |
730 * \return E_OUTOFMEMORY not enough memory | |
731 * | |
732 * \note routine does not allocates memory for grabber_rinbuffer_s structure | |
733 */ | |
734 static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize, | |
735 int blocksize) | |
736 { | |
737 int i; | |
738 | |
739 if (!rb) | |
740 return E_OUTOFMEMORY; | |
741 | |
742 rb->buffersize = buffersize < 2 ? 2 : buffersize; | |
743 rb->blocksize = blocksize; | |
744 | |
745 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n", | |
746 rb->buffersize, rb->blocksize); | |
747 | |
748 rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *)); | |
749 if (!rb) | |
750 return E_POINTER; | |
751 memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *)); | |
752 | |
753 for (i = 0; i < rb->buffersize; i++) { | |
754 rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char)); | |
755 if (!rb->ringbuffer[i]) { | |
756 destroy_ringbuffer(rb); | |
757 return E_OUTOFMEMORY; | |
758 } | |
759 } | |
760 rb->dpts = (double*) malloc(rb->buffersize * sizeof(double)); | |
761 if (!rb->dpts) { | |
762 destroy_ringbuffer(rb); | |
763 return E_OUTOFMEMORY; | |
764 } | |
765 rb->head = 0; | |
766 rb->tail = 0; | |
767 rb->count = 0; | |
768 rb->tStart = -1; | |
769 rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); | |
770 if (!rb->pMutex) { | |
771 destroy_ringbuffer(rb); | |
772 return E_OUTOFMEMORY; | |
773 } | |
774 InitializeCriticalSection(rb->pMutex); | |
775 return S_OK; | |
776 } | |
777 | |
778 /* | |
779 *--------------------------------------------------------------------------------------- | |
780 * | |
781 * Tuner related methods (frequency, capabilities, etc | |
782 * | |
783 *--------------------------------------------------------------------------------------- | |
784 */ | |
785 /** | |
786 * \brief returns string with name for givend PsysCon_* constant | |
787 * | |
788 * \param lPhysicalType constant from PhysicalConnectorType enumeration | |
789 * | |
790 * \return pointer to string with apropriate name | |
791 * | |
792 * \note | |
793 * Caller should not free returned pointer | |
794 */ | |
795 static char *physcon2str(const long lPhysicalType) | |
796 { | |
797 int i; | |
798 for(i=0; tv_physcon_types[i].name; i++) | |
799 if(tv_physcon_types[i].type==lPhysicalType) | |
800 return tv_physcon_types[i].name; | |
801 return "Unknown"; | |
802 }; | |
803 | |
804 /** | |
805 * \brief converts MPlayer's chanlist to system country code. | |
806 * | |
807 * \param chanlist MPlayer's chanlist name | |
808 * | |
809 * \return system country code | |
810 * | |
811 * \remarks | |
812 * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified | |
813 * country (which is usually larger then MPlayer's one, so workaround will work fine). | |
814 * | |
815 * \todo | |
816 * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes. | |
817 */ | |
818 static int chanlist2country(char *chanlist) | |
819 { | |
820 int i; | |
821 for(i=0; tv_chanlist2country[i].chanlist_name; i++) | |
822 if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name)) | |
823 break; | |
824 return tv_chanlist2country[i].country_code; | |
825 } | |
826 | |
827 /** | |
828 * \brief loads specified resource from module and return pointer to it | |
829 * | |
830 * \param hDLL valid module desriptor | |
831 * \param index index of resource. resource with name "#<index>" will be loaded | |
832 * | |
833 * \return pointer to loader resource or NULL if error occured | |
834 */ | |
835 static void *GetRC(HMODULE hDLL, int index) | |
836 { | |
837 char szRCDATA[10]; | |
838 char szName[10]; | |
839 HRSRC hRes; | |
840 HGLOBAL hTable; | |
841 | |
842 snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA); | |
843 snprintf(szName, 10, "#%d", index); | |
844 | |
845 hRes = FindResource(hDLL, szName, szRCDATA); | |
846 if (!hRes) { | |
847 return NULL; | |
848 } | |
849 hTable = LoadResource(hDLL, hRes); | |
850 if (!hTable) { | |
851 return NULL; | |
852 } | |
853 return LockResource(hTable); | |
854 } | |
855 | |
856 /** | |
857 * \brief loads frequency table for given country from kstvtune.ax | |
858 * | |
859 * \param[in] nCountry - country code | |
860 * \param[in] nInputType (TunerInputCable or TunerInputAntenna) | |
861 * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies | |
862 * \param[out] pnLen length of array | |
863 * \param[out] pnFirst - channel number of first entry in array (nChannelMax) | |
864 * | |
865 * \return S_OK if success | |
866 * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL | |
867 * \return E_FAIL error occured during load | |
868 * | |
869 * \remarks | |
870 * - array must be freed by caller | |
871 * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL | |
872 */ | |
873 static HRESULT load_freq_table(int nCountry, int nInputType, | |
874 long **pplFreqTable, int *pnLen, | |
875 int *pnFirst) | |
876 { | |
877 HMODULE hDLL; | |
878 long *plFreqTable; | |
879 TRCCountryList *pCountryList; | |
880 int i, index; | |
881 | |
882 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%d)\n",nCountry,nInputType); | |
883 /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */ | |
884 | |
885 if (!pplFreqTable || !pnFirst || !pnLen) | |
886 return E_POINTER; | |
887 if (!nCountry) | |
888 return E_FAIL; | |
889 | |
890 hDLL = LoadLibrary("kstvtune.ax"); | |
891 if (!hDLL) { | |
892 return E_FAIL; | |
893 } | |
894 pCountryList = GetRC(hDLL, 9999); | |
895 if (!pCountryList) { | |
896 FreeLibrary(hDLL); | |
897 return E_FAIL; | |
898 } | |
899 for (i = 0; pCountryList[i].CountryCode != 0; i++) | |
900 if (pCountryList[i].CountryCode == nCountry) | |
901 break; | |
902 if (pCountryList[i].CountryCode == 0) { | |
903 FreeLibrary(hDLL); | |
904 return E_FAIL; | |
905 } | |
906 if (nInputType == TunerInputCable) | |
907 index = pCountryList[i].CableFreqTable; | |
908 else | |
909 index = pCountryList[i].BroadcastFreqTable; | |
910 | |
911 plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel | |
912 if (!plFreqTable) { | |
913 FreeLibrary(hDLL); | |
914 return E_FAIL; | |
915 } | |
916 *pnFirst = plFreqTable[0]; | |
917 *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1); | |
918 *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long)); | |
919 if (!*pplFreqTable) { | |
920 FreeLibrary(hDLL); | |
921 return E_FAIL; | |
922 } | |
923 for (i = 0; i < *pnLen; i++) { | |
924 (*pplFreqTable)[i] = plFreqTable[i + 2]; | |
925 } | |
926 FreeLibrary(hDLL); | |
927 return S_OK; | |
928 } | |
929 | |
930 /** | |
931 * \brief tunes to given frequency through IKsPropertySet call | |
932 * | |
933 * \param pTVTuner IAMTVTuner interface of capture device | |
934 * \param lFreq frequency to tune (in Hz) | |
935 * | |
936 * \return S_OK success | |
937 * \return apropriate error code otherwise | |
938 * | |
939 * \note | |
940 * Due to either bug in driver or error in following code calll to IKsProperty::Set | |
941 * in this methods always fail with error 0x8007007a. | |
942 * | |
943 * \todo test code on other machines and an error | |
944 */ | |
945 static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq) | |
946 { | |
947 HRESULT hr; | |
948 DWORD dwSupported = 0; | |
949 DWORD cbBytes = 0; | |
950 KSPROPERTY_TUNER_MODE_CAPS_S mode_caps; | |
951 KSPROPERTY_TUNER_FREQUENCY_S frequency; | |
952 IKsPropertySet *pKSProp; | |
953 | |
954 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n"); | |
955 | |
956 memset(&mode_caps, 0, sizeof(mode_caps)); | |
957 memset(&frequency, 0, sizeof(frequency)); | |
958 | |
959 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp); | |
960 if (FAILED(hr)) | |
961 return hr; //no IKsPropertySet interface | |
962 | |
963 mode_caps.Mode = AMTUNER_MODE_TV; | |
964 hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER, | |
965 KSPROPERTY_TUNER_MODE_CAPS, &dwSupported); | |
966 if (FAILED(hr)) { | |
967 OLE_RELEASE_SAFE(pKSProp); | |
968 return hr; | |
969 } | |
970 | |
971 if (!dwSupported & KSPROPERTY_SUPPORT_GET) { | |
972 OLE_RELEASE_SAFE(pKSProp); | |
973 return E_FAIL; //PROPSETID_TINER not supported | |
974 } | |
975 | |
976 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER, | |
977 KSPROPERTY_TUNER_MODE_CAPS, | |
978 INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps), | |
979 INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps), | |
980 &mode_caps, sizeof(mode_caps), &cbBytes); | |
981 | |
982 frequency.Frequency = lFreq; | |
983 | |
984 if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES) | |
985 frequency.TuningFlags = KS_TUNER_TUNING_FINE; | |
986 else | |
987 frequency.TuningFlags = KS_TUNER_TUNING_EXACT; | |
988 | |
989 if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) { | |
990 OLE_RELEASE_SAFE(pKSProp); | |
991 return E_FAIL; | |
992 } | |
993 | |
994 hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER, | |
995 KSPROPERTY_TUNER_FREQUENCY, | |
996 INSTANCEDATA_OF_PROPERTY_PTR(&frequency), | |
997 INSTANCEDATA_OF_PROPERTY_SIZE(frequency), | |
998 &frequency, sizeof(frequency)); | |
999 if (FAILED(hr)) { | |
1000 OLE_RELEASE_SAFE(pKSProp); | |
1001 return hr; | |
1002 } | |
1003 | |
1004 OLE_RELEASE_SAFE(pKSProp); | |
1005 | |
1006 return S_OK; | |
1007 } | |
1008 | |
1009 /** | |
1010 * \brief find channel with nearest frequency and set it | |
1011 * | |
1012 * \param priv driver's private data | |
1013 * \param lFreq frequency in Hz | |
1014 * | |
1015 * \return S_OK if success | |
1016 * \return E_FAIL if error occured | |
1017 */ | |
1018 static HRESULT set_nearest_freq(priv_t * priv, long lFreq) | |
1019 { | |
1020 HRESULT hr; | |
1021 int i; | |
1022 long lFreqDiff=-1; | |
1023 int nChannel; | |
1024 TunerInputType tunerInput; | |
1025 long lInput; | |
1026 | |
1027 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called\n"); | |
1028 if(priv->freq_table_len == -1 && !priv->freq_table) { | |
1029 | |
1030 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput); | |
1031 if(FAILED(hr)){ //Falling back to 0 | |
1032 lInput=0; | |
1033 } | |
1034 | |
1035 hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput); | |
1036 | |
1037 if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME | |
1038 priv->freq_table_len=0; | |
1039 priv->freq_table=NULL; | |
1040 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableExtractFreqTable); | |
1041 return E_FAIL; | |
1042 }; | |
1043 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_FreqTableLoaded, tunerInput == TunerInputAntenna ? "broadcast" : "cable", | |
1044 chanlist2country(priv->tv_param->chanlist), priv->freq_table_len); | |
1045 } | |
1046 | |
1047 if (priv->freq_table_len <= 0) | |
1048 return E_FAIL; | |
1049 | |
1050 //FIXME: rewrite search algo | |
1051 nChannel = -1; | |
1052 for (i = 0; i < priv->freq_table_len; i++) { | |
1053 if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) { | |
1054 nChannel = priv->first_channel + i; | |
1055 lFreqDiff = labs(lFreq - priv->freq_table[i]); | |
1056 } | |
1057 } | |
1058 if (nChannel == -1) { | |
1059 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableFindNearestChannel); | |
1060 return E_FAIL; | |
1061 } | |
1062 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel, | |
1063 AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT); | |
1064 if (FAILED(hr)) { | |
1065 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableToSetChannel, (unsigned int)hr); | |
1066 return E_FAIL; | |
1067 } | |
1068 return S_OK; | |
1069 } | |
1070 | |
1071 /** | |
1072 * \brief setting frequency. decides whether use direct call/workaround | |
1073 * | |
1074 * \param priv driver's private data | |
1075 * \param lFreq frequency in Hz | |
1076 * | |
1077 * \return TVI_CONTROL_TRUE if success | |
1078 * \return TVI_CONTROL_FALSE if error occured | |
1079 * | |
1080 * \todo check for freq boundary | |
1081 */ | |
1082 static int set_frequency(priv_t * priv, long lFreq) | |
1083 { | |
1084 HRESULT hr; | |
1085 | |
1086 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called\n"); | |
1087 if (!priv->pTVTuner) | |
1088 return TVI_CONTROL_FALSE; | |
1089 if (priv->direct_setfreq_call) { //using direct call to set frequency | |
1090 hr = set_frequency_direct(priv->pTVTuner, lFreq); | |
1091 if (FAILED(hr)) { | |
1092 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DirectSetFreqFailed); | |
1093 priv->direct_setfreq_call = 0; | |
1094 } | |
1095 } | |
1096 if (!priv->direct_setfreq_call) { | |
1097 hr = set_nearest_freq(priv, lFreq); | |
1098 } | |
1099 if (FAILED(hr)) | |
1100 return TVI_CONTROL_FALSE; | |
1101 #ifdef DEPRECATED | |
1102 priv->pGrabber->ClearBuffer(priv->pGrabber); | |
1103 #endif | |
1104 return TVI_CONTROL_TRUE; | |
1105 } | |
1106 | |
1107 /** | |
1108 * \brief return current frequency from tuner (in Hz) | |
1109 * | |
1110 * \param pTVTuner IAMTVTuner interface of tuner | |
1111 * \param plFreq address of variable that receives current frequency | |
1112 * | |
1113 * \return S_OK success | |
1114 * \return E_POINTER pTVTuner==NULL || plFreq==NULL | |
1115 * \return apropriate error code otherwise | |
1116 */ | |
1117 static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq) | |
1118 { | |
1119 HRESULT hr; | |
1120 KSPROPERTY_TUNER_STATUS_S TunerStatus; | |
1121 DWORD cbBytes; | |
1122 IKsPropertySet *pKSProp; | |
1123 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n"); | |
1124 | |
1125 if (!plFreq) | |
1126 return E_POINTER; | |
1127 | |
1128 hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp); | |
1129 if (FAILED(hr)) { | |
1130 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n"); | |
1131 return hr; | |
1132 } | |
1133 | |
1134 hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER, | |
1135 KSPROPERTY_TUNER_STATUS, | |
1136 INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus), | |
1137 INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus), | |
1138 &TunerStatus, sizeof(TunerStatus), &cbBytes); | |
1139 if (FAILED(hr)) { | |
1140 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n"); | |
1141 return hr; | |
1142 } | |
1143 *plFreq = TunerStatus.CurrentFrequency; | |
1144 return S_OK; | |
1145 } | |
1146 | |
1147 /** | |
1148 * \brief gets current frequency | |
1149 * | |
1150 * \param priv driver's private data structure | |
1151 * \param plFreq - pointer to long int to store frequency to (in Hz) | |
1152 * | |
1153 * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise | |
1154 */ | |
1155 static int get_frequency(priv_t * priv, long *plFreq) | |
1156 { | |
1157 HRESULT hr; | |
1158 | |
1159 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n"); | |
1160 | |
1161 if (!plFreq || !priv->pTVTuner) | |
1162 return TVI_CONTROL_FALSE; | |
1163 | |
1164 if (priv->direct_getfreq_call) { //using direct call to get frequency | |
1165 hr = get_frequency_direct(priv->pTVTuner, plFreq); | |
1166 if (FAILED(hr)) { | |
1167 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_DirectGetFreqFailed); | |
1168 priv->direct_getfreq_call = 0; | |
1169 } | |
1170 } | |
1171 if (!priv->direct_getfreq_call) { | |
1172 hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq); | |
1173 if (FAILED(hr)) | |
1174 return TVI_CONTROL_FALSE; | |
1175 | |
1176 } | |
1177 return TVI_CONTROL_TRUE; | |
1178 } | |
1179 | |
1180 /** | |
1181 * \brief get tuner capabilities | |
1182 * | |
1183 * \param priv driver's private data | |
1184 */ | |
1185 static void get_capabilities(priv_t * priv) | |
1186 { | |
1187 long lAvailableFormats; | |
1188 HRESULT hr; | |
1189 int i; | |
1190 long lInputPins, lOutputPins, lRelated, lPhysicalType; | |
1191 IEnumPins *pEnum; | |
1192 char tmp[200]; | |
1193 IPin *pPin = 0; | |
1194 PIN_DIRECTION ThisPinDir; | |
1195 PIN_INFO pi; | |
1196 IAMAudioInputMixer *pIAMixer; | |
1197 | |
1198 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n"); | |
1199 if (priv->pTVTuner) { | |
1200 | |
1201 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_SupportedNorms); | |
1202 hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats, | |
1203 &lAvailableFormats); | |
1204 if (FAILED(hr)) | |
1205 tv_available_norms_count = 0; | |
1206 else { | |
1207 for (i = 0; i < TV_NORMS_COUNT; i++) { | |
1208 if (lAvailableFormats & tv_norms[i].index) { | |
1209 tv_available_norms[tv_available_norms_count] = i; | |
1210 mp_msg(MSGT_TV, MSGL_V, " %d=%s;", | |
1211 tv_available_norms_count + 1, tv_norms[i].name); | |
1212 tv_available_norms_count++; | |
1213 } | |
1214 } | |
1215 } | |
1216 mp_msg(MSGT_TV, MSGL_INFO, "\n"); | |
1217 } | |
1218 if (priv->pCrossbar) { | |
1219 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, | |
1220 &lInputPins); | |
1221 | |
1222 tv_available_inputs = (long *) malloc(sizeof(long) * lInputPins); | |
1223 tv_available_inputs_count = 0; | |
1224 | |
1225 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableVideoInputs); | |
1226 for (i = 0; i < lInputPins; i++) { | |
1227 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i, | |
1228 &lRelated, &lPhysicalType); | |
1229 | |
1230 if (lPhysicalType < 0x1000) { | |
1231 tv_available_inputs[tv_available_inputs_count++] = i; | |
1232 mp_msg(MSGT_TV, MSGL_V, " %d=%s;", | |
1233 tv_available_inputs_count - 1, | |
1234 physcon2str(lPhysicalType)); | |
1235 } | |
1236 } | |
1237 mp_msg(MSGT_TV, MSGL_INFO, "\n"); | |
1238 | |
1239 set_crossbar_input(priv, 0); | |
1240 } | |
1241 | |
1242 if (priv->adev_index != -1) { | |
1243 hr = OLE_CALL_ARGS(priv->pAudioFilter, EnumPins, &pEnum); | |
1244 if (FAILED(hr)) | |
1245 return; | |
1246 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableAudioInputs); | |
1247 i = 0; | |
1248 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) { | |
1249 memset(&pi, 0, sizeof(pi)); | |
1250 memset(tmp, 0, 200); | |
1251 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir); | |
1252 if (ThisPinDir == PINDIR_INPUT) { | |
1253 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi); | |
1254 wtoa(pi.achName, tmp, 200); | |
1255 OLE_RELEASE_SAFE(pi.pFilter); | |
1256 mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp); | |
1257 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin); | |
1258 hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer); | |
1259 if (SUCCEEDED(hr)) { | |
1260 if (i == priv->tv_param->audio_id) { | |
1261 OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE); | |
1262 if(priv->tv_param->volume>0) | |
1263 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume); | |
1264 #if 0 | |
1265 else | |
1266 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0); | |
1267 #endif | |
1268 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_InputSelected); | |
1269 } else { | |
1270 OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE); | |
1271 #if 0 | |
1272 OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0); | |
1273 #endif | |
1274 } | |
1275 OLE_RELEASE_SAFE(pIAMixer); | |
1276 } | |
1277 mp_msg(MSGT_TV, MSGL_V, ";"); | |
1278 OLE_RELEASE_SAFE(pPin); | |
1279 i++; | |
1280 } | |
1281 } | |
1282 mp_msg(MSGT_TV, MSGL_INFO, "\n"); | |
1283 OLE_RELEASE_SAFE(pEnum); | |
1284 } | |
1285 } | |
1286 | |
1287 /* | |
1288 *--------------------------------------------------------------------------------------- | |
1289 * | |
1290 * Filter related methods | |
1291 * | |
1292 *--------------------------------------------------------------------------------------- | |
1293 */ | |
1294 /** | |
1295 * \brief building in graph audio/video capture chain | |
1296 * | |
1297 * \param priv driver's private data | |
1298 * \param pCaptureFilter pointer to capture device's IBaseFilter interface | |
1299 * \param pbuf ringbuffer data structure | |
1300 * \param pmt media type for chain (AM_MEDIA_TYPE) | |
1301 * | |
1302 * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure | |
1303 */ | |
1304 static HRESULT build_sub_graph(priv_t * priv, IBaseFilter * pCaptureFilter, | |
1305 grabber_ringbuffer_t * pbuf, | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1306 AM_MEDIA_TYPE ** arpmt, |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1307 AM_MEDIA_TYPE* pmt, const GUID* ppin_category) |
24744 | 1308 { |
1309 HRESULT hr; | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1310 int nFormatProbed = 0; |
25029
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1311 |
24744 | 1312 IPin *pSGIn; |
1313 IPin *pSGOut; | |
1314 IPin *pNRIn=NULL; | |
1315 IPin *pCapturePin; | |
1316 | |
1317 IBaseFilter *pNR = NULL; | |
1318 IBaseFilter *pSGF = NULL; | |
1319 | |
1320 ISampleGrabber *pSG = NULL; | |
1321 | |
1322 hr=S_OK; | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1323 |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1324 //No supported formats |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1325 if(!arpmt[0]) |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1326 return E_FAIL; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1327 |
24744 | 1328 do{ |
1329 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, | |
1330 (IUnknown *) pCaptureFilter, | |
1331 PINDIR_OUTPUT, ppin_category, | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1332 &(arpmt[nFormatProbed]->majortype), FALSE, 0, &pCapturePin); |
24744 | 1333 if(FAILED(hr)){ |
1334 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr); | |
1335 break; | |
1336 } | |
1337 /* Addinf SampleGrabber filter for video stream */ | |
1338 hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pSGF); | |
1339 if(FAILED(hr)){ | |
1340 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); | |
1341 break; | |
1342 } | |
1343 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pSGF, L"Sample Grabber"); | |
1344 if(FAILED(hr)){ | |
1345 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); | |
1346 break; | |
1347 } | |
1348 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pSGIn); | |
1349 if(FAILED(hr)){ | |
1350 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr); | |
1351 break; | |
1352 } | |
1353 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut); | |
1354 if(FAILED(hr)){ | |
1355 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr); | |
1356 break; | |
1357 } | |
1358 | |
1359 /* creating ringbuffer for video samples */ | |
1360 priv->pCSGCB = CSampleGrabberCB_Create(pbuf); | |
1361 if(!priv->pCSGCB){ | |
1362 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY); | |
1363 break; | |
1364 } | |
1365 | |
1366 /* initializing SampleGrabber filter */ | |
1367 hr = OLE_QUERYINTERFACE(pSGF, IID_ISampleGrabber, pSG); | |
1368 if(FAILED(hr)){ | |
1369 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); | |
1370 break; | |
1371 } | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1372 hr = OLE_CALL_ARGS(pSG, SetMediaType, arpmt[nFormatProbed]); //set desired mediatype |
24744 | 1373 if(FAILED(hr)){ |
1374 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr); | |
1375 break; | |
1376 } | |
1377 // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data | |
1378 hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) priv->pCSGCB, 0); //we want to receive sample | |
1379 | |
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; | |
1383 } | |
1384 hr = OLE_CALL_ARGS(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; | |
1388 } | |
1389 hr = OLE_CALL_ARGS(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; | |
1393 } | |
1394 OLE_RELEASE_SAFE(pSG); | |
1395 | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1396 if(priv->tv_param->normalize_audio_chunks && !memcmp(&(arpmt[nFormatProbed]->majortype),&(MEDIATYPE_Audio),16)){ |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1397 set_buffer_preference(20,(WAVEFORMATEX*)(arpmt[nFormatProbed]->pbFormat),pCapturePin,pSGIn); |
25048 | 1398 } |
24744 | 1399 |
1400 /* connecting filters together: VideoCapture --> SampleGrabber */ | |
1401 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pCapturePin, pSGIn); | |
1402 if(FAILED(hr)){ | |
1403 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr); | |
1404 break; | |
1405 } | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1406 |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1407 hr = OLE_CALL_ARGS(pCapturePin, ConnectionMediaType, pmt); |
25029
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1408 if(FAILED(hr)) |
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1409 { |
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1410 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_GetActualMediatypeFailed, (unsigned int)hr); |
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1411 } |
24744 | 1412 |
1413 if(priv->tv_param->hidden_video_renderer){ | |
1414 IEnumFilters* pEnum; | |
1415 IBaseFilter* pFilter; | |
1416 | |
1417 hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)pCapturePin,NULL,NULL); | |
1418 | |
1419 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum); | |
1420 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) { | |
1421 LPVIDEOWINDOW pVideoWindow; | |
1422 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow); | |
1423 if (SUCCEEDED(hr)) | |
1424 { | |
1425 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0); | |
1426 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0); | |
1427 OLE_RELEASE_SAFE(pVideoWindow); | |
1428 } | |
1429 OLE_RELEASE_SAFE(pFilter); | |
1430 } | |
1431 OLE_RELEASE_SAFE(pEnum); | |
1432 }else | |
1433 { | |
25052
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1434 #if 0 |
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1435 /* |
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1436 Code below is disabled, because terminating chain with NullRenderer leads to jerky video. |
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1437 Perhaps, this happens because NullRenderer filter discards each received |
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1438 frame while discarded frames causes live source filter to dramatically reduce frame rate. |
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1439 */ |
24744 | 1440 /* adding sink for video stream */ |
1441 hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR); | |
1442 if(FAILED(hr)){ | |
1443 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr); | |
1444 break; | |
1445 } | |
1446 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer"); | |
1447 if(FAILED(hr)){ | |
1448 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr); | |
1449 break; | |
1450 } | |
1451 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn); | |
1452 if(FAILED(hr)){ | |
1453 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr); | |
1454 break; | |
1455 } | |
1456 /* | |
1457 Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection | |
1458 */ | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
1459 if(memcmp(&(arpmt[nFormatProbed]->majortype),&MEDIATYPE_VBI,16)){ |
24744 | 1460 /* connecting filters together: SampleGrabber --> NullRenderer */ |
1461 hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn); | |
1462 if(FAILED(hr)){ | |
1463 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr); | |
1464 break; | |
1465 } | |
1466 } | |
25052
7b2b17b57cf7
Disable terminating directshow chains with NullRenderer filter,
voroshil
parents:
25051
diff
changeset
|
1467 #endif |
24744 | 1468 } |
1469 | |
1470 hr = S_OK; | |
1471 } while(0); | |
25029
c9f20e41bc13
Make sure that mplayer will receive actual media type
voroshil
parents:
25028
diff
changeset
|
1472 |
24744 | 1473 OLE_RELEASE_SAFE(pSGF); |
1474 OLE_RELEASE_SAFE(pSGIn); | |
1475 OLE_RELEASE_SAFE(pSGOut); | |
1476 OLE_RELEASE_SAFE(pNR); | |
1477 OLE_RELEASE_SAFE(pNRIn); | |
1478 OLE_RELEASE_SAFE(pCapturePin); | |
1479 | |
1480 return hr; | |
1481 } | |
1482 | |
1483 /** | |
1484 * \brief configures crossbar for grabbing video stream from given input | |
1485 * | |
1486 * \param priv driver's private data | |
1487 * \param input index of available video input to get data from | |
1488 * | |
1489 * \return TVI_CONTROL_TRUE success | |
1490 * \return TVI_CONTROL_FALSE error | |
1491 */ | |
1492 static int set_crossbar_input(priv_t * priv, int input) | |
1493 { | |
1494 HRESULT hr; | |
1495 int i, nVideoDecoder, nAudioDecoder; | |
1496 long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins, | |
1497 lInputPins; | |
1498 | |
1499 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n"); | |
1500 if (!priv->pCrossbar || input < 0 | |
1501 || input >= tv_available_inputs_count) | |
1502 return TVI_CONTROL_FALSE; | |
1503 | |
1504 OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins); | |
1505 | |
1506 lInput = tv_available_inputs[input]; | |
1507 | |
1508 if (lInput < 0 || lInput >= lInputPins) | |
1509 return TVI_CONTROL_FALSE; | |
1510 | |
1511 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput, | |
1512 &lInputRelated, &lPhysicalType); | |
1513 | |
1514 nVideoDecoder = nAudioDecoder = -1; | |
1515 for (i = 0; i < lOutputPins; i++) { | |
1516 OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i, | |
1517 &lRelated, &lPhysicalType); | |
1518 if (lPhysicalType == PhysConn_Video_VideoDecoder) | |
1519 nVideoDecoder = i; | |
1520 if (lPhysicalType == PhysConn_Audio_AudioDecoder) | |
1521 nAudioDecoder = i; | |
1522 } | |
1523 if (nVideoDecoder >= 0) { | |
1524 //connecting given input with video decoder | |
1525 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput); | |
1526 if (hr != S_OK) { | |
1527 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputVideoDecoder, (unsigned int)hr); | |
1528 return TVI_CONTROL_FALSE; | |
1529 } | |
1530 } | |
1531 if (nAudioDecoder >= 0 && lInputRelated >= 0) { | |
1532 hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder, | |
1533 lInputRelated); | |
1534 if (hr != S_OK) { | |
1535 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputAudioDecoder, (unsigned int)hr); | |
1536 return TVI_CONTROL_FALSE; | |
1537 } | |
1538 } | |
1539 return TVI_CONTROL_TRUE; | |
1540 } | |
1541 | |
1542 /** | |
1543 * \brief adjusts video control (hue,saturation,contrast,brightess) | |
1544 * | |
1545 * \param priv driver's private data | |
1546 * \param control which control to adjust | |
1547 * \param value new value for control (0-100) | |
1548 * | |
1549 * \return TVI_CONTROL_TRUE success | |
1550 * \return TVI_CONTROL_FALSE error | |
1551 */ | |
1552 static int set_control(priv_t * priv, int control, int value) | |
1553 { | |
1554 long lMin, lMax, lStepping, lDefault, lFlags, lValue; | |
1555 HRESULT hr; | |
1556 | |
1557 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n"); | |
1558 if (value < -100 || value > 100 || !priv->pVideoProcAmp) | |
1559 return TVI_CONTROL_FALSE; | |
1560 | |
1561 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control, | |
1562 &lMin, &lMax, &lStepping, &lDefault, &lFlags); | |
1563 if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual) | |
1564 return TVI_CONTROL_FALSE; | |
1565 | |
1566 lValue = lMin + (value + 100) * (lMax - lMin) / 200; | |
1567 /* | |
1568 Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256 | |
1569 */ | |
1570 if (lStepping > lMax) { | |
1571 mp_msg(MSGT_TV, MSGL_DBG3, | |
1572 "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n", | |
1573 lStepping, lMax,control); | |
1574 lStepping = 1; | |
1575 } | |
1576 lValue -= lValue % lStepping; | |
1577 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue, | |
1578 VideoProcAmp_Flags_Manual); | |
1579 if (FAILED(hr)) | |
1580 return TVI_CONTROL_FALSE; | |
1581 | |
1582 return TVI_CONTROL_TRUE; | |
1583 } | |
1584 | |
1585 /** | |
1586 * \brief get current value of video control (hue,saturation,contrast,brightess) | |
1587 * | |
1588 * \param priv driver's private data | |
1589 * \param control which control to adjust | |
1590 * \param pvalue address of variable thar receives current value | |
1591 * | |
1592 * \return TVI_CONTROL_TRUE success | |
1593 * \return TVI_CONTROL_FALSE error | |
1594 */ | |
1595 static int get_control(priv_t * priv, int control, int *pvalue) | |
1596 { | |
1597 long lMin, lMax, lStepping, lDefault, lFlags, lValue; | |
1598 HRESULT hr; | |
1599 | |
1600 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n"); | |
1601 if (!pvalue || !priv->pVideoProcAmp) | |
1602 return TVI_CONTROL_FALSE; | |
1603 | |
1604 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control, | |
1605 &lMin, &lMax, &lStepping, &lDefault, &lFlags); | |
1606 if (FAILED(hr)) | |
1607 return TVI_CONTROL_FALSE; | |
1608 if (lMin == lMax) { | |
1609 *pvalue = lMin; | |
1610 return TVI_CONTROL_TRUE; | |
1611 } | |
1612 | |
1613 hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags); | |
1614 if (FAILED(hr)) | |
1615 return TVI_CONTROL_FALSE; | |
1616 | |
1617 *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100; | |
1618 | |
1619 return TVI_CONTROL_TRUE; | |
1620 } | |
1621 | |
1622 /** | |
25053
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1623 * \brief create AM_MEDIA_TYPE structure, corresponding to given FourCC code and width/height/fps |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1624 * \param fcc FourCC code for video format |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1625 * \param width picture width |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1626 * \param height pciture height |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1627 * \param fps frames per second (required for bitrate calculation) |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1628 * |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1629 * \return pointer to AM_MEDIA_TYPE structure if success, NULL - otherwise |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1630 */ |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1631 static AM_MEDIA_TYPE* create_video_format(int fcc, int width, int height, int fps) |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1632 { |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1633 int i; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1634 AM_MEDIA_TYPE mt; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1635 VIDEOINFOHEADER vHdr; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1636 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1637 /* Check given fcc in lookup table*/ |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1638 for(i=0; img_fmt_list[i].fmt && img_fmt_list[i].fmt!=fcc; i++) /* NOTHING */; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1639 if(!img_fmt_list[i].fmt) |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1640 return NULL; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1641 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1642 memset(&mt, 0, sizeof(AM_MEDIA_TYPE)); |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1643 memset(&vHdr, 0, sizeof(VIDEOINFOHEADER)); |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1644 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1645 vHdr.bmiHeader.biSize = sizeof(vHdr.bmiHeader); |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1646 vHdr.bmiHeader.biWidth = width; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1647 vHdr.bmiHeader.biHeight = height; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1648 //FIXME: is biPlanes required too? |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1649 //vHdr.bmiHeader.biPlanes = img_fmt_list[i].nPlanes; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1650 vHdr.bmiHeader.biBitCount = img_fmt_list[i].nBits; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1651 vHdr.bmiHeader.biCompression = img_fmt_list[i].nCompression; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1652 vHdr.bmiHeader.biSizeImage = width * height * img_fmt_list[i].nBits / 8; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1653 vHdr.dwBitRate = vHdr.bmiHeader.biSizeImage * 8 * fps; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1654 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1655 mt.pbFormat = (char*)&vHdr; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1656 mt.cbFormat = sizeof(vHdr); |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1657 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1658 mt.majortype = MEDIATYPE_Video; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1659 mt.subtype = *img_fmt_list[i].subtype; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1660 mt.formattype = FORMAT_VideoInfo; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1661 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1662 mt.bFixedSizeSamples = 1; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1663 mt.bTemporalCompression = 0; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1664 mt.lSampleSize = vHdr.bmiHeader.biSizeImage; |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1665 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1666 return CreateMediaType(&mt); |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1667 } |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1668 |
086c2accaaa2
Service routine for constructing AM_MEDIA_TYPE structure from
voroshil
parents:
25052
diff
changeset
|
1669 /** |
24744 | 1670 * \brief extracts fcc,width,height from AM_MEDIA_TYPE |
1671 * | |
1672 * \param pmt pointer to AM_MEDIA_TYPE to extract data from | |
1673 * \param pfcc address of variable that receives FourCC | |
1674 * \param pwidth address of variable that receives width | |
1675 * \param pheight address of variable that recevies height | |
1676 * | |
1677 * \return 1 if data extracted successfully, 0 - otherwise | |
1678 */ | |
1679 static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc, | |
1680 int *pwidth, int *pheight) | |
1681 { | |
1682 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n"); | |
1683 if (!pmt) | |
1684 return 0; | |
1685 if (!pmt->pbFormat) | |
1686 return 0; | |
1687 if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0) | |
1688 return 0; | |
1689 if (pfcc) | |
1690 *pfcc = subtype2imgfmt(&(pmt->subtype)); | |
1691 if (pwidth) | |
1692 *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth; | |
1693 if (pheight) | |
1694 *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight; | |
1695 return 1; | |
1696 } | |
1697 | |
1698 /** | |
1699 * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE | |
1700 * | |
1701 * \param pmt pointer to AM_MEDIA_TYPE to extract data from | |
1702 * \param pfcc address of variable that receives samplerate | |
1703 * \param pwidth address of variable that receives number of bits per sample | |
1704 * \param pheight address of variable that recevies number of channels | |
1705 * | |
1706 * \return 1 if data extracted successfully, 0 - otherwise | |
1707 */ | |
1708 static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate, | |
1709 int *pbits, int *pchannels) | |
1710 { | |
1711 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n"); | |
1712 if (!pmt) | |
1713 return 0; | |
1714 if (!pmt->pbFormat) | |
1715 return 0; | |
1716 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0) | |
1717 return 0; | |
1718 if (psamplerate) | |
1719 *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec; | |
1720 if (pbits) | |
1721 *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample; | |
1722 if (pchannels) | |
1723 *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels; | |
1724 return 1; | |
1725 } | |
1726 | |
1727 /** | |
1728 * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels | |
1729 * | |
1730 * \param pmt pointer to AM_MEDIA_TYPE for check | |
1731 * \param samplerate audio samplerate | |
1732 * \param bits bits per sample | |
1733 * \param channels number of audio channels | |
1734 * | |
1735 * \return 1 if AM_MEDIA_TYPE compatible | |
1736 * \return 0 if not | |
1737 */ | |
1738 static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate, | |
1739 int bits, int channels) | |
1740 { | |
1741 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n"); | |
1742 if (!pmt) | |
1743 return 0; | |
1744 if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0) | |
1745 return 0; | |
1746 if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0) | |
1747 return 0; | |
1748 if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0) | |
1749 return 0; | |
1750 if (!pmt->pbFormat) | |
1751 return 0; | |
1752 if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate) | |
1753 return 0; | |
1754 if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits) | |
1755 return 0; | |
1756 if (channels > 0 | |
1757 && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels) | |
1758 return 0; | |
1759 | |
1760 return 1; | |
1761 } | |
1762 | |
1763 /** | |
1764 * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height | |
1765 * | |
1766 * \param pmt pointer to AM_MEDIA_TYPE for check | |
1767 * \param fcc FourCC (compression) | |
1768 * \param width width of picture | |
1769 * \param height height of picture | |
1770 * | |
1771 * \return 1 if AM_MEDIA_TYPE compatible | |
1772 & \return 0 if not | |
1773 * | |
1774 * \note | |
1775 * width and height are currently not used | |
1776 * | |
1777 * \todo | |
1778 * add width/height check | |
1779 */ | |
1780 static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width, | |
1781 int height) | |
1782 { | |
1783 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n"); | |
1784 if (!pmt) | |
1785 return 0; | |
1786 if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0) | |
1787 return 0; | |
1788 if (subtype2imgfmt(&(pmt->subtype)) != fcc) | |
1789 return 0; | |
1790 return 1; | |
1791 } | |
1792 | |
1793 /** | |
1794 * \brief converts DirectShow subtype to MPlayer's IMGFMT | |
1795 * | |
1796 * \param subtype DirectShow subtype for video format | |
1797 * | |
1798 * \return MPlayer's IMGFMT or 0 if error occured | |
1799 */ | |
1800 static int subtype2imgfmt(const GUID * subtype) | |
1801 { | |
1802 int i; | |
1803 for (i = 0; img_fmt_list[i].fmt; i++) { | |
1804 if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0) | |
1805 return img_fmt_list[i].fmt; | |
1806 } | |
1807 return 0; | |
1808 } | |
1809 | |
1810 /** | |
1811 * \brief prints filter name and it pins | |
1812 * | |
1813 * \param pFilter - IBaseFilter to get data from | |
1814 * | |
1815 * \return S_OK if success, error code otherwise | |
1816 */ | |
1817 static HRESULT show_filter_info(IBaseFilter * pFilter) | |
1818 { | |
1819 char tmp[200]; | |
1820 FILTER_INFO fi; | |
1821 LPENUMPINS pEnum = 0; | |
1822 IPin *pPin = 0; | |
1823 PIN_DIRECTION ThisPinDir; | |
1824 PIN_INFO pi; | |
1825 HRESULT hr; | |
1826 int i; | |
1827 | |
1828 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n"); | |
1829 memset(&fi, 0, sizeof(fi)); | |
1830 memset(tmp, 0, 200); | |
1831 | |
1832 OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi); | |
1833 OLE_RELEASE_SAFE(fi.pGraph); | |
1834 wtoa(fi.achName, tmp, 200); | |
1835 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:", | |
1836 pFilter, tmp, fi.pGraph); | |
1837 hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum); | |
1838 if (FAILED(hr)) | |
1839 return hr; | |
1840 i = 0; | |
1841 while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) { | |
1842 memset(&pi, 0, sizeof(pi)); | |
1843 memset(tmp, 0, 200); | |
1844 OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir); | |
1845 if (ThisPinDir == PINDIR_OUTPUT) { | |
1846 OLE_CALL_ARGS(pPin, QueryPinInfo, &pi); | |
1847 wtoa(pi.achName, tmp, 200); | |
1848 OLE_RELEASE_SAFE(pi.pFilter); | |
1849 mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp); | |
1850 mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin); | |
1851 mp_msg(MSGT_TV, MSGL_DBG2, ";"); | |
1852 OLE_RELEASE_SAFE(pPin); | |
1853 i++; | |
1854 } | |
1855 } | |
1856 mp_msg(MSGT_TV, MSGL_DBG2, "\n"); | |
1857 OLE_RELEASE_SAFE(pEnum); | |
1858 return S_OK; | |
1859 } | |
1860 | |
1861 /** | |
1862 * \brief gets device's frendly in ANSI encoding | |
1863 * | |
1864 * \param pM IMoniker interface, got in enumeration process | |
1865 * \param category device category | |
1866 * | |
1867 * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise | |
1868 */ | |
1869 static int get_device_name(IMoniker * pM, char *pBuf, int nLen) | |
1870 { | |
1871 HRESULT hr; | |
1872 VARIANT var; | |
1873 IPropertyBag *pPropBag; | |
1874 hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag); | |
1875 if (FAILED(hr)) { | |
1876 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n"); | |
1877 return TVI_CONTROL_FALSE; | |
1878 } | |
1879 var.vt = VT_BSTR; | |
1880 hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var, | |
1881 NULL); | |
1882 if (FAILED(hr)) { | |
1883 hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var, | |
1884 NULL); | |
1885 } | |
1886 OLE_RELEASE_SAFE(pPropBag); | |
1887 if (SUCCEEDED(hr)) { | |
1888 wtoa(var.bstrVal, pBuf, nLen); | |
1889 return TVI_CONTROL_TRUE; | |
1890 } | |
1891 return TVI_CONTROL_FALSE; | |
1892 } | |
1893 | |
1894 /** | |
1895 * \brief find capture device at given index | |
1896 * | |
1897 * \param index device index to search for (-1 mean only print available) | |
1898 * \param category device category | |
1899 * | |
1900 * \return IBaseFilter interface for capture device with given index | |
1901 * | |
1902 * Sample values for category: | |
1903 * CLSID_VideoInputDeviceCategory - Video Capture Sources | |
1904 * CLSID_AudioInputDeviceCategory - Audio Capture Sources | |
1905 * See DirectShow SDK documentation for other possible values | |
1906 */ | |
1907 static IBaseFilter *find_capture_device(int index, REFCLSID category) | |
1908 { | |
1909 IBaseFilter *pFilter = NULL; | |
1910 ICreateDevEnum *pDevEnum = NULL; | |
1911 IEnumMoniker *pClassEnum = NULL; | |
1912 IMoniker *pM; | |
1913 HRESULT hr; | |
1914 ULONG cFetched; | |
1915 int i; | |
1916 char tmp[DEVICE_NAME_MAX_LEN + 1]; | |
1917 hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL, | |
1918 CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, | |
1919 (void *) &pDevEnum); | |
1920 if (FAILED(hr)) { | |
1921 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n"); | |
1922 return NULL; | |
1923 } | |
1924 | |
1925 hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0); | |
1926 OLE_RELEASE_SAFE(pDevEnum); | |
1927 if (FAILED(hr)) { | |
1928 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n"); | |
1929 return NULL; | |
1930 } | |
1931 if (hr == S_FALSE) { | |
1932 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n"); | |
1933 return NULL; | |
1934 } | |
1935 | |
1936 OLE_CALL(pClassEnum,Reset); | |
1937 for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) { | |
1938 if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE) | |
1939 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableGetDeviceName, i); | |
1940 else | |
1941 mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DeviceName, i, tmp); | |
1942 if (index != -1 && i == index) { | |
1943 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_UsingDevice, index, tmp); | |
1944 hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter); | |
1945 if (FAILED(hr)) | |
1946 pFilter = NULL; | |
1947 } | |
1948 OLE_RELEASE_SAFE(pM); | |
1949 } | |
1950 if (index != -1 && !pFilter) { | |
1951 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_DeviceNotFound, | |
1952 index); | |
1953 } | |
1954 OLE_RELEASE_SAFE(pClassEnum); | |
1955 | |
1956 return pFilter; | |
1957 } | |
1958 | |
1959 /** | |
1960 * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps | |
1961 * | |
1962 * \param[in] pVideoStreamConfig IAMStreamConfig of used capture device's output pin | |
1963 * \param[in] pMediaType MEDIATYPE_Video or MEDIATYPE_Audio | |
1964 * \param[out] parpmtMedia address of variable that receives pointer to array of AM_MEDIA_TYPE structures | |
1965 * \param[out] parCaps address of variable thar receives pointer to array of VIDEOSTREAM_CONFIG_CAPS or AUDIO_STREAM_CONFIG_CAPS | |
1966 * | |
1967 * \return S_OK success | |
1968 * \return E_POINTER one of parameters is NULL | |
1969 * \return E_FAIL required size of buffer is unknown for given media type | |
1970 * \return E_OUTOFMEMORY not enough memory | |
1971 * \return other error code from called methods | |
1972 * | |
1973 * \remarks | |
1974 * last item or returned arrays will be NULL | |
1975 */ | |
1976 static HRESULT get_available_formats_stream(IAMStreamConfig * | |
1977 pStreamConfig, | |
1978 const GUID * pMediaType, | |
1979 AM_MEDIA_TYPE *** parpmtMedia, | |
1980 void ***parCaps) | |
1981 { | |
1982 AM_MEDIA_TYPE **arpmt; | |
1983 void **pBuf=NULL; | |
1984 | |
1985 HRESULT hr; | |
1986 int i, count, size; | |
1987 int done; | |
1988 | |
1989 mp_msg(MSGT_TV, MSGL_DBG4, | |
1990 "tvi_dshow: get_available_formats_stream called\n"); | |
1991 | |
1992 if (!pStreamConfig || !pMediaType || !parpmtMedia || !parCaps) | |
1993 return E_POINTER; | |
1994 | |
1995 hr=OLE_CALL_ARGS(pStreamConfig, GetNumberOfCapabilities, &count, &size); | |
1996 if (FAILED(hr)) { | |
1997 mp_msg(MSGT_TV, MSGL_DBG4, | |
1998 "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n"); | |
1999 return hr; | |
2000 } | |
2001 if (memcmp(pMediaType, &MEDIATYPE_Video, 16) == 0){ | |
2002 if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) { | |
2003 mp_msg(MSGT_TV, MSGL_DBG4, | |
2004 "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n"); | |
2005 return E_FAIL; | |
2006 } else if (memcmp(pMediaType, &MEDIATYPE_Audio, 16) == 0){ | |
2007 if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) { | |
2008 mp_msg(MSGT_TV, MSGL_DBG4, | |
2009 "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n"); | |
2010 return E_FAIL; | |
2011 } else { | |
2012 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_stream"); | |
2013 return E_FAIL; | |
2014 } | |
2015 } | |
2016 } | |
2017 done = 0; | |
2018 | |
2019 arpmt = (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *)); | |
2020 if (arpmt) { | |
2021 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *)); | |
2022 | |
2023 pBuf = (void **) malloc((count + 1) * sizeof(void *)); | |
2024 if (pBuf) { | |
25061
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2025 int dst = 0; |
24744 | 2026 memset(pBuf, 0, (count + 1) * sizeof(void *)); |
2027 | |
2028 for (i = 0; i < count; i++) { | |
25061
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2029 pBuf[dst] = malloc(size); |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2030 |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2031 if (!pBuf[dst]) |
24744 | 2032 break; |
2033 | |
2034 hr = OLE_CALL_ARGS(pStreamConfig, GetStreamCaps, i, | |
25061
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2035 &(arpmt[dst]), pBuf[dst]); |
24744 | 2036 if (FAILED(hr)) |
2037 break; | |
25061
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2038 if(!memcmp(&(arpmt[dst]->majortype), &MEDIATYPE_Video, 16) && !subtype2imgfmt(&(arpmt[dst]->subtype))) |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2039 { |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2040 DisplayMediaType("Skipping unsupported video format", arpmt[dst]); |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2041 DeleteMediaType(arpmt[dst]); |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2042 free(pBuf[dst]); |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2043 arpmt[dst]=NULL; |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2044 pBuf[dst]=NULL; |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2045 continue; |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2046 } |
1f0eb7aa3206
Ignore video formats which are supported by device
voroshil
parents:
25060
diff
changeset
|
2047 dst++; |
24744 | 2048 } |
2049 if (i == count) { | |
2050 *parpmtMedia = arpmt; | |
2051 *parCaps = pBuf; | |
2052 done = 1; | |
2053 } | |
2054 } | |
2055 } | |
2056 if (!done) { | |
2057 for (i = 0; i < count; i++) { | |
2058 if (pBuf && pBuf[i]) | |
2059 free(pBuf[i]); | |
2060 if (arpmt && arpmt[i]) | |
2061 DeleteMediaType(arpmt[i]); | |
2062 } | |
2063 if (pBuf) | |
2064 free(pBuf); | |
2065 if (arpmt) | |
2066 free(arpmt); | |
2067 if (hr != S_OK) { | |
2068 mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n"); | |
2069 return hr; | |
2070 } else | |
2071 return E_OUTOFMEMORY; | |
2072 } | |
2073 return S_OK; | |
2074 } | |
2075 | |
2076 /** | |
2077 * \brief returns allocates an array and store available media formats for given pin type to it | |
2078 * | |
2079 * \param pBuilder ICaptureGraphBuilder2 interface of graph builder | |
2080 * \param pFilter IBaseFilter interface of capture device | |
2081 * \param pMediaType media type for search (MEDIATYPE_Video or MEDIATYPE_Audio) | |
2082 * \param[out] parpmtMediaType address of variable that receives pointer to array | |
2083 * | |
2084 * \return S_OK success | |
2085 * \return E_POINTER one of given pointers is null | |
2086 * \return apropriate error code otherwise | |
2087 */ | |
2088 static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder, | |
2089 IBaseFilter * pFilter, | |
2090 const GUID * pMediaType, | |
2091 AM_MEDIA_TYPE *** parpmtMedia, | |
2092 void ***parCaps) | |
2093 { | |
2094 IEnumMediaTypes *pEnum; | |
2095 IPin *pPin; | |
2096 int i, count, size; | |
2097 ULONG cFetched; | |
2098 AM_MEDIA_TYPE *pmt; | |
2099 HRESULT hr; | |
2100 void **pBuf; | |
2101 AM_MEDIA_TYPE **arpmt; //This will be real array | |
2102 int isvideo; | |
2103 VIDEO_STREAM_CONFIG_CAPS *pVideoCaps; | |
2104 AUDIO_STREAM_CONFIG_CAPS *pAudioCaps; | |
2105 int p1, p2, p3; | |
2106 | |
2107 mp_msg(MSGT_TV, MSGL_DBG4, | |
2108 "tvi_dshow: get_available_formats_pin called\n"); | |
2109 if (!pBuilder || !pFilter || !pMediaType || !parpmtMedia) | |
2110 return E_POINTER; | |
2111 | |
2112 hr = OLE_CALL_ARGS(pBuilder, FindPin, (IUnknown *) pFilter, | |
2113 PINDIR_OUTPUT, NULL, pMediaType, FALSE, 0, &pPin); | |
2114 if (FAILED(hr)) { | |
2115 mp_msg(MSGT_TV, MSGL_DBG4, | |
2116 "tvi_dshow: Call to FindPin failed (get_available_formats_pin)\n"); | |
2117 return hr; | |
2118 } | |
2119 if (memcmp(pMediaType, &MEDIATYPE_Video, 16) == 0) { | |
2120 isvideo = 1; | |
2121 size = sizeof(VIDEO_STREAM_CONFIG_CAPS); | |
2122 } else if (memcmp(pMediaType, &MEDIATYPE_Audio, 16) == 0) { | |
2123 isvideo = 0; | |
2124 size = sizeof(AUDIO_STREAM_CONFIG_CAPS); | |
2125 } else { | |
2126 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_pin"); | |
2127 return E_FAIL; | |
2128 } | |
2129 | |
2130 hr = OLE_CALL_ARGS(pPin, EnumMediaTypes, &pEnum); | |
2131 OLE_RELEASE_SAFE(pPin); | |
2132 if (FAILED(hr)) { | |
2133 mp_msg(MSGT_TV, MSGL_DBG4, | |
2134 "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n"); | |
2135 return hr; | |
2136 } | |
2137 for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) { | |
2138 if (!pmt) | |
2139 break; | |
2140 } | |
2141 OLE_CALL(pEnum,Reset); | |
2142 | |
2143 count = i; | |
2144 arpmt = | |
2145 (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *)); | |
2146 if (!arpmt) | |
2147 return E_OUTOFMEMORY; | |
2148 memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *)); | |
2149 | |
2150 for (i = 0; | |
2151 i < count | |
2152 && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK; | |
2153 i++); | |
2154 | |
2155 OLE_RELEASE_SAFE(pEnum); | |
2156 | |
2157 | |
2158 pBuf = (void **) malloc((count + 1) * sizeof(void *)); | |
2159 if (!pBuf) { | |
2160 for (i = 0; i < count; i++) | |
2161 if (arpmt[i]) | |
2162 DeleteMediaType(arpmt[i]); | |
2163 free(arpmt); | |
2164 return E_OUTOFMEMORY; | |
2165 } | |
2166 memset(pBuf, 0, (count + 1) * sizeof(void *)); | |
2167 | |
2168 for (i = 0; i < count; i++) { | |
2169 pBuf[i] = malloc(size); | |
2170 if (!pBuf[i]) | |
2171 break; | |
2172 memset(pBuf[i], 0, size); | |
2173 | |
2174 if (isvideo) { | |
2175 pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i]; | |
2176 extract_video_format(arpmt[i], NULL, &p1, &p2); | |
2177 pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx = | |
2178 p1; | |
2179 pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy = | |
2180 p2; | |
2181 } else { | |
2182 pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i]; | |
2183 extract_audio_format(arpmt[i], &p1, &p2, &p3); | |
2184 pAudioCaps->MaximumSampleFrequency = | |
2185 pAudioCaps->MinimumSampleFrequency = p1; | |
2186 pAudioCaps->MaximumBitsPerSample = | |
2187 pAudioCaps->MinimumBitsPerSample = p2; | |
2188 pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3; | |
2189 } | |
2190 | |
2191 } | |
2192 if (i != count) { | |
2193 for (i = 0; i < count; i++) { | |
2194 if (arpmt[i]) | |
2195 DeleteMediaType(arpmt[i]); | |
2196 if (pBuf[i]) | |
2197 free(pBuf[i]); | |
2198 } | |
2199 free(arpmt); | |
2200 free(pBuf); | |
2201 return E_OUTOFMEMORY; | |
2202 } | |
2203 *parpmtMedia = arpmt; | |
2204 *parCaps = pBuf; | |
2205 | |
2206 return S_OK; | |
2207 } | |
2208 | |
2209 /* | |
2210 *--------------------------------------------------------------------------------------- | |
2211 * | |
2212 * Public methods | |
2213 * | |
2214 *--------------------------------------------------------------------------------------- | |
2215 */ | |
2216 /** | |
2217 * \brief fills given buffer with audio data (usually one block) | |
2218 * | |
2219 * \param priv driver's private data structure | |
2220 * \param buffer buffer to store data to | |
2221 * \param len buffer's size in bytes (usually one block size) | |
2222 * | |
2223 * \return audio pts if audio present, 1 - otherwise | |
2224 */ | |
2225 static double grab_audio_frame(priv_t * priv, char *buffer, int len) | |
2226 { | |
2227 int bytes = 0; | |
2228 int i; | |
2229 double pts; | |
2230 grabber_ringbuffer_t *rb = priv->a_buf; | |
2231 grabber_ringbuffer_t *vrb = priv->v_buf; | |
2232 | |
2233 if (!rb || !rb->ringbuffer) | |
2234 return 1; | |
2235 | |
2236 if(vrb && vrb->tStart<0){ | |
2237 memset(buffer,0,len); | |
2238 return 0; | |
2239 } | |
2240 if(vrb && rb->tStart<0) | |
2241 rb->tStart=vrb->tStart; | |
2242 | |
2243 if (len < rb->blocksize) | |
2244 bytes = len; | |
2245 else | |
2246 bytes = rb->blocksize; | |
2247 | |
2248 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n", | |
2249 rb->count, len); | |
2250 if(!rb->count){ | |
2251 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n"); | |
2252 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000); | |
2253 if(!rb->count){ | |
2254 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n"); | |
2255 return 0; | |
2256 } | |
2257 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n"); | |
2258 } | |
2259 | |
2260 EnterCriticalSection(rb->pMutex); | |
2261 pts=rb->dpts[rb->head]-rb->tStart; | |
2262 memcpy(buffer, rb->ringbuffer[rb->head], bytes); | |
2263 rb->head = (rb->head + 1) % rb->buffersize; | |
2264 rb->count--; | |
2265 LeaveCriticalSection(rb->pMutex); | |
2266 return pts; | |
2267 } | |
2268 | |
2269 /** | |
2270 * \brief returns audio frame size | |
2271 * | |
2272 * \param priv driver's private data structure | |
2273 * | |
2274 * \return audio block size if audio enabled and 1 - otherwise | |
2275 */ | |
2276 static int get_audio_framesize(priv_t * priv) | |
2277 { | |
2278 if (!priv->a_buf) | |
2279 return 1; //no audio | |
2280 mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->a_buf->blocksize); | |
2281 return priv->a_buf->blocksize; | |
2282 } | |
2283 | |
2284 #ifdef HAVE_TV_TELETEXT | |
2285 static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp) | |
2286 { | |
2287 if(!priv || !ptsp) | |
2288 return TVI_CONTROL_FALSE; | |
2289 | |
2290 //STUBS!!! | |
2291 ptsp->interlaced=0; | |
2292 ptsp->offset=256; | |
2293 | |
2294 ptsp->sampling_rate=27e6; | |
2295 ptsp->samples_per_line=720; | |
2296 | |
2297 ptsp->count[0]=16; | |
2298 ptsp->count[1]=16; | |
2299 //END STUBS!!! | |
2300 ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]); | |
2301 | |
2302 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", | |
2303 ptsp->sampling_rate, | |
2304 ptsp->offset, | |
2305 ptsp->samples_per_line, | |
2306 ptsp->interlaced?"Yes":"No", | |
2307 ptsp->count[0], | |
2308 ptsp->count[1]); | |
2309 | |
2310 return TVI_CONTROL_TRUE; | |
2311 } | |
2312 | |
2313 static void vbi_grabber(priv_t* priv) | |
2314 { | |
2315 grabber_ringbuffer_t *rb = priv->vbi_buf; | |
2316 int i; | |
2317 unsigned char* buf; | |
2318 if (!rb || !rb->ringbuffer) | |
2319 return; | |
2320 | |
2321 buf=calloc(1,rb->blocksize); | |
2322 for(i=0; i<23 && rb->count; i++){ | |
2323 memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize); | |
2324 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf); | |
2325 rb->head = (rb->head + 1) % rb->buffersize; | |
2326 rb->count--; | |
2327 } | |
2328 free(buf); | |
2329 } | |
2330 #endif //HAVE_TV_TELETEXT | |
2331 | |
2332 /** | |
2333 * \brief fills given buffer with video data (usually one frame) | |
2334 * | |
2335 * \param priv driver's private data structure | |
2336 * \param buffer buffer to store data to | |
2337 * \param len buffer's size in bytes (usually one frame size) | |
2338 * | |
2339 * \return frame size if video present, 0 - otherwise | |
2340 */ | |
2341 static double grab_video_frame(priv_t * priv, char *buffer, int len) | |
2342 { | |
2343 int bytes = 0; | |
2344 int i; | |
2345 double pts; | |
2346 grabber_ringbuffer_t *rb = priv->v_buf; | |
2347 | |
2348 if (!rb || !rb->ringbuffer) | |
2349 return 1; | |
2350 if (len < rb->blocksize) | |
2351 bytes = len; | |
2352 else | |
2353 bytes = rb->blocksize; | |
2354 | |
2355 mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n", | |
2356 rb->count, len); | |
2357 if(!rb->count){ | |
2358 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n"); | |
2359 for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000); | |
2360 if(!rb->count){ | |
2361 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n"); | |
2362 return 0; | |
2363 } | |
2364 mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n"); | |
2365 } | |
2366 EnterCriticalSection(rb->pMutex); | |
2367 if(rb->tStart<0) | |
2368 rb->tStart=rb->dpts[rb->head]; | |
2369 pts=rb->dpts[rb->head]-rb->tStart; | |
2370 memcpy(buffer, rb->ringbuffer[rb->head], bytes); | |
2371 rb->head = (rb->head + 1) % rb->buffersize; | |
2372 rb->count--; | |
2373 LeaveCriticalSection(rb->pMutex); | |
2374 | |
2375 #ifdef HAVE_TV_TELETEXT | |
2376 vbi_grabber(priv); | |
2377 #endif | |
2378 return pts; | |
2379 } | |
2380 | |
2381 /** | |
2382 * \brief returns frame size | |
2383 * | |
2384 * \param priv driver's private data structure | |
2385 * | |
2386 * \return frame size if video present, 0 - otherwise | |
2387 */ | |
2388 static int get_video_framesize(priv_t * priv) | |
2389 { | |
2390 // if(!priv->pmtVideo) return 1; //no video | |
2391 // return(priv->pmtVideo->lSampleSize); | |
2392 if (!priv->v_buf) | |
2393 return 1; //no video | |
2394 mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->v_buf->blocksize); | |
2395 return priv->v_buf->blocksize; | |
2396 } | |
2397 | |
2398 /** | |
2399 * \brief calculate audio buffer size | |
2400 * \param video_buf_size size of video buffer in bytes | |
2401 * \param video_bitrate video bit rate | |
2402 * \param audio_bitrate audio bit rate | |
2403 * \return audio buffer isze in bytes | |
2404 * | |
2405 * \remarks length of video buffer and resulted audio buffer calculated in | |
2406 * seconds will be the same. | |
2407 */ | |
2408 static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate) | |
2409 { | |
2410 int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate); | |
2411 mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n", | |
2412 audio_bitrate,video_buf_size,video_bitrate,audio_buf_size); | |
2413 return audio_buf_size; | |
2414 } | |
2415 | |
2416 /** | |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2417 * \brief build video stream chain in graph |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2418 * \param priv private data structure |
24744 | 2419 * |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2420 * \return S_OK if chain was built successfully, apropriate error code otherwise |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2421 */ |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2422 static HRESULT build_video_chain(priv_t *priv) |
24744 | 2423 { |
2424 HRESULT hr; | |
2425 | |
25059 | 2426 if(priv->v_buf) |
2427 return S_OK; | |
2428 | |
24744 | 2429 if (priv->pVideoStreamConfig) { |
2430 hr = OLE_CALL_ARGS(priv->pVideoStreamConfig, SetFormat, priv->pmtVideo); | |
2431 if (FAILED(hr)) { | |
2432 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectVideoFormat, (unsigned int)hr); | |
2433 } | |
2434 } | |
2435 | |
2436 priv->v_buf=calloc(1,sizeof(grabber_ringbuffer_t)); | |
25058 | 2437 if(!priv->v_buf) |
2438 return E_OUTOFMEMORY; | |
24744 | 2439 |
2440 if (priv->tv_param->buffer_size >= 0) { | |
2441 priv->v_buf->buffersize = priv->tv_param->buffer_size; | |
2442 } else { | |
2443 priv->v_buf->buffersize = 16; | |
2444 } | |
2445 | |
2446 priv->v_buf->buffersize *= 1024 * 1024; | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
2447 hr=build_sub_graph(priv, priv->pVideoFilter, priv->v_buf, priv->arpmtVideo, priv->pmtVideo, &PIN_CATEGORY_CAPTURE); |
24744 | 2448 if(FAILED(hr)){ |
2449 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVideoSubGraph,(unsigned int)hr); | |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2450 return hr; |
24744 | 2451 } |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2452 return S_OK; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2453 } |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2454 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2455 /** |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2456 * \brief build audio stream chain in graph |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2457 * \param priv private data structure |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2458 * |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2459 * \return S_OK if chain was built successfully, apropriate error code otherwise |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2460 */ |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2461 static HRESULT build_audio_chain(priv_t *priv) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2462 { |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2463 HRESULT hr; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2464 |
25059 | 2465 if(priv->a_buf) |
2466 return S_OK; | |
2467 | |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2468 if(priv->immediate_mode) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2469 return S_OK; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2470 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2471 if (priv->pAudioStreamConfig) { |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2472 hr = OLE_CALL_ARGS(priv->pAudioStreamConfig, SetFormat, |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2473 priv->pmtAudio); |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2474 if (FAILED(hr)) { |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2475 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectAudioFormat, (unsigned int)hr); |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2476 } |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2477 } |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2478 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2479 if(priv->pmtAudio){ |
24744 | 2480 priv->a_buf=calloc(1,sizeof(grabber_ringbuffer_t)); |
25058 | 2481 if(!priv->a_buf) |
2482 return E_OUTOFMEMORY; | |
24744 | 2483 |
2484 /* let the audio buffer be the same size (in seconds) than video one */ | |
2485 priv->a_buf->buffersize=audio_buf_size_from_video( | |
2486 priv->v_buf->buffersize, | |
2487 (((VIDEOINFOHEADER *) priv->pmtVideo->pbFormat)->dwBitRate), | |
2488 (((WAVEFORMATEX *) (priv->pmtAudio->pbFormat))->nAvgBytesPerSec)); | |
2489 | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
2490 hr=build_sub_graph(priv, priv->pAudioFilter, priv->a_buf,priv->arpmtAudio,priv->pmtAudio,&PIN_CATEGORY_CAPTURE); |
24744 | 2491 if(FAILED(hr)){ |
2492 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildAudioSubGraph,(unsigned int)hr); | |
2493 return 0; | |
2494 } | |
2495 } | |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2496 return S_OK; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2497 } |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2498 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2499 /** |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2500 * \brief build VBI stream chain in graph |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2501 * \param priv private data structure |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2502 * |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2503 * \return S_OK if chain was built successfully, apropriate error code otherwise |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2504 */ |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2505 static HRESULT build_vbi_chain(priv_t *priv) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2506 { |
24744 | 2507 #ifdef HAVE_TV_TELETEXT |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2508 HRESULT hr; |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
2509 AM_MEDIA_TYPE* arpmtVBI[2] = { priv->pmtVBI, NULL }; |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2510 |
25059 | 2511 if(priv->vbi_buf) |
2512 return S_OK; | |
2513 | |
24744 | 2514 if(priv->tv_param->tdevice) |
2515 { | |
2516 priv->vbi_buf=calloc(1,sizeof(grabber_ringbuffer_t)); | |
25058 | 2517 if(!priv->vbi_buf) |
2518 return E_OUTOFMEMORY; | |
2519 | |
24744 | 2520 init_ringbuffer(priv->vbi_buf,24,priv->tsp.bufsize); |
2521 | |
2522 priv->pmtVBI=calloc(1,sizeof(AM_MEDIA_TYPE)); | |
2523 priv->pmtVBI->majortype=MEDIATYPE_VBI; | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
2524 hr=build_sub_graph(priv, priv->pVideoFilter, priv->vbi_buf,arpmtVBI,NULL,&PIN_CATEGORY_VBI); |
24744 | 2525 if(FAILED(hr)){ |
2526 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVBISubGraph,(unsigned int)hr); | |
2527 return 0; | |
2528 } | |
2529 } | |
2530 #endif | |
25057
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2531 return S_OK; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2532 } |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2533 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2534 /** |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2535 * \brief playback/capture real start |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2536 * |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2537 * \param priv driver's private data structure |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2538 * |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2539 * \return 1 if success, 0 - otherwise |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2540 * |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2541 * TODO: move some code from init() here |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2542 */ |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2543 static int start(priv_t * priv) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2544 { |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2545 HRESULT hr; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2546 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2547 hr = build_video_chain(priv); |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2548 if(FAILED(hr)) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2549 return 0; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2550 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2551 hr = build_audio_chain(priv); |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2552 if(FAILED(hr)) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2553 return 0; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2554 |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2555 hr = build_vbi_chain(priv); |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2556 if(FAILED(hr)) |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2557 return 0; |
7d74c1a2c840
Move chains building code into separate routines.
voroshil
parents:
25054
diff
changeset
|
2558 |
24744 | 2559 /* |
2560 Graph is ready to capture. Starting graph. | |
2561 */ | |
2562 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) { | |
2563 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n"); | |
2564 usec_sleep(10000000); | |
2565 mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n"); | |
2566 } | |
2567 if (!priv->pMediaControl) { | |
2568 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)E_POINTER); | |
2569 return 0; | |
2570 } | |
2571 hr = OLE_CALL(priv->pMediaControl, Run); | |
2572 if (FAILED(hr)) { | |
2573 mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableStartGraph, (unsigned int)hr); | |
2574 return 0; | |
2575 } | |
2576 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n"); | |
2577 priv->state = 1; | |
2578 | |
2579 return (1); | |
2580 } | |
2581 | |
2582 /** | |
2583 * \brief driver initialization | |
2584 * | |
2585 * \param priv driver's private data structure | |
2586 * | |
2587 * \return 1 if success, 0 - otherwise | |
2588 */ | |
2589 static int init(priv_t * priv) | |
2590 { | |
2591 HRESULT hr; | |
2592 int result = 0; | |
2593 long lInput, lTunerInput; | |
2594 IEnumFilters *pEnum; | |
2595 IBaseFilter *pFilter; | |
2596 IPin *pVPOutPin; | |
2597 | |
2598 priv->state=0; | |
2599 CoInitialize(NULL); | |
2600 do{ | |
2601 hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL, | |
2602 CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, | |
2603 (void **) &priv->pGraph); | |
2604 if(FAILED(hr)){ | |
2605 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr); | |
2606 break; | |
2607 } | |
2608 //Debug | |
2609 if (mp_msg_test(MSGT_TV, MSGL_DBG2)) { | |
2610 AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister)); | |
2611 } | |
2612 | |
2613 hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL, | |
2614 CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, | |
2615 (void **) &priv->pBuilder); | |
2616 if(FAILED(hr)){ | |
2617 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr); | |
2618 break; | |
2619 } | |
2620 | |
2621 hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph); | |
2622 if(FAILED(hr)){ | |
2623 mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr); | |
2624 break; | |
2625 } | |
2626 | |
2627 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n"); | |
2628 priv->pVideoFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory); | |
2629 if(!priv->pVideoFilter){ | |
2630 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoVideoCaptureDevice); | |
2631 break; | |
2632 } | |
2633 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->pVideoFilter, NULL); | |
2634 if(FAILED(hr)){ | |
2635 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr); | |
2636 break; | |
2637 } | |
2638 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n"); | |
2639 if (priv->adev_index != -1) { | |
2640 priv->pAudioFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices | |
2641 if(!priv->pAudioFilter){ | |
2642 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoAudioCaptureDevice); | |
2643 break; | |
2644 } | |
2645 | |
2646 hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->pAudioFilter, NULL); | |
2647 if(FAILED(hr)){ | |
2648 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr); | |
2649 break; | |
2650 } | |
2651 } else | |
2652 hr = OLE_QUERYINTERFACE(priv->pVideoFilter, IID_IBaseFilter, priv->pAudioFilter); | |
2653 | |
2654 hr = OLE_QUERYINTERFACE(priv->pVideoFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp); | |
2655 if (FAILED(hr) && hr != E_NOINTERFACE) | |
2656 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr); | |
2657 | |
2658 if (hr != S_OK) { | |
2659 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_VideoAdjustigNotSupported); | |
2660 priv->pVideoProcAmp = NULL; | |
2661 } | |
2662 | |
2663 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, | |
2664 &PIN_CATEGORY_CAPTURE, | |
2665 &MEDIATYPE_Video, | |
2666 priv->pVideoFilter, | |
2667 &IID_IAMCrossbar, (void **) &(priv->pCrossbar)); | |
2668 if (FAILED(hr)) { | |
2669 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_SelectingInputNotSupported); | |
2670 priv->pCrossbar = NULL; | |
2671 } | |
2672 | |
2673 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, | |
2674 &PIN_CATEGORY_CAPTURE, | |
2675 &MEDIATYPE_Video, | |
2676 priv->pVideoFilter, | |
2677 &IID_IAMStreamConfig, | |
2678 (void **) &(priv->pVideoStreamConfig)); | |
2679 if (FAILED(hr)) { | |
2680 mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_ChangingWidthHeightNotSupported); | |
2681 priv->pVideoStreamConfig = NULL; | |
2682 } | |
2683 | |
2684 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, | |
2685 &PIN_CATEGORY_CAPTURE, | |
2686 &MEDIATYPE_Audio, | |
2687 priv->pAudioFilter, | |
2688 &IID_IAMStreamConfig, | |
2689 (void **) &(priv->pAudioStreamConfig)); | |
2690 if (FAILED(hr)) { | |
2691 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IAMStreamConfig(audio) failed (0x%x).\n", (unsigned int)hr); | |
2692 priv->pAudioStreamConfig = NULL; | |
2693 } | |
2694 | |
2695 if (priv->tv_param->amode >= 0) { | |
2696 IAMTVAudio *pTVAudio; | |
2697 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->pVideoFilter,&IID_IAMTVAudio, (void *) &pTVAudio); | |
2698 if (hr == S_OK) { | |
2699 switch (priv->tv_param->amode) { | |
2700 case 0: | |
2701 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO); | |
2702 break; | |
2703 case 1: | |
2704 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO); | |
2705 break; | |
2706 case 2: | |
2707 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, | |
2708 AMTVAUDIO_MODE_LANG_A); | |
2709 break; | |
2710 case 3: | |
2711 hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, | |
2712 AMTVAUDIO_MODE_LANG_B); | |
2713 break; | |
2714 } | |
2715 OLE_RELEASE_SAFE(pTVAudio); | |
2716 if (FAILED(hr)) | |
2717 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_UnableSetAudioMode, priv->tv_param->amode,(unsigned int)hr); | |
2718 } | |
2719 } | |
2720 /* | |
2721 Getting available video formats (last pointer in array will be NULL) | |
2722 First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as | |
2723 min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default | |
2724 min/max values. | |
2725 */ | |
2726 | |
2727 hr = get_available_formats_stream(priv->pVideoStreamConfig, | |
2728 &MEDIATYPE_Video, | |
2729 &(priv->arpmtVideo), | |
2730 (void ***) &(priv->arVideoCaps)); | |
2731 if (FAILED(hr)) { | |
2732 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available video formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr); | |
2733 hr = get_available_formats_pin(priv->pBuilder, priv->pVideoFilter, | |
2734 &MEDIATYPE_Video, | |
2735 &(priv->arpmtVideo), | |
2736 (void ***) &(priv->arVideoCaps)); | |
2737 if(FAILED(hr)){ | |
2738 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetsupportedVideoFormats, (unsigned int)hr); | |
2739 break; | |
2740 } | |
2741 } | |
2742 priv->nVideoFormatUsed = 0; | |
2743 | |
2744 if (!priv->arpmtVideo[priv->nVideoFormatUsed] | |
2745 || !extract_video_format(priv->arpmtVideo[priv->nVideoFormatUsed], | |
2746 &(priv->fcc), &(priv->width), | |
2747 &(priv->height))) { | |
2748 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingVideoFormatStruct); | |
2749 break; | |
2750 } | |
2751 priv->pmtVideo = CreateMediaType(priv->arpmtVideo[priv->nVideoFormatUsed]); | |
2752 /* | |
2753 Getting available audio formats (last pointer in array will be NULL) | |
2754 */ | |
2755 hr = get_available_formats_stream(priv->pAudioStreamConfig, | |
2756 &MEDIATYPE_Audio, | |
2757 &(priv->arpmtAudio), | |
2758 (void ***) &(priv->arAudioCaps)); | |
2759 if (FAILED(hr)) { | |
2760 mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available audio formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr); | |
2761 hr = get_available_formats_pin(priv->pBuilder, priv->pAudioFilter, | |
2762 &MEDIATYPE_Audio, | |
2763 &(priv->arpmtAudio), | |
2764 (void ***) &(priv->arAudioCaps)); | |
2765 if (FAILED(hr)) { | |
2766 mp_msg(MSGT_TV,MSGL_WARN, MSGTR_TVI_DS_UnableGetsupportedAudioFormats, (unsigned int)hr); | |
2767 /* | |
2768 Following combination will disable sound | |
2769 */ | |
2770 priv->arpmtAudio = (AM_MEDIA_TYPE **) malloc(sizeof(AM_MEDIA_TYPE *)); | |
2771 priv->arpmtAudio[0] = NULL; | |
2772 priv->pmtAudio = NULL; | |
2773 priv->pAudioStreamConfig = NULL; | |
2774 } | |
2775 } | |
2776 /* debug */ | |
2777 { | |
2778 int i; | |
2779 for (i = 0; priv->arpmtVideo[i]; i++) { | |
2780 DisplayMediaType("Available video format", priv->arpmtVideo[i]); | |
2781 } | |
2782 for (i = 0; priv->arpmtAudio[i]; i++) { | |
2783 DisplayMediaType("Available audio format", priv->arpmtAudio[i]); | |
2784 } | |
2785 } | |
2786 priv->nAudioFormatUsed = 0; | |
2787 if (priv->arpmtAudio[priv->nAudioFormatUsed]) { | |
2788 priv->pmtAudio = CreateMediaType(priv->arpmtAudio[priv->nAudioFormatUsed]); | |
2789 if (!extract_audio_format(priv->pmtAudio, &(priv->samplerate), NULL, NULL)) { | |
2790 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingAudioFormatStruct); | |
2791 DisplayMediaType("audio format failed",priv->arpmtAudio[priv->nAudioFormatUsed]); | |
2792 break; | |
2793 } | |
2794 } | |
2795 show_filter_info(priv->pVideoFilter); | |
2796 | |
2797 hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl); | |
2798 if(FAILED(hr)){ | |
2799 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)hr); | |
2800 break; | |
2801 } | |
2802 hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, | |
2803 &PIN_CATEGORY_CAPTURE, NULL, | |
2804 priv->pVideoFilter, | |
2805 &IID_IAMTVTuner, (void **) &(priv->pTVTuner)); | |
2806 | |
2807 if (!priv->pTVTuner) { | |
2808 mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr); | |
2809 } | |
2810 | |
2811 // shows Tuner capabilities | |
2812 get_capabilities(priv); | |
2813 | |
2814 if (priv->pTVTuner) { | |
2815 hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode, | |
2816 chanlist2country(priv->tv_param->chanlist)); | |
2817 if(FAILED(hr)){ | |
2818 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr); | |
2819 } | |
2820 | |
2821 hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV); | |
2822 if(FAILED(hr)){ | |
2823 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr); | |
2824 break; | |
2825 } | |
2826 | |
2827 hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput); | |
2828 if(FAILED(hr)){ | |
2829 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr); | |
2830 break; | |
2831 } | |
2832 | |
2833 /* small hack */ | |
2834 lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna; | |
2835 | |
2836 hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput); | |
2837 if(FAILED(hr)){ | |
2838 mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr); | |
2839 break; | |
2840 } | |
2841 | |
2842 } | |
2843 | |
2844 /** | |
2845 for VIVO cards we should check if preview pin is available on video capture device. | |
2846 If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter. | |
2847 Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph | |
2848 */ | |
2849 hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, | |
2850 (IUnknown *) priv->pVideoFilter, | |
2851 PINDIR_OUTPUT, | |
2852 &PIN_CATEGORY_VIDEOPORT, NULL, FALSE, | |
2853 0, (IPin **) & pVPOutPin); | |
2854 if (SUCCEEDED(hr)) { | |
2855 hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin); | |
2856 OLE_RELEASE_SAFE(pVPOutPin); | |
2857 | |
2858 if (FAILED(hr)) { | |
2859 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableTerminateVPPin, (unsigned int)hr); | |
2860 break; | |
2861 } | |
2862 } | |
2863 | |
2864 OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum); | |
2865 while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) { | |
2866 LPVIDEOWINDOW pVideoWindow; | |
2867 hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow); | |
2868 if (SUCCEEDED(hr)) | |
2869 { | |
2870 if(priv->tv_param->hidden_vp_renderer){ | |
2871 OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0); | |
2872 OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0); | |
2873 }else | |
2874 { | |
2875 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter); | |
2876 } | |
2877 OLE_RELEASE_SAFE(pVideoWindow); | |
2878 } | |
2879 OLE_RELEASE_SAFE(pFilter); | |
2880 } | |
2881 OLE_RELEASE_SAFE(pEnum); | |
2882 if(priv->tv_param->system_clock) | |
2883 { | |
2884 LPREFERENCECLOCK rc; | |
2885 IBaseFilter* pBF; | |
2886 hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL, | |
2887 CLSCTX_INPROC_SERVER, &IID_IReferenceClock, | |
2888 (void *) &rc); | |
2889 | |
2890 OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF); | |
2891 OLE_CALL_ARGS(pBF,SetSyncSource,rc); | |
2892 } | |
2893 #ifdef HAVE_TV_TELETEXT | |
2894 if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE) | |
2895 break; | |
2896 #endif | |
2897 result = 1; | |
2898 } while(0); | |
2899 | |
2900 if (!result){ | |
2901 mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_GraphInitFailure); | |
2902 uninit(priv); | |
2903 } | |
2904 return result; | |
2905 } | |
2906 | |
2907 /** | |
2908 * \brief driver uninitialization | |
2909 * | |
2910 * \param priv driver's private data structure | |
2911 * | |
2912 * \return always 1 | |
2913 */ | |
2914 static int uninit(priv_t * priv) | |
2915 { | |
2916 int i; | |
2917 if (!priv) | |
2918 return (1); | |
2919 //Debug | |
2920 if (priv->dwRegister) { | |
2921 RemoveFromRot(priv->dwRegister); | |
2922 } | |
2923 #ifdef HAVE_TV_TELETEXT | |
2924 teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1); | |
2925 #endif | |
2926 //stop audio grabber thread | |
2927 | |
2928 if (priv->state && priv->pMediaControl) { | |
2929 OLE_CALL(priv->pMediaControl, Stop); | |
2930 } | |
2931 OLE_RELEASE_SAFE(priv->pMediaControl); | |
2932 priv->state = 0; | |
2933 | |
2934 if (priv->pGraph) { | |
2935 if (priv->pVideoFilter) | |
2936 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->pVideoFilter); | |
2937 if (priv->pAudioFilter) | |
2938 OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->pAudioFilter); | |
2939 } | |
2940 OLE_RELEASE_SAFE(priv->pCrossbar); | |
2941 OLE_RELEASE_SAFE(priv->pVideoStreamConfig); | |
2942 OLE_RELEASE_SAFE(priv->pAudioStreamConfig); | |
2943 OLE_RELEASE_SAFE(priv->pVideoProcAmp); | |
2944 OLE_RELEASE_SAFE(priv->pVideoFilter); | |
2945 OLE_RELEASE_SAFE(priv->pAudioFilter); | |
2946 OLE_RELEASE_SAFE(priv->pGraph); | |
2947 OLE_RELEASE_SAFE(priv->pBuilder); | |
2948 OLE_RELEASE_SAFE(priv->pCSGCB); | |
2949 | |
2950 if (priv->pmtVideo) | |
2951 DeleteMediaType(priv->pmtVideo); | |
2952 if (priv->pmtAudio) | |
2953 DeleteMediaType(priv->pmtAudio); | |
2954 if (priv->pmtVBI) | |
2955 DeleteMediaType(priv->pmtVBI); | |
2956 | |
2957 if (priv->arpmtVideo) { | |
2958 for (i = 0; priv->arpmtVideo[i]; i++) { | |
2959 DeleteMediaType(priv->arpmtVideo[i]); | |
2960 } | |
2961 free(priv->arpmtVideo); | |
2962 } | |
2963 if (priv->arVideoCaps) { | |
2964 for (i = 0; priv->arVideoCaps[i]; i++) { | |
2965 free(priv->arVideoCaps[i]); | |
2966 } | |
2967 free(priv->arVideoCaps); | |
2968 } | |
2969 if (priv->arpmtAudio) { | |
2970 for (i = 0; priv->arpmtAudio[i]; i++) { | |
2971 DeleteMediaType(priv->arpmtAudio[i]); | |
2972 } | |
2973 free(priv->arpmtAudio); | |
2974 } | |
2975 if (priv->arAudioCaps) { | |
2976 for (i = 0; priv->arAudioCaps[i]; i++) { | |
2977 free(priv->arAudioCaps[i]); | |
2978 } | |
2979 free(priv->arAudioCaps); | |
2980 } | |
2981 if (priv->a_buf) { | |
2982 destroy_ringbuffer(priv->a_buf); | |
2983 free(priv->a_buf); | |
2984 priv->a_buf = NULL; | |
2985 } | |
2986 if (priv->v_buf) { | |
2987 destroy_ringbuffer(priv->v_buf); | |
2988 free(priv->v_buf); | |
2989 priv->v_buf = NULL; | |
2990 } | |
2991 if (priv->vbi_buf) { | |
2992 destroy_ringbuffer(priv->vbi_buf); | |
2993 free(priv->vbi_buf); | |
2994 priv->vbi_buf = NULL; | |
2995 } | |
2996 if(priv->freq_table){ | |
2997 priv->freq_table_len=-1; | |
2998 free(priv->freq_table); | |
2999 priv->freq_table=NULL; | |
3000 } | |
3001 CoUninitialize(); | |
3002 return (1); | |
3003 } | |
3004 | |
3005 /** | |
3006 * \brief driver pre-initialization | |
3007 * | |
3008 * \param device string, containing device name in form "x[.y]", where x is video capture device | |
3009 * (default: 0, first available); y (if given) sets audio capture device | |
3010 * | |
3011 * \return 1 if success,0 - otherwise | |
3012 */ | |
3013 static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param) | |
3014 { | |
3015 tvi_handle_t *h; | |
3016 priv_t *priv; | |
3017 int a; | |
3018 | |
3019 h = new_handle(); | |
3020 if (!h) | |
3021 return (NULL); | |
3022 | |
3023 priv = h->priv; | |
3024 | |
3025 memset(priv, 0, sizeof(priv_t)); | |
3026 priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled | |
3027 priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled | |
3028 priv->adev_index = -1; | |
3029 priv->freq_table_len=-1; | |
3030 priv->tv_param=tv_param; | |
3031 | |
3032 if (tv_param->device) { | |
3033 if (sscanf(tv_param->device, "%d", &a) == 1) { | |
3034 priv->dev_index = a; | |
3035 } else { | |
3036 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceParam, tv_param->device); | |
3037 free_handle(h); | |
3038 return NULL; | |
3039 } | |
3040 if (priv->dev_index < 0) { | |
3041 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceIndex, a); | |
3042 free_handle(h); | |
3043 return NULL; | |
3044 } | |
3045 } | |
3046 if (tv_param->adevice) { | |
3047 if (sscanf(tv_param->adevice, "%d", &a) == 1) { | |
3048 priv->adev_index = a; | |
3049 } else { | |
3050 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceParam, tv_param->adevice); | |
3051 free_handle(h); | |
3052 return NULL; | |
3053 } | |
3054 if (priv->dev_index < 0) { | |
3055 mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceIndex, a); | |
3056 free_handle(h); | |
3057 return NULL; | |
3058 } | |
3059 } | |
3060 return h; | |
3061 } | |
3062 | |
3063 /** | |
3064 * \brief driver's ioctl handler | |
3065 * | |
3066 * \param priv driver's private data structure | |
3067 * \param cmd ioctl command | |
3068 * \param arg ioct command's parameter | |
3069 * | |
3070 * \return TVI_CONTROL_TRUE if success | |
3071 * \return TVI_CONTROL_FALSE if failure | |
3072 * \return TVI_CONTROL_UNKNOWN if unknowm cmd called | |
3073 */ | |
3074 static int control(priv_t * priv, int cmd, void *arg) | |
3075 { | |
3076 switch (cmd) { | |
3077 /* need rewrite */ | |
3078 case TVI_CONTROL_VID_SET_FORMAT: | |
3079 { | |
3080 int fcc, i; | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3081 void* tmp; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3082 |
24744 | 3083 if (priv->state) |
3084 return TVI_CONTROL_FALSE; | |
3085 fcc = *(int *) arg; | |
3086 | |
3087 if(!priv->arpmtVideo) | |
3088 return TVI_CONTROL_FALSE; | |
3089 for (i = 0; priv->arpmtVideo[i]; i++) | |
3090 if (check_video_format | |
3091 (priv->arpmtVideo[i], fcc, priv->width, priv->height)) | |
3092 break; | |
3093 if (!priv->arpmtVideo[i]) | |
3094 return TVI_CONTROL_FALSE; | |
3095 | |
25063
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3096 tmp = priv->arpmtVideo[0]; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3097 priv->arpmtVideo[0] = priv->arpmtVideo[i]; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3098 priv->arpmtVideo[i] = tmp; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3099 |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3100 tmp = priv->arVideoCaps[0]; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3101 priv->arVideoCaps[0] = priv->arVideoCaps[i]; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3102 priv->arVideoCaps[i] = tmp; |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3103 |
29260745e4fa
Pass all available formats to chain building routine and
voroshil
parents:
25061
diff
changeset
|
3104 priv->nVideoFormatUsed = 0; |
24744 | 3105 |
3106 if (priv->pmtVideo) | |
3107 DeleteMediaType(priv->pmtVideo); | |
3108 priv->pmtVideo = | |
3109 CreateMediaType(priv->arpmtVideo[priv->nVideoFormatUsed]); | |
3110 DisplayMediaType("VID_SET_FORMAT", priv->pmtVideo); | |
3111 /* | |
3112 Setting width & height to preferred by driver values | |
3113 */ | |
3114 extract_video_format(priv->arpmtVideo[priv->nVideoFormatUsed], | |
3115 &(priv->fcc), &(priv->width), | |
3116 &(priv->height)); | |
3117 return TVI_CONTROL_TRUE; | |
3118 } | |
3119 case TVI_CONTROL_VID_GET_FORMAT: | |
3120 { | |
3121 if(!priv->pmtVideo) | |
3122 return TVI_CONTROL_FALSE; | |
3123 DisplayMediaType("VID_GET_FORMAT", priv->pmtVideo); | |
3124 if (priv->fcc) { | |
3125 *(int *) arg = priv->fcc; | |
3126 return (TVI_CONTROL_TRUE); | |
3127 } else | |
3128 return (TVI_CONTROL_FALSE); | |
3129 } | |
3130 case TVI_CONTROL_VID_SET_WIDTH: | |
3131 { | |
3132 VIDEO_STREAM_CONFIG_CAPS *pCaps; | |
3133 VIDEOINFOHEADER *Vhdr; | |
3134 int width = *(int *) arg; | |
3135 if (priv->state) | |
3136 return TVI_CONTROL_FALSE; | |
3137 | |
3138 pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; | |
3139 if (!pCaps) | |
3140 return TVI_CONTROL_FALSE; | |
3141 if (width < pCaps->MinOutputSize.cx | |
3142 || width > pCaps->MaxOutputSize.cx) | |
3143 return TVI_CONTROL_FALSE; | |
3144 | |
3145 if (width % pCaps->OutputGranularityX) | |
3146 return TVI_CONTROL_FALSE; | |
3147 | |
3148 if (!priv->pmtVideo || !priv->pmtVideo->pbFormat) | |
3149 return TVI_CONTROL_FALSE; | |
3150 Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; | |
3151 Vhdr->bmiHeader.biWidth = width; | |
3152 priv->pmtVideo->lSampleSize = Vhdr->bmiHeader.biSizeImage = | |
3153 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth * | |
3154 Vhdr->bmiHeader.biHeight) >> 3; | |
3155 | |
3156 priv->width = width; | |
3157 | |
3158 return (TVI_CONTROL_TRUE); | |
3159 } | |
3160 case TVI_CONTROL_VID_GET_WIDTH: | |
3161 { | |
3162 if (priv->width) { | |
3163 *(int *) arg = priv->width; | |
3164 return (TVI_CONTROL_TRUE); | |
3165 } else | |
3166 return TVI_CONTROL_FALSE; | |
3167 } | |
3168 case TVI_CONTROL_VID_CHK_WIDTH: | |
3169 { | |
3170 VIDEO_STREAM_CONFIG_CAPS *pCaps; | |
3171 int width = *(int *) arg; | |
3172 pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; | |
3173 if (!pCaps) | |
3174 return TVI_CONTROL_FALSE; | |
3175 if (width < pCaps->MinOutputSize.cx | |
3176 || width > pCaps->MaxOutputSize.cx) | |
3177 return TVI_CONTROL_FALSE; | |
3178 | |
3179 if (width % pCaps->OutputGranularityX) | |
3180 return TVI_CONTROL_FALSE; | |
3181 return (TVI_CONTROL_TRUE); | |
3182 } | |
3183 case TVI_CONTROL_VID_SET_HEIGHT: | |
3184 { | |
3185 VIDEO_STREAM_CONFIG_CAPS *pCaps; | |
3186 VIDEOINFOHEADER *Vhdr; | |
3187 int height = *(int *) arg; | |
3188 if (priv->state) | |
3189 return TVI_CONTROL_FALSE; | |
3190 | |
3191 pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; | |
3192 if (!pCaps) | |
3193 return TVI_CONTROL_FALSE; | |
3194 if (height < pCaps->MinOutputSize.cy | |
3195 || height > pCaps->MaxOutputSize.cy) | |
3196 return TVI_CONTROL_FALSE; | |
3197 | |
3198 if (height % pCaps->OutputGranularityY) | |
3199 return TVI_CONTROL_FALSE; | |
3200 | |
3201 if (!priv->pmtVideo || !priv->pmtVideo->pbFormat) | |
3202 return TVI_CONTROL_FALSE; | |
3203 Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; | |
3204 | |
3205 if (Vhdr->bmiHeader.biHeight < 0) | |
3206 Vhdr->bmiHeader.biHeight = -height; | |
3207 else | |
3208 Vhdr->bmiHeader.biHeight = height; | |
3209 priv->pmtVideo->lSampleSize = Vhdr->bmiHeader.biSizeImage = | |
3210 labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth * | |
3211 Vhdr->bmiHeader.biHeight) >> 3; | |
3212 | |
3213 priv->height = height; | |
3214 return (TVI_CONTROL_TRUE); | |
3215 } | |
3216 case TVI_CONTROL_VID_GET_HEIGHT: | |
3217 { | |
3218 if (priv->height) { | |
3219 *(int *) arg = priv->height; | |
3220 return (TVI_CONTROL_TRUE); | |
3221 } else | |
3222 return TVI_CONTROL_FALSE; | |
3223 } | |
3224 case TVI_CONTROL_VID_CHK_HEIGHT: | |
3225 { | |
3226 VIDEO_STREAM_CONFIG_CAPS *pCaps; | |
3227 int height = *(int *) arg; | |
3228 pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; | |
3229 if (!pCaps) | |
3230 return TVI_CONTROL_FALSE; | |
3231 if (height < pCaps->MinOutputSize.cy | |
3232 || height > pCaps->MaxOutputSize.cy) | |
3233 return TVI_CONTROL_FALSE; | |
3234 | |
3235 if (height % pCaps->OutputGranularityY) | |
3236 return TVI_CONTROL_FALSE; | |
3237 | |
3238 return (TVI_CONTROL_TRUE); | |
3239 } | |
3240 case TVI_CONTROL_IS_AUDIO: | |
3241 if (!priv->pmtAudio) | |
3242 return TVI_CONTROL_FALSE; | |
3243 else | |
3244 return TVI_CONTROL_TRUE; | |
3245 case TVI_CONTROL_IS_VIDEO: | |
3246 return TVI_CONTROL_TRUE; | |
3247 case TVI_CONTROL_AUD_GET_FORMAT: | |
3248 { | |
3249 *(int *) arg = AF_FORMAT_S16_LE; | |
3250 if (!priv->pmtAudio) | |
3251 return TVI_CONTROL_FALSE; | |
3252 else | |
3253 return TVI_CONTROL_TRUE; | |
3254 } | |
3255 case TVI_CONTROL_AUD_GET_CHANNELS: | |
3256 { | |
3257 *(int *) arg = priv->channels; | |
3258 if (!priv->pmtAudio) | |
3259 return TVI_CONTROL_FALSE; | |
3260 else | |
3261 return TVI_CONTROL_TRUE; | |
3262 } | |
3263 case TVI_CONTROL_AUD_SET_SAMPLERATE: | |
3264 { | |
3265 int i, samplerate; | |
3266 if (priv->state) | |
3267 return TVI_CONTROL_FALSE; | |
3268 if (!priv->arpmtAudio[0]) | |
3269 return TVI_CONTROL_FALSE; | |
3270 | |
3271 samplerate = *(int *) arg;; | |
3272 | |
3273 for (i = 0; priv->arpmtAudio[i]; i++) | |
3274 if (check_audio_format | |
3275 (priv->arpmtAudio[i], samplerate, 16, priv->channels)) | |
3276 break; | |
3277 if (!priv->arpmtAudio[i]) { | |
3278 //request not found. failing back to first available | |
3279 mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_SamplerateNotsupported, samplerate); | |
3280 i = 0; | |
3281 } | |
3282 if (priv->pmtAudio) | |
3283 DeleteMediaType(priv->pmtAudio); | |
3284 priv->pmtAudio = CreateMediaType(priv->arpmtAudio[i]); | |
3285 extract_audio_format(priv->arpmtAudio[i], &(priv->samplerate), | |
3286 NULL, &(priv->channels)); | |
3287 return TVI_CONTROL_TRUE; | |
3288 } | |
3289 case TVI_CONTROL_AUD_GET_SAMPLERATE: | |
3290 { | |
3291 *(int *) arg = priv->samplerate; | |
3292 if (!priv->samplerate) | |
3293 return TVI_CONTROL_FALSE; | |
3294 if (!priv->pmtAudio) | |
3295 return TVI_CONTROL_FALSE; | |
3296 else | |
3297 return TVI_CONTROL_TRUE; | |
3298 } | |
3299 case TVI_CONTROL_AUD_GET_SAMPLESIZE: | |
3300 { | |
3301 WAVEFORMATEX *pWF; | |
3302 if (!priv->pmtAudio) | |
3303 return TVI_CONTROL_FALSE; | |
3304 if (!priv->pmtAudio->pbFormat) | |
3305 return TVI_CONTROL_FALSE; | |
3306 pWF = (WAVEFORMATEX *) priv->pmtAudio->pbFormat; | |
3307 *(int *) arg = pWF->wBitsPerSample / 8; | |
3308 return TVI_CONTROL_TRUE; | |
3309 } | |
3310 case TVI_CONTROL_IS_TUNER: | |
3311 { | |
3312 if (!priv->pTVTuner) | |
3313 return TVI_CONTROL_FALSE; | |
3314 | |
3315 return (TVI_CONTROL_TRUE); | |
3316 } | |
3317 case TVI_CONTROL_TUN_SET_NORM: | |
3318 { | |
3319 IAMAnalogVideoDecoder *pVD; | |
3320 long lAnalogFormat; | |
3321 int i; | |
3322 HRESULT hr; | |
3323 | |
3324 i = *(int *) arg; | |
3325 i--; | |
3326 if (i < 0 || i >= tv_available_norms_count) | |
3327 return TVI_CONTROL_FALSE; | |
3328 lAnalogFormat = tv_norms[tv_available_norms[i]].index; | |
3329 | |
3330 hr = OLE_QUERYINTERFACE(priv->pVideoFilter,IID_IAMAnalogVideoDecoder, pVD); | |
3331 if (hr != S_OK) | |
3332 return TVI_CONTROL_FALSE; | |
3333 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat); | |
3334 OLE_RELEASE_SAFE(pVD); | |
3335 if (FAILED(hr)) | |
3336 return (TVI_CONTROL_FALSE); | |
3337 else | |
3338 return (TVI_CONTROL_TRUE); | |
3339 } | |
3340 case TVI_CONTROL_TUN_GET_NORM: | |
3341 { | |
3342 long lAnalogFormat; | |
3343 int i; | |
3344 HRESULT hr; | |
3345 IAMAnalogVideoDecoder *pVD; | |
3346 | |
3347 hr = OLE_QUERYINTERFACE(priv->pVideoFilter,IID_IAMAnalogVideoDecoder, pVD); | |
3348 if (hr == S_OK) { | |
3349 hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat); | |
3350 OLE_RELEASE_SAFE(pVD); | |
3351 } | |
3352 | |
3353 if (FAILED(hr)) { //trying another method | |
3354 if (!priv->pTVTuner) | |
3355 return TVI_CONTROL_FALSE; | |
3356 hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat); | |
3357 if (FAILED(hr)) | |
3358 return TVI_CONTROL_FALSE; | |
3359 } | |
3360 for (i = 0; i < tv_available_norms_count; i++) { | |
3361 if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) { | |
3362 *(int *) arg = i + 1; | |
3363 return TVI_CONTROL_TRUE; | |
3364 } | |
3365 } | |
3366 return (TVI_CONTROL_FALSE); | |
3367 } | |
3368 case TVI_CONTROL_SPC_GET_NORMID: | |
3369 { | |
3370 int i; | |
3371 if (!priv->pTVTuner) | |
3372 return TVI_CONTROL_FALSE; | |
3373 for (i = 0; i < tv_available_norms_count; i++) { | |
3374 if (!strcasecmp | |
3375 (tv_norms[tv_available_norms[i]].name, (char *) arg)) { | |
3376 *(int *) arg = i + 1; | |
3377 return TVI_CONTROL_TRUE; | |
3378 } | |
3379 } | |
3380 return TVI_CONTROL_FALSE; | |
3381 } | |
3382 case TVI_CONTROL_SPC_SET_INPUT: | |
3383 { | |
3384 return set_crossbar_input(priv, *(int *) arg); | |
3385 } | |
3386 case TVI_CONTROL_TUN_GET_FREQ: | |
3387 { | |
3388 unsigned long lFreq; | |
3389 int ret; | |
3390 if (!priv->pTVTuner) | |
3391 return TVI_CONTROL_FALSE; | |
3392 | |
3393 ret = get_frequency(priv, &lFreq); | |
3394 lFreq = lFreq * 16 / 1000000; //convert from Hz to 1/16 MHz units | |
3395 | |
3396 *(unsigned long *) arg = lFreq; | |
3397 return ret; | |
3398 } | |
3399 case TVI_CONTROL_TUN_SET_FREQ: | |
3400 { | |
3401 unsigned long nFreq = *(unsigned long *) arg; | |
3402 if (!priv->pTVTuner) | |
3403 return TVI_CONTROL_FALSE; | |
3404 //convert to Hz | |
3405 nFreq = 1000000 * nFreq / 16; //convert from 1/16 MHz units to Hz | |
3406 return set_frequency(priv, nFreq); | |
3407 } | |
3408 case TVI_CONTROL_VID_SET_HUE: | |
3409 return set_control(priv, VideoProcAmp_Hue, *(int *) arg); | |
3410 case TVI_CONTROL_VID_GET_HUE: | |
3411 return get_control(priv, VideoProcAmp_Hue, (int *) arg); | |
3412 case TVI_CONTROL_VID_SET_CONTRAST: | |
3413 return set_control(priv, VideoProcAmp_Contrast, *(int *) arg); | |
3414 case TVI_CONTROL_VID_GET_CONTRAST: | |
3415 return get_control(priv, VideoProcAmp_Contrast, (int *) arg); | |
3416 case TVI_CONTROL_VID_SET_SATURATION: | |
3417 return set_control(priv, VideoProcAmp_Saturation, *(int *) arg); | |
3418 case TVI_CONTROL_VID_GET_SATURATION: | |
3419 return get_control(priv, VideoProcAmp_Saturation, (int *) arg); | |
3420 case TVI_CONTROL_VID_SET_BRIGHTNESS: | |
3421 return set_control(priv, VideoProcAmp_Brightness, *(int *) arg); | |
3422 case TVI_CONTROL_VID_GET_BRIGHTNESS: | |
3423 return get_control(priv, VideoProcAmp_Brightness, (int *) arg); | |
3424 | |
3425 case TVI_CONTROL_VID_GET_FPS: | |
3426 { | |
3427 VIDEOINFOHEADER *Vhdr; | |
3428 if (!priv->pmtVideo) | |
3429 return TVI_CONTROL_FALSE; | |
3430 if (!priv->pmtVideo->pbFormat) | |
3431 return TVI_CONTROL_FALSE; | |
3432 Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; | |
3433 *(float *) arg = | |
25028
2cae9470f53b
Fix FPS from bitrate calculation (was 8 times larger than real value).
voroshil
parents:
25019
diff
changeset
|
3434 (1.0 * Vhdr->dwBitRate) / (Vhdr->bmiHeader.biSizeImage * 8); |
24744 | 3435 return TVI_CONTROL_TRUE; |
3436 } | |
3437 case TVI_CONTROL_IMMEDIATE: | |
3438 priv->immediate_mode = 1; | |
3439 return TVI_CONTROL_TRUE; | |
3440 #ifdef HAVE_TV_TELETEXT | |
3441 case TVI_CONTROL_VBI_INIT: | |
3442 { | |
3443 void* ptr; | |
3444 ptr=&(priv->tsp); | |
3445 if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==TVI_CONTROL_TRUE) | |
3446 priv->priv_vbi=ptr; | |
3447 else | |
3448 priv->priv_vbi=NULL; | |
3449 return TVI_CONTROL_TRUE; | |
3450 } | |
3451 default: | |
3452 return teletext_control(priv->priv_vbi,cmd,arg); | |
3453 #endif | |
3454 } | |
3455 return (TVI_CONTROL_UNKNOWN); | |
3456 } |