Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Directshow capture interface | |
3 | * Copyright (c) 2010 Ramiro Polla | |
4 | * | |
5 | * This file is part of FFmpeg. | |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * FFmpeg is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with FFmpeg; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
f6fa7814 | 22 | #include "dshow_capture.h" |
2ba45a60 DM |
23 | #include "libavutil/parseutils.h" |
24 | #include "libavutil/pixdesc.h" | |
25 | #include "libavutil/opt.h" | |
26 | #include "libavformat/internal.h" | |
27 | #include "libavformat/riff.h" | |
28 | #include "avdevice.h" | |
2ba45a60 DM |
29 | #include "libavcodec/raw.h" |
30 | ||
31 | struct dshow_ctx { | |
32 | const AVClass *class; | |
33 | ||
34 | IGraphBuilder *graph; | |
35 | ||
36 | char *device_name[2]; | |
37 | int video_device_number; | |
38 | int audio_device_number; | |
39 | ||
40 | int list_options; | |
41 | int list_devices; | |
42 | int audio_buffer_size; | |
43 | ||
44 | IBaseFilter *device_filter[2]; | |
45 | IPin *device_pin[2]; | |
46 | libAVFilter *capture_filter[2]; | |
47 | libAVPin *capture_pin[2]; | |
48 | ||
49 | HANDLE mutex; | |
50 | HANDLE event[2]; /* event[0] is set by DirectShow | |
51 | * event[1] is set by callback() */ | |
52 | AVPacketList *pktl; | |
53 | ||
54 | int eof; | |
55 | ||
56 | int64_t curbufsize[2]; | |
57 | unsigned int video_frame_num; | |
58 | ||
59 | IMediaControl *control; | |
60 | IMediaEvent *media_event; | |
61 | ||
62 | enum AVPixelFormat pixel_format; | |
63 | enum AVCodecID video_codec_id; | |
64 | char *framerate; | |
65 | ||
66 | int requested_width; | |
67 | int requested_height; | |
68 | AVRational requested_framerate; | |
69 | ||
70 | int sample_rate; | |
71 | int sample_size; | |
72 | int channels; | |
73 | }; | |
74 | ||
75 | static enum AVPixelFormat dshow_pixfmt(DWORD biCompression, WORD biBitCount) | |
76 | { | |
77 | switch(biCompression) { | |
78 | case BI_BITFIELDS: | |
79 | case BI_RGB: | |
80 | switch(biBitCount) { /* 1-8 are untested */ | |
81 | case 1: | |
82 | return AV_PIX_FMT_MONOWHITE; | |
83 | case 4: | |
84 | return AV_PIX_FMT_RGB4; | |
85 | case 8: | |
86 | return AV_PIX_FMT_RGB8; | |
87 | case 16: | |
88 | return AV_PIX_FMT_RGB555; | |
89 | case 24: | |
90 | return AV_PIX_FMT_BGR24; | |
91 | case 32: | |
92 | return AV_PIX_FMT_0RGB32; | |
93 | } | |
94 | } | |
95 | return avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), biCompression); // all others | |
96 | } | |
97 | ||
98 | static int | |
99 | dshow_read_close(AVFormatContext *s) | |
100 | { | |
101 | struct dshow_ctx *ctx = s->priv_data; | |
102 | AVPacketList *pktl; | |
103 | ||
104 | if (ctx->control) { | |
105 | IMediaControl_Stop(ctx->control); | |
106 | IMediaControl_Release(ctx->control); | |
107 | } | |
108 | ||
109 | if (ctx->media_event) | |
110 | IMediaEvent_Release(ctx->media_event); | |
111 | ||
112 | if (ctx->graph) { | |
113 | IEnumFilters *fenum; | |
114 | int r; | |
115 | r = IGraphBuilder_EnumFilters(ctx->graph, &fenum); | |
116 | if (r == S_OK) { | |
117 | IBaseFilter *f; | |
118 | IEnumFilters_Reset(fenum); | |
119 | while (IEnumFilters_Next(fenum, 1, &f, NULL) == S_OK) { | |
120 | if (IGraphBuilder_RemoveFilter(ctx->graph, f) == S_OK) | |
121 | IEnumFilters_Reset(fenum); /* When a filter is removed, | |
122 | * the list must be reset. */ | |
123 | IBaseFilter_Release(f); | |
124 | } | |
125 | IEnumFilters_Release(fenum); | |
126 | } | |
127 | IGraphBuilder_Release(ctx->graph); | |
128 | } | |
129 | ||
130 | if (ctx->capture_pin[VideoDevice]) | |
131 | libAVPin_Release(ctx->capture_pin[VideoDevice]); | |
132 | if (ctx->capture_pin[AudioDevice]) | |
133 | libAVPin_Release(ctx->capture_pin[AudioDevice]); | |
134 | if (ctx->capture_filter[VideoDevice]) | |
135 | libAVFilter_Release(ctx->capture_filter[VideoDevice]); | |
136 | if (ctx->capture_filter[AudioDevice]) | |
137 | libAVFilter_Release(ctx->capture_filter[AudioDevice]); | |
138 | ||
139 | if (ctx->device_pin[VideoDevice]) | |
140 | IPin_Release(ctx->device_pin[VideoDevice]); | |
141 | if (ctx->device_pin[AudioDevice]) | |
142 | IPin_Release(ctx->device_pin[AudioDevice]); | |
143 | if (ctx->device_filter[VideoDevice]) | |
144 | IBaseFilter_Release(ctx->device_filter[VideoDevice]); | |
145 | if (ctx->device_filter[AudioDevice]) | |
146 | IBaseFilter_Release(ctx->device_filter[AudioDevice]); | |
147 | ||
148 | if (ctx->device_name[0]) | |
149 | av_free(ctx->device_name[0]); | |
150 | if (ctx->device_name[1]) | |
151 | av_free(ctx->device_name[1]); | |
152 | ||
153 | if(ctx->mutex) | |
154 | CloseHandle(ctx->mutex); | |
155 | if(ctx->event[0]) | |
156 | CloseHandle(ctx->event[0]); | |
157 | if(ctx->event[1]) | |
158 | CloseHandle(ctx->event[1]); | |
159 | ||
160 | pktl = ctx->pktl; | |
161 | while (pktl) { | |
162 | AVPacketList *next = pktl->next; | |
163 | av_destruct_packet(&pktl->pkt); | |
164 | av_free(pktl); | |
165 | pktl = next; | |
166 | } | |
167 | ||
168 | CoUninitialize(); | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static char *dup_wchar_to_utf8(wchar_t *w) | |
174 | { | |
175 | char *s = NULL; | |
176 | int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0); | |
177 | s = av_malloc(l); | |
178 | if (s) | |
179 | WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0); | |
180 | return s; | |
181 | } | |
182 | ||
183 | static int shall_we_drop(AVFormatContext *s, int index, enum dshowDeviceType devtype) | |
184 | { | |
185 | struct dshow_ctx *ctx = s->priv_data; | |
186 | static const uint8_t dropscore[] = {62, 75, 87, 100}; | |
187 | const int ndropscores = FF_ARRAY_ELEMS(dropscore); | |
188 | unsigned int buffer_fullness = (ctx->curbufsize[index]*100)/s->max_picture_buffer; | |
189 | ||
190 | if(dropscore[++ctx->video_frame_num%ndropscores] <= buffer_fullness) { | |
191 | av_log(s, AV_LOG_ERROR, | |
192 | "real-time buffer[%s] too full (%d%% of size: %d)! frame dropped!\n", ctx->device_name[devtype], buffer_fullness, s->max_picture_buffer); | |
193 | return 1; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | static void | |
200 | callback(void *priv_data, int index, uint8_t *buf, int buf_size, int64_t time, enum dshowDeviceType devtype) | |
201 | { | |
202 | AVFormatContext *s = priv_data; | |
203 | struct dshow_ctx *ctx = s->priv_data; | |
204 | AVPacketList **ppktl, *pktl_next; | |
205 | ||
206 | // dump_videohdr(s, vdhdr); | |
207 | ||
208 | WaitForSingleObject(ctx->mutex, INFINITE); | |
209 | ||
210 | if(shall_we_drop(s, index, devtype)) | |
211 | goto fail; | |
212 | ||
213 | pktl_next = av_mallocz(sizeof(AVPacketList)); | |
214 | if(!pktl_next) | |
215 | goto fail; | |
216 | ||
217 | if(av_new_packet(&pktl_next->pkt, buf_size) < 0) { | |
218 | av_free(pktl_next); | |
219 | goto fail; | |
220 | } | |
221 | ||
222 | pktl_next->pkt.stream_index = index; | |
223 | pktl_next->pkt.pts = time; | |
224 | memcpy(pktl_next->pkt.data, buf, buf_size); | |
225 | ||
226 | for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next); | |
227 | *ppktl = pktl_next; | |
228 | ctx->curbufsize[index] += buf_size; | |
229 | ||
230 | SetEvent(ctx->event[1]); | |
231 | ReleaseMutex(ctx->mutex); | |
232 | ||
233 | return; | |
234 | fail: | |
235 | ReleaseMutex(ctx->mutex); | |
236 | return; | |
237 | } | |
238 | ||
239 | /** | |
240 | * Cycle through available devices using the device enumerator devenum, | |
241 | * retrieve the device with type specified by devtype and return the | |
242 | * pointer to the object found in *pfilter. | |
243 | * If pfilter is NULL, list all device names. | |
244 | */ | |
245 | static int | |
246 | dshow_cycle_devices(AVFormatContext *avctx, ICreateDevEnum *devenum, | |
247 | enum dshowDeviceType devtype, IBaseFilter **pfilter) | |
248 | { | |
249 | struct dshow_ctx *ctx = avctx->priv_data; | |
250 | IBaseFilter *device_filter = NULL; | |
251 | IEnumMoniker *classenum = NULL; | |
252 | IMoniker *m = NULL; | |
253 | const char *device_name = ctx->device_name[devtype]; | |
254 | int skip = (devtype == VideoDevice) ? ctx->video_device_number | |
255 | : ctx->audio_device_number; | |
256 | int r; | |
257 | ||
258 | const GUID *device_guid[2] = { &CLSID_VideoInputDeviceCategory, | |
259 | &CLSID_AudioInputDeviceCategory }; | |
260 | const char *devtypename = (devtype == VideoDevice) ? "video" : "audio"; | |
261 | ||
262 | r = ICreateDevEnum_CreateClassEnumerator(devenum, device_guid[devtype], | |
263 | (IEnumMoniker **) &classenum, 0); | |
264 | if (r != S_OK) { | |
265 | av_log(avctx, AV_LOG_ERROR, "Could not enumerate %s devices.\n", | |
266 | devtypename); | |
267 | return AVERROR(EIO); | |
268 | } | |
269 | ||
270 | while (!device_filter && IEnumMoniker_Next(classenum, 1, &m, NULL) == S_OK) { | |
271 | IPropertyBag *bag = NULL; | |
272 | char *buf = NULL; | |
273 | VARIANT var; | |
274 | ||
275 | r = IMoniker_BindToStorage(m, 0, 0, &IID_IPropertyBag, (void *) &bag); | |
276 | if (r != S_OK) | |
277 | goto fail1; | |
278 | ||
279 | var.vt = VT_BSTR; | |
280 | r = IPropertyBag_Read(bag, L"FriendlyName", &var, NULL); | |
281 | if (r != S_OK) | |
282 | goto fail1; | |
283 | ||
284 | buf = dup_wchar_to_utf8(var.bstrVal); | |
285 | ||
286 | if (pfilter) { | |
287 | if (strcmp(device_name, buf)) | |
288 | goto fail1; | |
289 | ||
290 | if (!skip--) | |
291 | IMoniker_BindToObject(m, 0, 0, &IID_IBaseFilter, (void *) &device_filter); | |
292 | } else { | |
293 | av_log(avctx, AV_LOG_INFO, " \"%s\"\n", buf); | |
294 | } | |
295 | ||
296 | fail1: | |
297 | if (buf) | |
298 | av_free(buf); | |
299 | if (bag) | |
300 | IPropertyBag_Release(bag); | |
301 | IMoniker_Release(m); | |
302 | } | |
303 | ||
304 | IEnumMoniker_Release(classenum); | |
305 | ||
306 | if (pfilter) { | |
307 | if (!device_filter) { | |
308 | av_log(avctx, AV_LOG_ERROR, "Could not find %s device.\n", | |
309 | devtypename); | |
310 | return AVERROR(EIO); | |
311 | } | |
312 | *pfilter = device_filter; | |
313 | } | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | /** | |
319 | * Cycle through available formats using the specified pin, | |
320 | * try to set parameters specified through AVOptions and if successful | |
321 | * return 1 in *pformat_set. | |
322 | * If pformat_set is NULL, list all pin capabilities. | |
323 | */ | |
324 | static void | |
325 | dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype, | |
326 | IPin *pin, int *pformat_set) | |
327 | { | |
328 | struct dshow_ctx *ctx = avctx->priv_data; | |
329 | IAMStreamConfig *config = NULL; | |
330 | AM_MEDIA_TYPE *type = NULL; | |
331 | int format_set = 0; | |
332 | void *caps = NULL; | |
333 | int i, n, size; | |
334 | ||
335 | if (IPin_QueryInterface(pin, &IID_IAMStreamConfig, (void **) &config) != S_OK) | |
336 | return; | |
337 | if (IAMStreamConfig_GetNumberOfCapabilities(config, &n, &size) != S_OK) | |
338 | goto end; | |
339 | ||
340 | caps = av_malloc(size); | |
341 | if (!caps) | |
342 | goto end; | |
343 | ||
344 | for (i = 0; i < n && !format_set; i++) { | |
345 | IAMStreamConfig_GetStreamCaps(config, i, &type, (void *) caps); | |
346 | ||
347 | #if DSHOWDEBUG | |
348 | ff_print_AM_MEDIA_TYPE(type); | |
349 | #endif | |
350 | ||
351 | if (devtype == VideoDevice) { | |
352 | VIDEO_STREAM_CONFIG_CAPS *vcaps = caps; | |
353 | BITMAPINFOHEADER *bih; | |
354 | int64_t *fr; | |
355 | const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL }; | |
356 | #if DSHOWDEBUG | |
357 | ff_print_VIDEO_STREAM_CONFIG_CAPS(vcaps); | |
358 | #endif | |
359 | if (IsEqualGUID(&type->formattype, &FORMAT_VideoInfo)) { | |
360 | VIDEOINFOHEADER *v = (void *) type->pbFormat; | |
361 | fr = &v->AvgTimePerFrame; | |
362 | bih = &v->bmiHeader; | |
363 | } else if (IsEqualGUID(&type->formattype, &FORMAT_VideoInfo2)) { | |
364 | VIDEOINFOHEADER2 *v = (void *) type->pbFormat; | |
365 | fr = &v->AvgTimePerFrame; | |
366 | bih = &v->bmiHeader; | |
367 | } else { | |
368 | goto next; | |
369 | } | |
370 | if (!pformat_set) { | |
371 | enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount); | |
372 | if (pix_fmt == AV_PIX_FMT_NONE) { | |
373 | enum AVCodecID codec_id = av_codec_get_id(tags, bih->biCompression); | |
374 | AVCodec *codec = avcodec_find_decoder(codec_id); | |
375 | if (codec_id == AV_CODEC_ID_NONE || !codec) { | |
376 | av_log(avctx, AV_LOG_INFO, " unknown compression type 0x%X", (int) bih->biCompression); | |
377 | } else { | |
378 | av_log(avctx, AV_LOG_INFO, " vcodec=%s", codec->name); | |
379 | } | |
380 | } else { | |
381 | av_log(avctx, AV_LOG_INFO, " pixel_format=%s", av_get_pix_fmt_name(pix_fmt)); | |
382 | } | |
383 | av_log(avctx, AV_LOG_INFO, " min s=%ldx%ld fps=%g max s=%ldx%ld fps=%g\n", | |
384 | vcaps->MinOutputSize.cx, vcaps->MinOutputSize.cy, | |
385 | 1e7 / vcaps->MaxFrameInterval, | |
386 | vcaps->MaxOutputSize.cx, vcaps->MaxOutputSize.cy, | |
387 | 1e7 / vcaps->MinFrameInterval); | |
388 | continue; | |
389 | } | |
390 | if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) { | |
391 | if (ctx->video_codec_id != av_codec_get_id(tags, bih->biCompression)) | |
392 | goto next; | |
393 | } | |
394 | if (ctx->pixel_format != AV_PIX_FMT_NONE && | |
395 | ctx->pixel_format != dshow_pixfmt(bih->biCompression, bih->biBitCount)) { | |
396 | goto next; | |
397 | } | |
398 | if (ctx->framerate) { | |
399 | int64_t framerate = ((int64_t) ctx->requested_framerate.den*10000000) | |
400 | / ctx->requested_framerate.num; | |
401 | if (framerate > vcaps->MaxFrameInterval || | |
402 | framerate < vcaps->MinFrameInterval) | |
403 | goto next; | |
404 | *fr = framerate; | |
405 | } | |
406 | if (ctx->requested_width && ctx->requested_height) { | |
407 | if (ctx->requested_width > vcaps->MaxOutputSize.cx || | |
408 | ctx->requested_width < vcaps->MinOutputSize.cx || | |
409 | ctx->requested_height > vcaps->MaxOutputSize.cy || | |
410 | ctx->requested_height < vcaps->MinOutputSize.cy) | |
411 | goto next; | |
412 | bih->biWidth = ctx->requested_width; | |
413 | bih->biHeight = ctx->requested_height; | |
414 | } | |
415 | } else { | |
416 | AUDIO_STREAM_CONFIG_CAPS *acaps = caps; | |
417 | WAVEFORMATEX *fx; | |
418 | #if DSHOWDEBUG | |
419 | ff_print_AUDIO_STREAM_CONFIG_CAPS(acaps); | |
420 | #endif | |
421 | if (IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx)) { | |
422 | fx = (void *) type->pbFormat; | |
423 | } else { | |
424 | goto next; | |
425 | } | |
426 | if (!pformat_set) { | |
427 | av_log(avctx, AV_LOG_INFO, " min ch=%lu bits=%lu rate=%6lu max ch=%lu bits=%lu rate=%6lu\n", | |
428 | acaps->MinimumChannels, acaps->MinimumBitsPerSample, acaps->MinimumSampleFrequency, | |
429 | acaps->MaximumChannels, acaps->MaximumBitsPerSample, acaps->MaximumSampleFrequency); | |
430 | continue; | |
431 | } | |
432 | if (ctx->sample_rate) { | |
433 | if (ctx->sample_rate > acaps->MaximumSampleFrequency || | |
434 | ctx->sample_rate < acaps->MinimumSampleFrequency) | |
435 | goto next; | |
436 | fx->nSamplesPerSec = ctx->sample_rate; | |
437 | } | |
438 | if (ctx->sample_size) { | |
439 | if (ctx->sample_size > acaps->MaximumBitsPerSample || | |
440 | ctx->sample_size < acaps->MinimumBitsPerSample) | |
441 | goto next; | |
442 | fx->wBitsPerSample = ctx->sample_size; | |
443 | } | |
444 | if (ctx->channels) { | |
445 | if (ctx->channels > acaps->MaximumChannels || | |
446 | ctx->channels < acaps->MinimumChannels) | |
447 | goto next; | |
448 | fx->nChannels = ctx->channels; | |
449 | } | |
450 | } | |
451 | if (IAMStreamConfig_SetFormat(config, type) != S_OK) | |
452 | goto next; | |
453 | format_set = 1; | |
454 | next: | |
455 | if (type->pbFormat) | |
456 | CoTaskMemFree(type->pbFormat); | |
457 | CoTaskMemFree(type); | |
458 | } | |
459 | end: | |
460 | IAMStreamConfig_Release(config); | |
461 | if (caps) | |
462 | av_free(caps); | |
463 | if (pformat_set) | |
464 | *pformat_set = format_set; | |
465 | } | |
466 | ||
467 | /** | |
468 | * Set audio device buffer size in milliseconds (which can directly impact | |
469 | * latency, depending on the device). | |
470 | */ | |
471 | static int | |
472 | dshow_set_audio_buffer_size(AVFormatContext *avctx, IPin *pin) | |
473 | { | |
474 | struct dshow_ctx *ctx = avctx->priv_data; | |
475 | IAMBufferNegotiation *buffer_negotiation = NULL; | |
476 | ALLOCATOR_PROPERTIES props = { -1, -1, -1, -1 }; | |
477 | IAMStreamConfig *config = NULL; | |
478 | AM_MEDIA_TYPE *type = NULL; | |
479 | int ret = AVERROR(EIO); | |
480 | ||
481 | if (IPin_QueryInterface(pin, &IID_IAMStreamConfig, (void **) &config) != S_OK) | |
482 | goto end; | |
483 | if (IAMStreamConfig_GetFormat(config, &type) != S_OK) | |
484 | goto end; | |
485 | if (!IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx)) | |
486 | goto end; | |
487 | ||
488 | props.cbBuffer = (((WAVEFORMATEX *) type->pbFormat)->nAvgBytesPerSec) | |
489 | * ctx->audio_buffer_size / 1000; | |
490 | ||
491 | if (IPin_QueryInterface(pin, &IID_IAMBufferNegotiation, (void **) &buffer_negotiation) != S_OK) | |
492 | goto end; | |
493 | if (IAMBufferNegotiation_SuggestAllocatorProperties(buffer_negotiation, &props) != S_OK) | |
494 | goto end; | |
495 | ||
496 | ret = 0; | |
497 | ||
498 | end: | |
499 | if (buffer_negotiation) | |
500 | IAMBufferNegotiation_Release(buffer_negotiation); | |
501 | if (type) { | |
502 | if (type->pbFormat) | |
503 | CoTaskMemFree(type->pbFormat); | |
504 | CoTaskMemFree(type); | |
505 | } | |
506 | if (config) | |
507 | IAMStreamConfig_Release(config); | |
508 | ||
509 | return ret; | |
510 | } | |
511 | ||
512 | /** | |
513 | * Cycle through available pins using the device_filter device, of type | |
514 | * devtype, retrieve the first output pin and return the pointer to the | |
515 | * object found in *ppin. | |
516 | * If ppin is NULL, cycle through all pins listing audio/video capabilities. | |
517 | */ | |
518 | static int | |
519 | dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype, | |
520 | IBaseFilter *device_filter, IPin **ppin) | |
521 | { | |
522 | struct dshow_ctx *ctx = avctx->priv_data; | |
523 | IEnumPins *pins = 0; | |
524 | IPin *device_pin = NULL; | |
525 | IPin *pin; | |
526 | int r; | |
527 | ||
528 | const GUID *mediatype[2] = { &MEDIATYPE_Video, &MEDIATYPE_Audio }; | |
529 | const char *devtypename = (devtype == VideoDevice) ? "video" : "audio"; | |
530 | ||
531 | int set_format = (devtype == VideoDevice && (ctx->framerate || | |
532 | (ctx->requested_width && ctx->requested_height) || | |
533 | ctx->pixel_format != AV_PIX_FMT_NONE || | |
534 | ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO)) | |
535 | || (devtype == AudioDevice && (ctx->channels || ctx->sample_rate)); | |
536 | int format_set = 0; | |
537 | ||
538 | r = IBaseFilter_EnumPins(device_filter, &pins); | |
539 | if (r != S_OK) { | |
540 | av_log(avctx, AV_LOG_ERROR, "Could not enumerate pins.\n"); | |
541 | return AVERROR(EIO); | |
542 | } | |
543 | ||
544 | if (!ppin) { | |
545 | av_log(avctx, AV_LOG_INFO, "DirectShow %s device options\n", | |
546 | devtypename); | |
547 | } | |
548 | while (!device_pin && IEnumPins_Next(pins, 1, &pin, NULL) == S_OK) { | |
549 | IKsPropertySet *p = NULL; | |
550 | IEnumMediaTypes *types = NULL; | |
551 | PIN_INFO info = {0}; | |
552 | AM_MEDIA_TYPE *type; | |
553 | GUID category; | |
554 | DWORD r2; | |
555 | ||
556 | IPin_QueryPinInfo(pin, &info); | |
557 | IBaseFilter_Release(info.pFilter); | |
558 | ||
559 | if (info.dir != PINDIR_OUTPUT) | |
560 | goto next; | |
561 | if (IPin_QueryInterface(pin, &IID_IKsPropertySet, (void **) &p) != S_OK) | |
562 | goto next; | |
563 | if (IKsPropertySet_Get(p, &ROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, | |
564 | NULL, 0, &category, sizeof(GUID), &r2) != S_OK) | |
565 | goto next; | |
566 | if (!IsEqualGUID(&category, &PIN_CATEGORY_CAPTURE)) | |
567 | goto next; | |
568 | ||
569 | if (!ppin) { | |
570 | char *buf = dup_wchar_to_utf8(info.achName); | |
571 | av_log(avctx, AV_LOG_INFO, " Pin \"%s\"\n", buf); | |
572 | av_free(buf); | |
573 | dshow_cycle_formats(avctx, devtype, pin, NULL); | |
574 | goto next; | |
575 | } | |
576 | if (set_format) { | |
577 | dshow_cycle_formats(avctx, devtype, pin, &format_set); | |
578 | if (!format_set) { | |
579 | goto next; | |
580 | } | |
581 | } | |
582 | if (devtype == AudioDevice && ctx->audio_buffer_size) { | |
583 | if (dshow_set_audio_buffer_size(avctx, pin) < 0) { | |
584 | av_log(avctx, AV_LOG_ERROR, "unable to set audio buffer size %d to pin, using pin anyway...", ctx->audio_buffer_size); | |
585 | } | |
586 | } | |
587 | ||
588 | if (IPin_EnumMediaTypes(pin, &types) != S_OK) | |
589 | goto next; | |
590 | ||
591 | IEnumMediaTypes_Reset(types); | |
592 | while (!device_pin && IEnumMediaTypes_Next(types, 1, &type, NULL) == S_OK) { | |
593 | if (IsEqualGUID(&type->majortype, mediatype[devtype])) { | |
594 | device_pin = pin; | |
595 | goto next; | |
596 | } | |
597 | CoTaskMemFree(type); | |
598 | } | |
599 | ||
600 | next: | |
601 | if (types) | |
602 | IEnumMediaTypes_Release(types); | |
603 | if (p) | |
604 | IKsPropertySet_Release(p); | |
605 | if (device_pin != pin) | |
606 | IPin_Release(pin); | |
607 | } | |
608 | ||
609 | IEnumPins_Release(pins); | |
610 | ||
611 | if (ppin) { | |
612 | if (set_format && !format_set) { | |
613 | av_log(avctx, AV_LOG_ERROR, "Could not set %s options\n", devtypename); | |
614 | return AVERROR(EIO); | |
615 | } | |
616 | if (!device_pin) { | |
617 | av_log(avctx, AV_LOG_ERROR, | |
618 | "Could not find output pin from %s capture device.\n", devtypename); | |
619 | return AVERROR(EIO); | |
620 | } | |
621 | *ppin = device_pin; | |
622 | } | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
627 | /** | |
628 | * List options for device with type devtype. | |
629 | * | |
630 | * @param devenum device enumerator used for accessing the device | |
631 | */ | |
632 | static int | |
633 | dshow_list_device_options(AVFormatContext *avctx, ICreateDevEnum *devenum, | |
634 | enum dshowDeviceType devtype) | |
635 | { | |
636 | struct dshow_ctx *ctx = avctx->priv_data; | |
637 | IBaseFilter *device_filter = NULL; | |
638 | int r; | |
639 | ||
640 | if ((r = dshow_cycle_devices(avctx, devenum, devtype, &device_filter)) < 0) | |
641 | return r; | |
642 | ctx->device_filter[devtype] = device_filter; | |
643 | if ((r = dshow_cycle_pins(avctx, devtype, device_filter, NULL)) < 0) | |
644 | return r; | |
645 | ||
646 | return 0; | |
647 | } | |
648 | ||
649 | static int | |
650 | dshow_open_device(AVFormatContext *avctx, ICreateDevEnum *devenum, | |
651 | enum dshowDeviceType devtype) | |
652 | { | |
653 | struct dshow_ctx *ctx = avctx->priv_data; | |
654 | IBaseFilter *device_filter = NULL; | |
655 | IGraphBuilder *graph = ctx->graph; | |
656 | IPin *device_pin = NULL; | |
657 | libAVPin *capture_pin = NULL; | |
658 | libAVFilter *capture_filter = NULL; | |
659 | int ret = AVERROR(EIO); | |
660 | int r; | |
661 | ||
662 | const wchar_t *filter_name[2] = { L"Audio capture filter", L"Video capture filter" }; | |
663 | ||
664 | if ((r = dshow_cycle_devices(avctx, devenum, devtype, &device_filter)) < 0) { | |
665 | ret = r; | |
666 | goto error; | |
667 | } | |
668 | ||
669 | ctx->device_filter [devtype] = device_filter; | |
670 | ||
671 | r = IGraphBuilder_AddFilter(graph, device_filter, NULL); | |
672 | if (r != S_OK) { | |
673 | av_log(avctx, AV_LOG_ERROR, "Could not add device filter to graph.\n"); | |
674 | goto error; | |
675 | } | |
676 | ||
677 | if ((r = dshow_cycle_pins(avctx, devtype, device_filter, &device_pin)) < 0) { | |
678 | ret = r; | |
679 | goto error; | |
680 | } | |
681 | ctx->device_pin[devtype] = device_pin; | |
682 | ||
683 | capture_filter = libAVFilter_Create(avctx, callback, devtype); | |
684 | if (!capture_filter) { | |
685 | av_log(avctx, AV_LOG_ERROR, "Could not create grabber filter.\n"); | |
686 | goto error; | |
687 | } | |
688 | ctx->capture_filter[devtype] = capture_filter; | |
689 | ||
690 | r = IGraphBuilder_AddFilter(graph, (IBaseFilter *) capture_filter, | |
691 | filter_name[devtype]); | |
692 | if (r != S_OK) { | |
693 | av_log(avctx, AV_LOG_ERROR, "Could not add capture filter to graph\n"); | |
694 | goto error; | |
695 | } | |
696 | ||
697 | libAVPin_AddRef(capture_filter->pin); | |
698 | capture_pin = capture_filter->pin; | |
699 | ctx->capture_pin[devtype] = capture_pin; | |
700 | ||
701 | r = IGraphBuilder_ConnectDirect(graph, device_pin, (IPin *) capture_pin, NULL); | |
702 | if (r != S_OK) { | |
703 | av_log(avctx, AV_LOG_ERROR, "Could not connect pins\n"); | |
704 | goto error; | |
705 | } | |
706 | ||
707 | ret = 0; | |
708 | ||
709 | error: | |
710 | return ret; | |
711 | } | |
712 | ||
713 | static enum AVCodecID waveform_codec_id(enum AVSampleFormat sample_fmt) | |
714 | { | |
715 | switch (sample_fmt) { | |
716 | case AV_SAMPLE_FMT_U8: return AV_CODEC_ID_PCM_U8; | |
717 | case AV_SAMPLE_FMT_S16: return AV_CODEC_ID_PCM_S16LE; | |
718 | case AV_SAMPLE_FMT_S32: return AV_CODEC_ID_PCM_S32LE; | |
719 | default: return AV_CODEC_ID_NONE; /* Should never happen. */ | |
720 | } | |
721 | } | |
722 | ||
723 | static enum AVSampleFormat sample_fmt_bits_per_sample(int bits) | |
724 | { | |
725 | switch (bits) { | |
726 | case 8: return AV_SAMPLE_FMT_U8; | |
727 | case 16: return AV_SAMPLE_FMT_S16; | |
728 | case 32: return AV_SAMPLE_FMT_S32; | |
729 | default: return AV_SAMPLE_FMT_NONE; /* Should never happen. */ | |
730 | } | |
731 | } | |
732 | ||
733 | static int | |
734 | dshow_add_device(AVFormatContext *avctx, | |
735 | enum dshowDeviceType devtype) | |
736 | { | |
737 | struct dshow_ctx *ctx = avctx->priv_data; | |
738 | AM_MEDIA_TYPE type; | |
739 | AVCodecContext *codec; | |
740 | AVStream *st; | |
741 | int ret = AVERROR(EIO); | |
742 | ||
743 | st = avformat_new_stream(avctx, NULL); | |
744 | if (!st) { | |
745 | ret = AVERROR(ENOMEM); | |
746 | goto error; | |
747 | } | |
748 | st->id = devtype; | |
749 | ||
750 | ctx->capture_filter[devtype]->stream_index = st->index; | |
751 | ||
752 | libAVPin_ConnectionMediaType(ctx->capture_pin[devtype], &type); | |
753 | ||
754 | codec = st->codec; | |
755 | if (devtype == VideoDevice) { | |
756 | BITMAPINFOHEADER *bih = NULL; | |
757 | AVRational time_base; | |
758 | ||
759 | if (IsEqualGUID(&type.formattype, &FORMAT_VideoInfo)) { | |
760 | VIDEOINFOHEADER *v = (void *) type.pbFormat; | |
761 | time_base = (AVRational) { v->AvgTimePerFrame, 10000000 }; | |
762 | bih = &v->bmiHeader; | |
763 | } else if (IsEqualGUID(&type.formattype, &FORMAT_VideoInfo2)) { | |
764 | VIDEOINFOHEADER2 *v = (void *) type.pbFormat; | |
765 | time_base = (AVRational) { v->AvgTimePerFrame, 10000000 }; | |
766 | bih = &v->bmiHeader; | |
767 | } | |
768 | if (!bih) { | |
769 | av_log(avctx, AV_LOG_ERROR, "Could not get media type.\n"); | |
770 | goto error; | |
771 | } | |
772 | ||
773 | codec->time_base = time_base; | |
774 | codec->codec_type = AVMEDIA_TYPE_VIDEO; | |
775 | codec->width = bih->biWidth; | |
776 | codec->height = bih->biHeight; | |
777 | codec->codec_tag = bih->biCompression; | |
778 | codec->pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount); | |
779 | if (bih->biCompression == MKTAG('H', 'D', 'Y', 'C')) { | |
780 | av_log(avctx, AV_LOG_DEBUG, "attempt to use full range for HDYC...\n"); | |
781 | codec->color_range = AVCOL_RANGE_MPEG; // just in case it needs this... | |
782 | } | |
783 | if (codec->pix_fmt == AV_PIX_FMT_NONE) { | |
784 | const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL }; | |
785 | codec->codec_id = av_codec_get_id(tags, bih->biCompression); | |
786 | if (codec->codec_id == AV_CODEC_ID_NONE) { | |
787 | av_log(avctx, AV_LOG_ERROR, "Unknown compression type. " | |
788 | "Please report type 0x%X.\n", (int) bih->biCompression); | |
789 | return AVERROR_PATCHWELCOME; | |
790 | } | |
791 | codec->bits_per_coded_sample = bih->biBitCount; | |
792 | } else { | |
793 | codec->codec_id = AV_CODEC_ID_RAWVIDEO; | |
794 | if (bih->biCompression == BI_RGB || bih->biCompression == BI_BITFIELDS) { | |
795 | codec->bits_per_coded_sample = bih->biBitCount; | |
796 | codec->extradata = av_malloc(9 + FF_INPUT_BUFFER_PADDING_SIZE); | |
797 | if (codec->extradata) { | |
798 | codec->extradata_size = 9; | |
799 | memcpy(codec->extradata, "BottomUp", 9); | |
800 | } | |
801 | } | |
802 | } | |
803 | } else { | |
804 | WAVEFORMATEX *fx = NULL; | |
805 | ||
806 | if (IsEqualGUID(&type.formattype, &FORMAT_WaveFormatEx)) { | |
807 | fx = (void *) type.pbFormat; | |
808 | } | |
809 | if (!fx) { | |
810 | av_log(avctx, AV_LOG_ERROR, "Could not get media type.\n"); | |
811 | goto error; | |
812 | } | |
813 | ||
814 | codec->codec_type = AVMEDIA_TYPE_AUDIO; | |
815 | codec->sample_fmt = sample_fmt_bits_per_sample(fx->wBitsPerSample); | |
816 | codec->codec_id = waveform_codec_id(codec->sample_fmt); | |
817 | codec->sample_rate = fx->nSamplesPerSec; | |
818 | codec->channels = fx->nChannels; | |
819 | } | |
820 | ||
821 | avpriv_set_pts_info(st, 64, 1, 10000000); | |
822 | ||
823 | ret = 0; | |
824 | ||
825 | error: | |
826 | return ret; | |
827 | } | |
828 | ||
829 | static int parse_device_name(AVFormatContext *avctx) | |
830 | { | |
831 | struct dshow_ctx *ctx = avctx->priv_data; | |
832 | char **device_name = ctx->device_name; | |
833 | char *name = av_strdup(avctx->filename); | |
834 | char *tmp = name; | |
835 | int ret = 1; | |
836 | char *type; | |
837 | ||
838 | while ((type = strtok(tmp, "="))) { | |
839 | char *token = strtok(NULL, ":"); | |
840 | tmp = NULL; | |
841 | ||
842 | if (!strcmp(type, "video")) { | |
843 | device_name[0] = token; | |
844 | } else if (!strcmp(type, "audio")) { | |
845 | device_name[1] = token; | |
846 | } else { | |
847 | device_name[0] = NULL; | |
848 | device_name[1] = NULL; | |
849 | break; | |
850 | } | |
851 | } | |
852 | ||
853 | if (!device_name[0] && !device_name[1]) { | |
854 | ret = 0; | |
855 | } else { | |
856 | if (device_name[0]) | |
857 | device_name[0] = av_strdup(device_name[0]); | |
858 | if (device_name[1]) | |
859 | device_name[1] = av_strdup(device_name[1]); | |
860 | } | |
861 | ||
862 | av_free(name); | |
863 | return ret; | |
864 | } | |
865 | ||
866 | static int dshow_read_header(AVFormatContext *avctx) | |
867 | { | |
868 | struct dshow_ctx *ctx = avctx->priv_data; | |
869 | IGraphBuilder *graph = NULL; | |
870 | ICreateDevEnum *devenum = NULL; | |
871 | IMediaControl *control = NULL; | |
872 | IMediaEvent *media_event = NULL; | |
873 | HANDLE media_event_handle; | |
874 | HANDLE proc; | |
875 | int ret = AVERROR(EIO); | |
876 | int r; | |
877 | ||
878 | CoInitialize(0); | |
879 | ||
880 | if (!ctx->list_devices && !parse_device_name(avctx)) { | |
881 | av_log(avctx, AV_LOG_ERROR, "Malformed dshow input string.\n"); | |
882 | goto error; | |
883 | } | |
884 | ||
885 | ctx->video_codec_id = avctx->video_codec_id ? avctx->video_codec_id | |
886 | : AV_CODEC_ID_RAWVIDEO; | |
887 | if (ctx->pixel_format != AV_PIX_FMT_NONE) { | |
888 | if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) { | |
889 | av_log(avctx, AV_LOG_ERROR, "Pixel format may only be set when " | |
890 | "video codec is not set or set to rawvideo\n"); | |
891 | ret = AVERROR(EINVAL); | |
892 | goto error; | |
893 | } | |
894 | } | |
895 | if (ctx->framerate) { | |
896 | r = av_parse_video_rate(&ctx->requested_framerate, ctx->framerate); | |
897 | if (r < 0) { | |
898 | av_log(avctx, AV_LOG_ERROR, "Could not parse framerate '%s'.\n", ctx->framerate); | |
899 | goto error; | |
900 | } | |
901 | } | |
902 | ||
903 | r = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, | |
904 | &IID_IGraphBuilder, (void **) &graph); | |
905 | if (r != S_OK) { | |
906 | av_log(avctx, AV_LOG_ERROR, "Could not create capture graph.\n"); | |
907 | goto error; | |
908 | } | |
909 | ctx->graph = graph; | |
910 | ||
911 | r = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, | |
912 | &IID_ICreateDevEnum, (void **) &devenum); | |
913 | if (r != S_OK) { | |
914 | av_log(avctx, AV_LOG_ERROR, "Could not enumerate system devices.\n"); | |
915 | goto error; | |
916 | } | |
917 | ||
918 | if (ctx->list_devices) { | |
919 | av_log(avctx, AV_LOG_INFO, "DirectShow video devices\n"); | |
920 | dshow_cycle_devices(avctx, devenum, VideoDevice, NULL); | |
921 | av_log(avctx, AV_LOG_INFO, "DirectShow audio devices\n"); | |
922 | dshow_cycle_devices(avctx, devenum, AudioDevice, NULL); | |
923 | ret = AVERROR_EXIT; | |
924 | goto error; | |
925 | } | |
926 | if (ctx->list_options) { | |
927 | if (ctx->device_name[VideoDevice]) | |
928 | dshow_list_device_options(avctx, devenum, VideoDevice); | |
929 | if (ctx->device_name[AudioDevice]) | |
930 | dshow_list_device_options(avctx, devenum, AudioDevice); | |
931 | ret = AVERROR_EXIT; | |
932 | goto error; | |
933 | } | |
934 | ||
935 | if (ctx->device_name[VideoDevice]) { | |
936 | if ((r = dshow_open_device(avctx, devenum, VideoDevice)) < 0 || | |
937 | (r = dshow_add_device(avctx, VideoDevice)) < 0) { | |
938 | ret = r; | |
939 | goto error; | |
940 | } | |
941 | } | |
942 | if (ctx->device_name[AudioDevice]) { | |
943 | if ((r = dshow_open_device(avctx, devenum, AudioDevice)) < 0 || | |
944 | (r = dshow_add_device(avctx, AudioDevice)) < 0) { | |
945 | ret = r; | |
946 | goto error; | |
947 | } | |
948 | } | |
949 | ctx->curbufsize[0] = 0; | |
950 | ctx->curbufsize[1] = 0; | |
951 | ctx->mutex = CreateMutex(NULL, 0, NULL); | |
952 | if (!ctx->mutex) { | |
953 | av_log(avctx, AV_LOG_ERROR, "Could not create Mutex\n"); | |
954 | goto error; | |
955 | } | |
956 | ctx->event[1] = CreateEvent(NULL, 1, 0, NULL); | |
957 | if (!ctx->event[1]) { | |
958 | av_log(avctx, AV_LOG_ERROR, "Could not create Event\n"); | |
959 | goto error; | |
960 | } | |
961 | ||
962 | r = IGraphBuilder_QueryInterface(graph, &IID_IMediaControl, (void **) &control); | |
963 | if (r != S_OK) { | |
964 | av_log(avctx, AV_LOG_ERROR, "Could not get media control.\n"); | |
965 | goto error; | |
966 | } | |
967 | ctx->control = control; | |
968 | ||
969 | r = IGraphBuilder_QueryInterface(graph, &IID_IMediaEvent, (void **) &media_event); | |
970 | if (r != S_OK) { | |
971 | av_log(avctx, AV_LOG_ERROR, "Could not get media event.\n"); | |
972 | goto error; | |
973 | } | |
974 | ctx->media_event = media_event; | |
975 | ||
976 | r = IMediaEvent_GetEventHandle(media_event, (void *) &media_event_handle); | |
977 | if (r != S_OK) { | |
978 | av_log(avctx, AV_LOG_ERROR, "Could not get media event handle.\n"); | |
979 | goto error; | |
980 | } | |
981 | proc = GetCurrentProcess(); | |
982 | r = DuplicateHandle(proc, media_event_handle, proc, &ctx->event[0], | |
983 | 0, 0, DUPLICATE_SAME_ACCESS); | |
984 | if (!r) { | |
985 | av_log(avctx, AV_LOG_ERROR, "Could not duplicate media event handle.\n"); | |
986 | goto error; | |
987 | } | |
988 | ||
989 | r = IMediaControl_Run(control); | |
990 | if (r == S_FALSE) { | |
991 | OAFilterState pfs; | |
992 | r = IMediaControl_GetState(control, 0, &pfs); | |
993 | } | |
994 | if (r != S_OK) { | |
995 | av_log(avctx, AV_LOG_ERROR, "Could not run filter\n"); | |
996 | goto error; | |
997 | } | |
998 | ||
999 | ret = 0; | |
1000 | ||
1001 | error: | |
1002 | ||
1003 | if (devenum) | |
1004 | ICreateDevEnum_Release(devenum); | |
1005 | ||
1006 | if (ret < 0) | |
1007 | dshow_read_close(avctx); | |
1008 | ||
1009 | return ret; | |
1010 | } | |
1011 | ||
1012 | /** | |
1013 | * Checks media events from DirectShow and returns -1 on error or EOF. Also | |
1014 | * purges all events that might be in the event queue to stop the trigger | |
1015 | * of event notification. | |
1016 | */ | |
1017 | static int dshow_check_event_queue(IMediaEvent *media_event) | |
1018 | { | |
1019 | LONG_PTR p1, p2; | |
1020 | long code; | |
1021 | int ret = 0; | |
1022 | ||
1023 | while (IMediaEvent_GetEvent(media_event, &code, &p1, &p2, 0) != E_ABORT) { | |
1024 | if (code == EC_COMPLETE || code == EC_DEVICE_LOST || code == EC_ERRORABORT) | |
1025 | ret = -1; | |
1026 | IMediaEvent_FreeEventParams(media_event, code, p1, p2); | |
1027 | } | |
1028 | ||
1029 | return ret; | |
1030 | } | |
1031 | ||
1032 | static int dshow_read_packet(AVFormatContext *s, AVPacket *pkt) | |
1033 | { | |
1034 | struct dshow_ctx *ctx = s->priv_data; | |
1035 | AVPacketList *pktl = NULL; | |
1036 | ||
1037 | while (!ctx->eof && !pktl) { | |
1038 | WaitForSingleObject(ctx->mutex, INFINITE); | |
1039 | pktl = ctx->pktl; | |
1040 | if (pktl) { | |
1041 | *pkt = pktl->pkt; | |
1042 | ctx->pktl = ctx->pktl->next; | |
1043 | av_free(pktl); | |
1044 | ctx->curbufsize[pkt->stream_index] -= pkt->size; | |
1045 | } | |
1046 | ResetEvent(ctx->event[1]); | |
1047 | ReleaseMutex(ctx->mutex); | |
1048 | if (!pktl) { | |
1049 | if (dshow_check_event_queue(ctx->media_event) < 0) { | |
1050 | ctx->eof = 1; | |
1051 | } else if (s->flags & AVFMT_FLAG_NONBLOCK) { | |
1052 | return AVERROR(EAGAIN); | |
1053 | } else { | |
1054 | WaitForMultipleObjects(2, ctx->event, 0, INFINITE); | |
1055 | } | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | return ctx->eof ? AVERROR(EIO) : pkt->size; | |
1060 | } | |
1061 | ||
1062 | #define OFFSET(x) offsetof(struct dshow_ctx, x) | |
1063 | #define DEC AV_OPT_FLAG_DECODING_PARAM | |
1064 | static const AVOption options[] = { | |
1065 | { "video_size", "set video size given a string such as 640x480 or hd720.", OFFSET(requested_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, | |
1066 | { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, -1, INT_MAX, DEC }, | |
1067 | { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, | |
1068 | { "sample_rate", "set audio sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, | |
1069 | { "sample_size", "set audio sample size", OFFSET(sample_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 16, DEC }, | |
1070 | { "channels", "set number of audio channels, such as 1 or 2", OFFSET(channels), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, | |
1071 | { "list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, DEC, "list_devices" }, | |
1072 | { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, DEC, "list_devices" }, | |
1073 | { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_devices" }, | |
1074 | { "list_options", "list available options for specified device", OFFSET(list_options), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, DEC, "list_options" }, | |
1075 | { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, DEC, "list_options" }, | |
1076 | { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_options" }, | |
1077 | { "video_device_number", "set video device number for devices with same name (starts at 0)", OFFSET(video_device_number), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, | |
1078 | { "audio_device_number", "set audio device number for devices with same name (starts at 0)", OFFSET(audio_device_number), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, | |
1079 | { "audio_buffer_size", "set audio device buffer latency size in milliseconds (default is the device's default)", OFFSET(audio_buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, | |
1080 | { NULL }, | |
1081 | }; | |
1082 | ||
1083 | static const AVClass dshow_class = { | |
1084 | .class_name = "dshow indev", | |
1085 | .item_name = av_default_item_name, | |
1086 | .option = options, | |
1087 | .version = LIBAVUTIL_VERSION_INT, | |
1088 | .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, | |
1089 | }; | |
1090 | ||
1091 | AVInputFormat ff_dshow_demuxer = { | |
1092 | .name = "dshow", | |
1093 | .long_name = NULL_IF_CONFIG_SMALL("DirectShow capture"), | |
1094 | .priv_data_size = sizeof(struct dshow_ctx), | |
1095 | .read_header = dshow_read_header, | |
1096 | .read_packet = dshow_read_packet, | |
1097 | .read_close = dshow_read_close, | |
1098 | .flags = AVFMT_NOFILE, | |
1099 | .priv_class = &dshow_class, | |
1100 | }; |