yading@10: /* yading@10: * Directshow capture interface yading@10: * Copyright (c) 2010 Ramiro Polla yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "libavutil/parseutils.h" yading@10: #include "libavutil/pixdesc.h" yading@10: #include "libavutil/opt.h" yading@10: #include "libavformat/internal.h" yading@10: #include "libavformat/riff.h" yading@10: #include "avdevice.h" yading@10: #include "dshow_capture.h" yading@10: #include "libavcodec/raw.h" yading@10: yading@10: struct dshow_ctx { yading@10: const AVClass *class; yading@10: yading@10: IGraphBuilder *graph; yading@10: yading@10: char *device_name[2]; yading@10: int video_device_number; yading@10: int audio_device_number; yading@10: yading@10: int list_options; yading@10: int list_devices; yading@10: int audio_buffer_size; yading@10: yading@10: IBaseFilter *device_filter[2]; yading@10: IPin *device_pin[2]; yading@10: libAVFilter *capture_filter[2]; yading@10: libAVPin *capture_pin[2]; yading@10: yading@10: HANDLE mutex; yading@10: HANDLE event[2]; /* event[0] is set by DirectShow yading@10: * event[1] is set by callback() */ yading@10: AVPacketList *pktl; yading@10: yading@10: int eof; yading@10: yading@10: int64_t curbufsize; yading@10: unsigned int video_frame_num; yading@10: yading@10: IMediaControl *control; yading@10: IMediaEvent *media_event; yading@10: yading@10: enum AVPixelFormat pixel_format; yading@10: enum AVCodecID video_codec_id; yading@10: char *framerate; yading@10: yading@10: int requested_width; yading@10: int requested_height; yading@10: AVRational requested_framerate; yading@10: yading@10: int sample_rate; yading@10: int sample_size; yading@10: int channels; yading@10: }; yading@10: yading@10: static enum AVPixelFormat dshow_pixfmt(DWORD biCompression, WORD biBitCount) yading@10: { yading@10: switch(biCompression) { yading@10: case BI_BITFIELDS: yading@10: case BI_RGB: yading@10: switch(biBitCount) { /* 1-8 are untested */ yading@10: case 1: yading@10: return AV_PIX_FMT_MONOWHITE; yading@10: case 4: yading@10: return AV_PIX_FMT_RGB4; yading@10: case 8: yading@10: return AV_PIX_FMT_RGB8; yading@10: case 16: yading@10: return AV_PIX_FMT_RGB555; yading@10: case 24: yading@10: return AV_PIX_FMT_BGR24; yading@10: case 32: yading@10: return AV_PIX_FMT_RGB32; yading@10: } yading@10: } yading@10: return avpriv_find_pix_fmt(ff_raw_pix_fmt_tags, biCompression); // all others yading@10: } yading@10: yading@10: static int yading@10: dshow_read_close(AVFormatContext *s) yading@10: { yading@10: struct dshow_ctx *ctx = s->priv_data; yading@10: AVPacketList *pktl; yading@10: yading@10: if (ctx->control) { yading@10: IMediaControl_Stop(ctx->control); yading@10: IMediaControl_Release(ctx->control); yading@10: } yading@10: yading@10: if (ctx->media_event) yading@10: IMediaEvent_Release(ctx->media_event); yading@10: yading@10: if (ctx->graph) { yading@10: IEnumFilters *fenum; yading@10: int r; yading@10: r = IGraphBuilder_EnumFilters(ctx->graph, &fenum); yading@10: if (r == S_OK) { yading@10: IBaseFilter *f; yading@10: IEnumFilters_Reset(fenum); yading@10: while (IEnumFilters_Next(fenum, 1, &f, NULL) == S_OK) { yading@10: if (IGraphBuilder_RemoveFilter(ctx->graph, f) == S_OK) yading@10: IEnumFilters_Reset(fenum); /* When a filter is removed, yading@10: * the list must be reset. */ yading@10: IBaseFilter_Release(f); yading@10: } yading@10: IEnumFilters_Release(fenum); yading@10: } yading@10: IGraphBuilder_Release(ctx->graph); yading@10: } yading@10: yading@10: if (ctx->capture_pin[VideoDevice]) yading@10: libAVPin_Release(ctx->capture_pin[VideoDevice]); yading@10: if (ctx->capture_pin[AudioDevice]) yading@10: libAVPin_Release(ctx->capture_pin[AudioDevice]); yading@10: if (ctx->capture_filter[VideoDevice]) yading@10: libAVFilter_Release(ctx->capture_filter[VideoDevice]); yading@10: if (ctx->capture_filter[AudioDevice]) yading@10: libAVFilter_Release(ctx->capture_filter[AudioDevice]); yading@10: yading@10: if (ctx->device_pin[VideoDevice]) yading@10: IPin_Release(ctx->device_pin[VideoDevice]); yading@10: if (ctx->device_pin[AudioDevice]) yading@10: IPin_Release(ctx->device_pin[AudioDevice]); yading@10: if (ctx->device_filter[VideoDevice]) yading@10: IBaseFilter_Release(ctx->device_filter[VideoDevice]); yading@10: if (ctx->device_filter[AudioDevice]) yading@10: IBaseFilter_Release(ctx->device_filter[AudioDevice]); yading@10: yading@10: if (ctx->device_name[0]) yading@10: av_free(ctx->device_name[0]); yading@10: if (ctx->device_name[1]) yading@10: av_free(ctx->device_name[1]); yading@10: yading@10: if(ctx->mutex) yading@10: CloseHandle(ctx->mutex); yading@10: if(ctx->event[0]) yading@10: CloseHandle(ctx->event[0]); yading@10: if(ctx->event[1]) yading@10: CloseHandle(ctx->event[1]); yading@10: yading@10: pktl = ctx->pktl; yading@10: while (pktl) { yading@10: AVPacketList *next = pktl->next; yading@10: av_destruct_packet(&pktl->pkt); yading@10: av_free(pktl); yading@10: pktl = next; yading@10: } yading@10: yading@10: CoUninitialize(); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static char *dup_wchar_to_utf8(wchar_t *w) yading@10: { yading@10: char *s = NULL; yading@10: int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0); yading@10: s = av_malloc(l); yading@10: if (s) yading@10: WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0); yading@10: return s; yading@10: } yading@10: yading@10: static int shall_we_drop(AVFormatContext *s) yading@10: { yading@10: struct dshow_ctx *ctx = s->priv_data; yading@10: const uint8_t dropscore[] = {62, 75, 87, 100}; yading@10: const int ndropscores = FF_ARRAY_ELEMS(dropscore); yading@10: unsigned int buffer_fullness = (ctx->curbufsize*100)/s->max_picture_buffer; yading@10: yading@10: if(dropscore[++ctx->video_frame_num%ndropscores] <= buffer_fullness) { yading@10: av_log(s, AV_LOG_ERROR, yading@10: "real-time buffer %d%% full! frame dropped!\n", buffer_fullness); yading@10: return 1; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static void yading@10: callback(void *priv_data, int index, uint8_t *buf, int buf_size, int64_t time) yading@10: { yading@10: AVFormatContext *s = priv_data; yading@10: struct dshow_ctx *ctx = s->priv_data; yading@10: AVPacketList **ppktl, *pktl_next; yading@10: yading@10: // dump_videohdr(s, vdhdr); yading@10: yading@10: WaitForSingleObject(ctx->mutex, INFINITE); yading@10: yading@10: if(shall_we_drop(s)) yading@10: goto fail; yading@10: yading@10: pktl_next = av_mallocz(sizeof(AVPacketList)); yading@10: if(!pktl_next) yading@10: goto fail; yading@10: yading@10: if(av_new_packet(&pktl_next->pkt, buf_size) < 0) { yading@10: av_free(pktl_next); yading@10: goto fail; yading@10: } yading@10: yading@10: pktl_next->pkt.stream_index = index; yading@10: pktl_next->pkt.pts = time; yading@10: memcpy(pktl_next->pkt.data, buf, buf_size); yading@10: yading@10: for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next); yading@10: *ppktl = pktl_next; yading@10: yading@10: ctx->curbufsize += buf_size; yading@10: yading@10: SetEvent(ctx->event[1]); yading@10: ReleaseMutex(ctx->mutex); yading@10: yading@10: return; yading@10: fail: yading@10: ReleaseMutex(ctx->mutex); yading@10: return; yading@10: } yading@10: yading@10: /** yading@10: * Cycle through available devices using the device enumerator devenum, yading@10: * retrieve the device with type specified by devtype and return the yading@10: * pointer to the object found in *pfilter. yading@10: * If pfilter is NULL, list all device names. yading@10: */ yading@10: static int yading@10: dshow_cycle_devices(AVFormatContext *avctx, ICreateDevEnum *devenum, yading@10: enum dshowDeviceType devtype, IBaseFilter **pfilter) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IBaseFilter *device_filter = NULL; yading@10: IEnumMoniker *classenum = NULL; yading@10: IMoniker *m = NULL; yading@10: const char *device_name = ctx->device_name[devtype]; yading@10: int skip = (devtype == VideoDevice) ? ctx->video_device_number yading@10: : ctx->audio_device_number; yading@10: int r; yading@10: yading@10: const GUID *device_guid[2] = { &CLSID_VideoInputDeviceCategory, yading@10: &CLSID_AudioInputDeviceCategory }; yading@10: const char *devtypename = (devtype == VideoDevice) ? "video" : "audio"; yading@10: yading@10: r = ICreateDevEnum_CreateClassEnumerator(devenum, device_guid[devtype], yading@10: (IEnumMoniker **) &classenum, 0); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not enumerate %s devices.\n", yading@10: devtypename); yading@10: return AVERROR(EIO); yading@10: } yading@10: yading@10: while (!device_filter && IEnumMoniker_Next(classenum, 1, &m, NULL) == S_OK) { yading@10: IPropertyBag *bag = NULL; yading@10: char *buf = NULL; yading@10: VARIANT var; yading@10: yading@10: r = IMoniker_BindToStorage(m, 0, 0, &IID_IPropertyBag, (void *) &bag); yading@10: if (r != S_OK) yading@10: goto fail1; yading@10: yading@10: var.vt = VT_BSTR; yading@10: r = IPropertyBag_Read(bag, L"FriendlyName", &var, NULL); yading@10: if (r != S_OK) yading@10: goto fail1; yading@10: yading@10: buf = dup_wchar_to_utf8(var.bstrVal); yading@10: yading@10: if (pfilter) { yading@10: if (strcmp(device_name, buf)) yading@10: goto fail1; yading@10: yading@10: if (!skip--) yading@10: IMoniker_BindToObject(m, 0, 0, &IID_IBaseFilter, (void *) &device_filter); yading@10: } else { yading@10: av_log(avctx, AV_LOG_INFO, " \"%s\"\n", buf); yading@10: } yading@10: yading@10: fail1: yading@10: if (buf) yading@10: av_free(buf); yading@10: if (bag) yading@10: IPropertyBag_Release(bag); yading@10: IMoniker_Release(m); yading@10: } yading@10: yading@10: IEnumMoniker_Release(classenum); yading@10: yading@10: if (pfilter) { yading@10: if (!device_filter) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not find %s device.\n", yading@10: devtypename); yading@10: return AVERROR(EIO); yading@10: } yading@10: *pfilter = device_filter; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * Cycle through available formats using the specified pin, yading@10: * try to set parameters specified through AVOptions and if successful yading@10: * return 1 in *pformat_set. yading@10: * If pformat_set is NULL, list all pin capabilities. yading@10: */ yading@10: static void yading@10: dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype, yading@10: IPin *pin, int *pformat_set) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IAMStreamConfig *config = NULL; yading@10: AM_MEDIA_TYPE *type = NULL; yading@10: int format_set = 0; yading@10: void *caps = NULL; yading@10: int i, n, size; yading@10: yading@10: if (IPin_QueryInterface(pin, &IID_IAMStreamConfig, (void **) &config) != S_OK) yading@10: return; yading@10: if (IAMStreamConfig_GetNumberOfCapabilities(config, &n, &size) != S_OK) yading@10: goto end; yading@10: yading@10: caps = av_malloc(size); yading@10: if (!caps) yading@10: goto end; yading@10: yading@10: for (i = 0; i < n && !format_set; i++) { yading@10: IAMStreamConfig_GetStreamCaps(config, i, &type, (void *) caps); yading@10: yading@10: #if DSHOWDEBUG yading@10: ff_print_AM_MEDIA_TYPE(type); yading@10: #endif yading@10: yading@10: if (devtype == VideoDevice) { yading@10: VIDEO_STREAM_CONFIG_CAPS *vcaps = caps; yading@10: BITMAPINFOHEADER *bih; yading@10: int64_t *fr; yading@10: #if DSHOWDEBUG yading@10: ff_print_VIDEO_STREAM_CONFIG_CAPS(vcaps); yading@10: #endif yading@10: if (IsEqualGUID(&type->formattype, &FORMAT_VideoInfo)) { yading@10: VIDEOINFOHEADER *v = (void *) type->pbFormat; yading@10: fr = &v->AvgTimePerFrame; yading@10: bih = &v->bmiHeader; yading@10: } else if (IsEqualGUID(&type->formattype, &FORMAT_VideoInfo2)) { yading@10: VIDEOINFOHEADER2 *v = (void *) type->pbFormat; yading@10: fr = &v->AvgTimePerFrame; yading@10: bih = &v->bmiHeader; yading@10: } else { yading@10: goto next; yading@10: } yading@10: if (!pformat_set) { yading@10: enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount); yading@10: if (pix_fmt == AV_PIX_FMT_NONE) { yading@10: enum AVCodecID codec_id = ff_codec_get_id(avformat_get_riff_video_tags(), bih->biCompression); yading@10: AVCodec *codec = avcodec_find_decoder(codec_id); yading@10: if (codec_id == AV_CODEC_ID_NONE || !codec) { yading@10: av_log(avctx, AV_LOG_INFO, " unknown compression type 0x%X", (int) bih->biCompression); yading@10: } else { yading@10: av_log(avctx, AV_LOG_INFO, " vcodec=%s", codec->name); yading@10: } yading@10: } else { yading@10: av_log(avctx, AV_LOG_INFO, " pixel_format=%s", av_get_pix_fmt_name(pix_fmt)); yading@10: } yading@10: av_log(avctx, AV_LOG_INFO, " min s=%ldx%ld fps=%g max s=%ldx%ld fps=%g\n", yading@10: vcaps->MinOutputSize.cx, vcaps->MinOutputSize.cy, yading@10: 1e7 / vcaps->MaxFrameInterval, yading@10: vcaps->MaxOutputSize.cx, vcaps->MaxOutputSize.cy, yading@10: 1e7 / vcaps->MinFrameInterval); yading@10: continue; yading@10: } yading@10: if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) { yading@10: if (ctx->video_codec_id != ff_codec_get_id(avformat_get_riff_video_tags(), bih->biCompression)) yading@10: goto next; yading@10: } yading@10: if (ctx->pixel_format != AV_PIX_FMT_NONE && yading@10: ctx->pixel_format != dshow_pixfmt(bih->biCompression, bih->biBitCount)) { yading@10: goto next; yading@10: } yading@10: if (ctx->framerate) { yading@10: int64_t framerate = ((int64_t) ctx->requested_framerate.den*10000000) yading@10: / ctx->requested_framerate.num; yading@10: if (framerate > vcaps->MaxFrameInterval || yading@10: framerate < vcaps->MinFrameInterval) yading@10: goto next; yading@10: *fr = framerate; yading@10: } yading@10: if (ctx->requested_width && ctx->requested_height) { yading@10: if (ctx->requested_width > vcaps->MaxOutputSize.cx || yading@10: ctx->requested_width < vcaps->MinOutputSize.cx || yading@10: ctx->requested_height > vcaps->MaxOutputSize.cy || yading@10: ctx->requested_height < vcaps->MinOutputSize.cy) yading@10: goto next; yading@10: bih->biWidth = ctx->requested_width; yading@10: bih->biHeight = ctx->requested_height; yading@10: } yading@10: } else { yading@10: AUDIO_STREAM_CONFIG_CAPS *acaps = caps; yading@10: WAVEFORMATEX *fx; yading@10: #if DSHOWDEBUG yading@10: ff_print_AUDIO_STREAM_CONFIG_CAPS(acaps); yading@10: #endif yading@10: if (IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx)) { yading@10: fx = (void *) type->pbFormat; yading@10: } else { yading@10: goto next; yading@10: } yading@10: if (!pformat_set) { yading@10: av_log(avctx, AV_LOG_INFO, " min ch=%lu bits=%lu rate=%6lu max ch=%lu bits=%lu rate=%6lu\n", yading@10: acaps->MinimumChannels, acaps->MinimumBitsPerSample, acaps->MinimumSampleFrequency, yading@10: acaps->MaximumChannels, acaps->MaximumBitsPerSample, acaps->MaximumSampleFrequency); yading@10: continue; yading@10: } yading@10: if (ctx->sample_rate) { yading@10: if (ctx->sample_rate > acaps->MaximumSampleFrequency || yading@10: ctx->sample_rate < acaps->MinimumSampleFrequency) yading@10: goto next; yading@10: fx->nSamplesPerSec = ctx->sample_rate; yading@10: } yading@10: if (ctx->sample_size) { yading@10: if (ctx->sample_size > acaps->MaximumBitsPerSample || yading@10: ctx->sample_size < acaps->MinimumBitsPerSample) yading@10: goto next; yading@10: fx->wBitsPerSample = ctx->sample_size; yading@10: } yading@10: if (ctx->channels) { yading@10: if (ctx->channels > acaps->MaximumChannels || yading@10: ctx->channels < acaps->MinimumChannels) yading@10: goto next; yading@10: fx->nChannels = ctx->channels; yading@10: } yading@10: } yading@10: if (IAMStreamConfig_SetFormat(config, type) != S_OK) yading@10: goto next; yading@10: format_set = 1; yading@10: next: yading@10: if (type->pbFormat) yading@10: CoTaskMemFree(type->pbFormat); yading@10: CoTaskMemFree(type); yading@10: } yading@10: end: yading@10: IAMStreamConfig_Release(config); yading@10: if (caps) yading@10: av_free(caps); yading@10: if (pformat_set) yading@10: *pformat_set = format_set; yading@10: } yading@10: yading@10: /** yading@10: * Set audio device buffer size in milliseconds (which can directly impact yading@10: * latency, depending on the device). yading@10: */ yading@10: static int yading@10: dshow_set_audio_buffer_size(AVFormatContext *avctx, IPin *pin) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IAMBufferNegotiation *buffer_negotiation = NULL; yading@10: ALLOCATOR_PROPERTIES props = { -1, -1, -1, -1 }; yading@10: IAMStreamConfig *config = NULL; yading@10: AM_MEDIA_TYPE *type = NULL; yading@10: int ret = AVERROR(EIO); yading@10: yading@10: if (IPin_QueryInterface(pin, &IID_IAMStreamConfig, (void **) &config) != S_OK) yading@10: goto end; yading@10: if (IAMStreamConfig_GetFormat(config, &type) != S_OK) yading@10: goto end; yading@10: if (!IsEqualGUID(&type->formattype, &FORMAT_WaveFormatEx)) yading@10: goto end; yading@10: yading@10: props.cbBuffer = (((WAVEFORMATEX *) type->pbFormat)->nAvgBytesPerSec) yading@10: * ctx->audio_buffer_size / 1000; yading@10: yading@10: if (IPin_QueryInterface(pin, &IID_IAMBufferNegotiation, (void **) &buffer_negotiation) != S_OK) yading@10: goto end; yading@10: if (IAMBufferNegotiation_SuggestAllocatorProperties(buffer_negotiation, &props) != S_OK) yading@10: goto end; yading@10: yading@10: ret = 0; yading@10: yading@10: end: yading@10: if (buffer_negotiation) yading@10: IAMBufferNegotiation_Release(buffer_negotiation); yading@10: if (type) { yading@10: if (type->pbFormat) yading@10: CoTaskMemFree(type->pbFormat); yading@10: CoTaskMemFree(type); yading@10: } yading@10: if (config) yading@10: IAMStreamConfig_Release(config); yading@10: yading@10: return ret; yading@10: } yading@10: yading@10: /** yading@10: * Cycle through available pins using the device_filter device, of type yading@10: * devtype, retrieve the first output pin and return the pointer to the yading@10: * object found in *ppin. yading@10: * If ppin is NULL, cycle through all pins listing audio/video capabilities. yading@10: */ yading@10: static int yading@10: dshow_cycle_pins(AVFormatContext *avctx, enum dshowDeviceType devtype, yading@10: IBaseFilter *device_filter, IPin **ppin) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IEnumPins *pins = 0; yading@10: IPin *device_pin = NULL; yading@10: IPin *pin; yading@10: int r; yading@10: yading@10: const GUID *mediatype[2] = { &MEDIATYPE_Video, &MEDIATYPE_Audio }; yading@10: const char *devtypename = (devtype == VideoDevice) ? "video" : "audio"; yading@10: yading@10: int set_format = (devtype == VideoDevice && (ctx->framerate || yading@10: (ctx->requested_width && ctx->requested_height) || yading@10: ctx->pixel_format != AV_PIX_FMT_NONE || yading@10: ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO)) yading@10: || (devtype == AudioDevice && (ctx->channels || ctx->sample_rate)); yading@10: int format_set = 0; yading@10: yading@10: r = IBaseFilter_EnumPins(device_filter, &pins); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not enumerate pins.\n"); yading@10: return AVERROR(EIO); yading@10: } yading@10: yading@10: if (!ppin) { yading@10: av_log(avctx, AV_LOG_INFO, "DirectShow %s device options\n", yading@10: devtypename); yading@10: } yading@10: while (!device_pin && IEnumPins_Next(pins, 1, &pin, NULL) == S_OK) { yading@10: IKsPropertySet *p = NULL; yading@10: IEnumMediaTypes *types = NULL; yading@10: PIN_INFO info = {0}; yading@10: AM_MEDIA_TYPE *type; yading@10: GUID category; yading@10: DWORD r2; yading@10: yading@10: IPin_QueryPinInfo(pin, &info); yading@10: IBaseFilter_Release(info.pFilter); yading@10: yading@10: if (info.dir != PINDIR_OUTPUT) yading@10: goto next; yading@10: if (IPin_QueryInterface(pin, &IID_IKsPropertySet, (void **) &p) != S_OK) yading@10: goto next; yading@10: if (IKsPropertySet_Get(p, &ROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, yading@10: NULL, 0, &category, sizeof(GUID), &r2) != S_OK) yading@10: goto next; yading@10: if (!IsEqualGUID(&category, &PIN_CATEGORY_CAPTURE)) yading@10: goto next; yading@10: yading@10: if (!ppin) { yading@10: char *buf = dup_wchar_to_utf8(info.achName); yading@10: av_log(avctx, AV_LOG_INFO, " Pin \"%s\"\n", buf); yading@10: av_free(buf); yading@10: dshow_cycle_formats(avctx, devtype, pin, NULL); yading@10: goto next; yading@10: } yading@10: if (set_format) { yading@10: dshow_cycle_formats(avctx, devtype, pin, &format_set); yading@10: if (!format_set) { yading@10: goto next; yading@10: } yading@10: } yading@10: if (devtype == AudioDevice && ctx->audio_buffer_size) { yading@10: if (dshow_set_audio_buffer_size(avctx, pin) < 0) yading@10: goto next; yading@10: } yading@10: yading@10: if (IPin_EnumMediaTypes(pin, &types) != S_OK) yading@10: goto next; yading@10: yading@10: IEnumMediaTypes_Reset(types); yading@10: while (!device_pin && IEnumMediaTypes_Next(types, 1, &type, NULL) == S_OK) { yading@10: if (IsEqualGUID(&type->majortype, mediatype[devtype])) { yading@10: device_pin = pin; yading@10: goto next; yading@10: } yading@10: CoTaskMemFree(type); yading@10: } yading@10: yading@10: next: yading@10: if (types) yading@10: IEnumMediaTypes_Release(types); yading@10: if (p) yading@10: IKsPropertySet_Release(p); yading@10: if (device_pin != pin) yading@10: IPin_Release(pin); yading@10: } yading@10: yading@10: IEnumPins_Release(pins); yading@10: yading@10: if (ppin) { yading@10: if (set_format && !format_set) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not set %s options\n", devtypename); yading@10: return AVERROR(EIO); yading@10: } yading@10: if (!device_pin) { yading@10: av_log(avctx, AV_LOG_ERROR, yading@10: "Could not find output pin from %s capture device.\n", devtypename); yading@10: return AVERROR(EIO); yading@10: } yading@10: *ppin = device_pin; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * List options for device with type devtype. yading@10: * yading@10: * @param devenum device enumerator used for accessing the device yading@10: */ yading@10: static int yading@10: dshow_list_device_options(AVFormatContext *avctx, ICreateDevEnum *devenum, yading@10: enum dshowDeviceType devtype) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IBaseFilter *device_filter = NULL; yading@10: int r; yading@10: yading@10: if ((r = dshow_cycle_devices(avctx, devenum, devtype, &device_filter)) < 0) yading@10: return r; yading@10: ctx->device_filter[devtype] = device_filter; yading@10: if ((r = dshow_cycle_pins(avctx, devtype, device_filter, NULL)) < 0) yading@10: return r; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int yading@10: dshow_open_device(AVFormatContext *avctx, ICreateDevEnum *devenum, yading@10: enum dshowDeviceType devtype) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IBaseFilter *device_filter = NULL; yading@10: IGraphBuilder *graph = ctx->graph; yading@10: IPin *device_pin = NULL; yading@10: libAVPin *capture_pin = NULL; yading@10: libAVFilter *capture_filter = NULL; yading@10: int ret = AVERROR(EIO); yading@10: int r; yading@10: yading@10: const wchar_t *filter_name[2] = { L"Audio capture filter", L"Video capture filter" }; yading@10: yading@10: if ((r = dshow_cycle_devices(avctx, devenum, devtype, &device_filter)) < 0) { yading@10: ret = r; yading@10: goto error; yading@10: } yading@10: yading@10: ctx->device_filter [devtype] = device_filter; yading@10: yading@10: r = IGraphBuilder_AddFilter(graph, device_filter, NULL); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not add device filter to graph.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: if ((r = dshow_cycle_pins(avctx, devtype, device_filter, &device_pin)) < 0) { yading@10: ret = r; yading@10: goto error; yading@10: } yading@10: ctx->device_pin[devtype] = device_pin; yading@10: yading@10: capture_filter = libAVFilter_Create(avctx, callback, devtype); yading@10: if (!capture_filter) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not create grabber filter.\n"); yading@10: goto error; yading@10: } yading@10: ctx->capture_filter[devtype] = capture_filter; yading@10: yading@10: r = IGraphBuilder_AddFilter(graph, (IBaseFilter *) capture_filter, yading@10: filter_name[devtype]); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not add capture filter to graph\n"); yading@10: goto error; yading@10: } yading@10: yading@10: libAVPin_AddRef(capture_filter->pin); yading@10: capture_pin = capture_filter->pin; yading@10: ctx->capture_pin[devtype] = capture_pin; yading@10: yading@10: r = IGraphBuilder_ConnectDirect(graph, device_pin, (IPin *) capture_pin, NULL); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not connect pins\n"); yading@10: goto error; yading@10: } yading@10: yading@10: ret = 0; yading@10: yading@10: error: yading@10: return ret; yading@10: } yading@10: yading@10: static enum AVCodecID waveform_codec_id(enum AVSampleFormat sample_fmt) yading@10: { yading@10: switch (sample_fmt) { yading@10: case AV_SAMPLE_FMT_U8: return AV_CODEC_ID_PCM_U8; yading@10: case AV_SAMPLE_FMT_S16: return AV_CODEC_ID_PCM_S16LE; yading@10: case AV_SAMPLE_FMT_S32: return AV_CODEC_ID_PCM_S32LE; yading@10: default: return AV_CODEC_ID_NONE; /* Should never happen. */ yading@10: } yading@10: } yading@10: yading@10: static enum AVSampleFormat sample_fmt_bits_per_sample(int bits) yading@10: { yading@10: switch (bits) { yading@10: case 8: return AV_SAMPLE_FMT_U8; yading@10: case 16: return AV_SAMPLE_FMT_S16; yading@10: case 32: return AV_SAMPLE_FMT_S32; yading@10: default: return AV_SAMPLE_FMT_NONE; /* Should never happen. */ yading@10: } yading@10: } yading@10: yading@10: static int yading@10: dshow_add_device(AVFormatContext *avctx, yading@10: enum dshowDeviceType devtype) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: AM_MEDIA_TYPE type; yading@10: AVCodecContext *codec; yading@10: AVStream *st; yading@10: int ret = AVERROR(EIO); yading@10: yading@10: st = avformat_new_stream(avctx, NULL); yading@10: if (!st) { yading@10: ret = AVERROR(ENOMEM); yading@10: goto error; yading@10: } yading@10: st->id = devtype; yading@10: yading@10: ctx->capture_filter[devtype]->stream_index = st->index; yading@10: yading@10: libAVPin_ConnectionMediaType(ctx->capture_pin[devtype], &type); yading@10: yading@10: codec = st->codec; yading@10: if (devtype == VideoDevice) { yading@10: BITMAPINFOHEADER *bih = NULL; yading@10: AVRational time_base; yading@10: yading@10: if (IsEqualGUID(&type.formattype, &FORMAT_VideoInfo)) { yading@10: VIDEOINFOHEADER *v = (void *) type.pbFormat; yading@10: time_base = (AVRational) { v->AvgTimePerFrame, 10000000 }; yading@10: bih = &v->bmiHeader; yading@10: } else if (IsEqualGUID(&type.formattype, &FORMAT_VideoInfo2)) { yading@10: VIDEOINFOHEADER2 *v = (void *) type.pbFormat; yading@10: time_base = (AVRational) { v->AvgTimePerFrame, 10000000 }; yading@10: bih = &v->bmiHeader; yading@10: } yading@10: if (!bih) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not get media type.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: codec->time_base = time_base; yading@10: codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@10: codec->width = bih->biWidth; yading@10: codec->height = bih->biHeight; yading@10: codec->pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount); yading@10: if (bih->biCompression == MKTAG('H', 'D', 'Y', 'C')) { yading@10: av_log(avctx, AV_LOG_DEBUG, "attempt to use full range for HDYC...\n"); yading@10: codec->color_range = AVCOL_RANGE_MPEG; // just in case it needs this... yading@10: } yading@10: if (codec->pix_fmt == AV_PIX_FMT_NONE) { yading@10: codec->codec_id = ff_codec_get_id(avformat_get_riff_video_tags(), bih->biCompression); yading@10: if (codec->codec_id == AV_CODEC_ID_NONE) { yading@10: av_log(avctx, AV_LOG_ERROR, "Unknown compression type. " yading@10: "Please report type 0x%X.\n", (int) bih->biCompression); yading@10: return AVERROR_PATCHWELCOME; yading@10: } yading@10: codec->bits_per_coded_sample = bih->biBitCount; yading@10: } else { yading@10: codec->codec_id = AV_CODEC_ID_RAWVIDEO; yading@10: if (bih->biCompression == BI_RGB || bih->biCompression == BI_BITFIELDS) { yading@10: codec->bits_per_coded_sample = bih->biBitCount; yading@10: codec->extradata = av_malloc(9 + FF_INPUT_BUFFER_PADDING_SIZE); yading@10: if (codec->extradata) { yading@10: codec->extradata_size = 9; yading@10: memcpy(codec->extradata, "BottomUp", 9); yading@10: } yading@10: } yading@10: } yading@10: } else { yading@10: WAVEFORMATEX *fx = NULL; yading@10: yading@10: if (IsEqualGUID(&type.formattype, &FORMAT_WaveFormatEx)) { yading@10: fx = (void *) type.pbFormat; yading@10: } yading@10: if (!fx) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not get media type.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@10: codec->sample_fmt = sample_fmt_bits_per_sample(fx->wBitsPerSample); yading@10: codec->codec_id = waveform_codec_id(codec->sample_fmt); yading@10: codec->sample_rate = fx->nSamplesPerSec; yading@10: codec->channels = fx->nChannels; yading@10: } yading@10: yading@10: avpriv_set_pts_info(st, 64, 1, 10000000); yading@10: yading@10: ret = 0; yading@10: yading@10: error: yading@10: return ret; yading@10: } yading@10: yading@10: static int parse_device_name(AVFormatContext *avctx) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: char **device_name = ctx->device_name; yading@10: char *name = av_strdup(avctx->filename); yading@10: char *tmp = name; yading@10: int ret = 1; yading@10: char *type; yading@10: yading@10: while ((type = strtok(tmp, "="))) { yading@10: char *token = strtok(NULL, ":"); yading@10: tmp = NULL; yading@10: yading@10: if (!strcmp(type, "video")) { yading@10: device_name[0] = token; yading@10: } else if (!strcmp(type, "audio")) { yading@10: device_name[1] = token; yading@10: } else { yading@10: device_name[0] = NULL; yading@10: device_name[1] = NULL; yading@10: break; yading@10: } yading@10: } yading@10: yading@10: if (!device_name[0] && !device_name[1]) { yading@10: ret = 0; yading@10: } else { yading@10: if (device_name[0]) yading@10: device_name[0] = av_strdup(device_name[0]); yading@10: if (device_name[1]) yading@10: device_name[1] = av_strdup(device_name[1]); yading@10: } yading@10: yading@10: av_free(name); yading@10: return ret; yading@10: } yading@10: yading@10: static int dshow_read_header(AVFormatContext *avctx) yading@10: { yading@10: struct dshow_ctx *ctx = avctx->priv_data; yading@10: IGraphBuilder *graph = NULL; yading@10: ICreateDevEnum *devenum = NULL; yading@10: IMediaControl *control = NULL; yading@10: IMediaEvent *media_event = NULL; yading@10: HANDLE media_event_handle; yading@10: HANDLE proc; yading@10: int ret = AVERROR(EIO); yading@10: int r; yading@10: yading@10: CoInitialize(0); yading@10: yading@10: if (!ctx->list_devices && !parse_device_name(avctx)) { yading@10: av_log(avctx, AV_LOG_ERROR, "Malformed dshow input string.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: ctx->video_codec_id = avctx->video_codec_id ? avctx->video_codec_id yading@10: : AV_CODEC_ID_RAWVIDEO; yading@10: if (ctx->pixel_format != AV_PIX_FMT_NONE) { yading@10: if (ctx->video_codec_id != AV_CODEC_ID_RAWVIDEO) { yading@10: av_log(avctx, AV_LOG_ERROR, "Pixel format may only be set when " yading@10: "video codec is not set or set to rawvideo\n"); yading@10: ret = AVERROR(EINVAL); yading@10: goto error; yading@10: } yading@10: } yading@10: if (ctx->framerate) { yading@10: r = av_parse_video_rate(&ctx->requested_framerate, ctx->framerate); yading@10: if (r < 0) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not parse framerate '%s'.\n", ctx->framerate); yading@10: goto error; yading@10: } yading@10: } yading@10: yading@10: r = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, yading@10: &IID_IGraphBuilder, (void **) &graph); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not create capture graph.\n"); yading@10: goto error; yading@10: } yading@10: ctx->graph = graph; yading@10: yading@10: r = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, yading@10: &IID_ICreateDevEnum, (void **) &devenum); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not enumerate system devices.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: if (ctx->list_devices) { yading@10: av_log(avctx, AV_LOG_INFO, "DirectShow video devices\n"); yading@10: dshow_cycle_devices(avctx, devenum, VideoDevice, NULL); yading@10: av_log(avctx, AV_LOG_INFO, "DirectShow audio devices\n"); yading@10: dshow_cycle_devices(avctx, devenum, AudioDevice, NULL); yading@10: ret = AVERROR_EXIT; yading@10: goto error; yading@10: } yading@10: if (ctx->list_options) { yading@10: if (ctx->device_name[VideoDevice]) yading@10: dshow_list_device_options(avctx, devenum, VideoDevice); yading@10: if (ctx->device_name[AudioDevice]) yading@10: dshow_list_device_options(avctx, devenum, AudioDevice); yading@10: ret = AVERROR_EXIT; yading@10: goto error; yading@10: } yading@10: yading@10: if (ctx->device_name[VideoDevice]) { yading@10: if ((r = dshow_open_device(avctx, devenum, VideoDevice)) < 0 || yading@10: (r = dshow_add_device(avctx, VideoDevice)) < 0) { yading@10: ret = r; yading@10: goto error; yading@10: } yading@10: } yading@10: if (ctx->device_name[AudioDevice]) { yading@10: if ((r = dshow_open_device(avctx, devenum, AudioDevice)) < 0 || yading@10: (r = dshow_add_device(avctx, AudioDevice)) < 0) { yading@10: ret = r; yading@10: goto error; yading@10: } yading@10: } yading@10: yading@10: ctx->mutex = CreateMutex(NULL, 0, NULL); yading@10: if (!ctx->mutex) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not create Mutex\n"); yading@10: goto error; yading@10: } yading@10: ctx->event[1] = CreateEvent(NULL, 1, 0, NULL); yading@10: if (!ctx->event[1]) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not create Event\n"); yading@10: goto error; yading@10: } yading@10: yading@10: r = IGraphBuilder_QueryInterface(graph, &IID_IMediaControl, (void **) &control); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not get media control.\n"); yading@10: goto error; yading@10: } yading@10: ctx->control = control; yading@10: yading@10: r = IGraphBuilder_QueryInterface(graph, &IID_IMediaEvent, (void **) &media_event); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not get media event.\n"); yading@10: goto error; yading@10: } yading@10: ctx->media_event = media_event; yading@10: yading@10: r = IMediaEvent_GetEventHandle(media_event, (void *) &media_event_handle); yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not get media event handle.\n"); yading@10: goto error; yading@10: } yading@10: proc = GetCurrentProcess(); yading@10: r = DuplicateHandle(proc, media_event_handle, proc, &ctx->event[0], yading@10: 0, 0, DUPLICATE_SAME_ACCESS); yading@10: if (!r) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not duplicate media event handle.\n"); yading@10: goto error; yading@10: } yading@10: yading@10: r = IMediaControl_Run(control); yading@10: if (r == S_FALSE) { yading@10: OAFilterState pfs; yading@10: r = IMediaControl_GetState(control, 0, &pfs); yading@10: } yading@10: if (r != S_OK) { yading@10: av_log(avctx, AV_LOG_ERROR, "Could not run filter\n"); yading@10: goto error; yading@10: } yading@10: yading@10: ret = 0; yading@10: yading@10: error: yading@10: yading@10: if (devenum) yading@10: ICreateDevEnum_Release(devenum); yading@10: yading@10: if (ret < 0) yading@10: dshow_read_close(avctx); yading@10: yading@10: return ret; yading@10: } yading@10: yading@10: /** yading@10: * Checks media events from DirectShow and returns -1 on error or EOF. Also yading@10: * purges all events that might be in the event queue to stop the trigger yading@10: * of event notification. yading@10: */ yading@10: static int dshow_check_event_queue(IMediaEvent *media_event) yading@10: { yading@10: LONG_PTR p1, p2; yading@10: long code; yading@10: int ret = 0; yading@10: yading@10: while (IMediaEvent_GetEvent(media_event, &code, &p1, &p2, 0) != E_ABORT) { yading@10: if (code == EC_COMPLETE || code == EC_DEVICE_LOST || code == EC_ERRORABORT) yading@10: ret = -1; yading@10: IMediaEvent_FreeEventParams(media_event, code, p1, p2); yading@10: } yading@10: yading@10: return ret; yading@10: } yading@10: yading@10: static int dshow_read_packet(AVFormatContext *s, AVPacket *pkt) yading@10: { yading@10: struct dshow_ctx *ctx = s->priv_data; yading@10: AVPacketList *pktl = NULL; yading@10: yading@10: while (!ctx->eof && !pktl) { yading@10: WaitForSingleObject(ctx->mutex, INFINITE); yading@10: pktl = ctx->pktl; yading@10: if (pktl) { yading@10: *pkt = pktl->pkt; yading@10: ctx->pktl = ctx->pktl->next; yading@10: av_free(pktl); yading@10: ctx->curbufsize -= pkt->size; yading@10: } yading@10: ResetEvent(ctx->event[1]); yading@10: ReleaseMutex(ctx->mutex); yading@10: if (!pktl) { yading@10: if (dshow_check_event_queue(ctx->media_event) < 0) { yading@10: ctx->eof = 1; yading@10: } else if (s->flags & AVFMT_FLAG_NONBLOCK) { yading@10: return AVERROR(EAGAIN); yading@10: } else { yading@10: WaitForMultipleObjects(2, ctx->event, 0, INFINITE); yading@10: } yading@10: } yading@10: } yading@10: yading@10: return ctx->eof ? AVERROR(EIO) : pkt->size; yading@10: } yading@10: yading@10: #define OFFSET(x) offsetof(struct dshow_ctx, x) yading@10: #define DEC AV_OPT_FLAG_DECODING_PARAM yading@10: static const AVOption options[] = { yading@10: { "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 }, yading@10: { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, -1, AV_PIX_FMT_NB-1, DEC }, yading@10: { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, yading@10: { "sample_rate", "set audio sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, yading@10: { "sample_size", "set audio sample size", OFFSET(sample_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 16, DEC }, yading@10: { "channels", "set number of audio channels, such as 1 or 2", OFFSET(channels), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC }, yading@10: { "list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, DEC, "list_devices" }, yading@10: { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, DEC, "list_devices" }, yading@10: { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_devices" }, yading@10: { "list_options", "list available options for specified device", OFFSET(list_options), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, DEC, "list_options" }, yading@10: { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, DEC, "list_options" }, yading@10: { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, DEC, "list_options" }, yading@10: { "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 }, yading@10: { "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 }, yading@10: { "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 }, yading@10: { NULL }, yading@10: }; yading@10: yading@10: static const AVClass dshow_class = { yading@10: .class_name = "dshow indev", yading@10: .item_name = av_default_item_name, yading@10: .option = options, yading@10: .version = LIBAVUTIL_VERSION_INT, yading@10: }; yading@10: yading@10: AVInputFormat ff_dshow_demuxer = { yading@10: .name = "dshow", yading@10: .long_name = NULL_IF_CONFIG_SMALL("DirectShow capture"), yading@10: .priv_data_size = sizeof(struct dshow_ctx), yading@10: .read_header = dshow_read_header, yading@10: .read_packet = dshow_read_packet, yading@10: .read_close = dshow_read_close, yading@10: .flags = AVFMT_NOFILE, yading@10: .priv_class = &dshow_class, yading@10: };