/* * DirectShow capture interface * Copyright (c) 2010 Ramiro Polla * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dshow_capture.h" #include #define imemoffset offsetof(libAVPin, imemvtbl) DECLARE_QUERYINTERFACE(libAVPin, { {&IID_IUnknown,0}, {&IID_IPin,0}, {&IID_IMemInputPin,imemoffset} }) DECLARE_ADDREF(libAVPin) DECLARE_RELEASE(libAVPin) long WINAPI libAVPin_Connect(libAVPin *this, IPin *pin, const AM_MEDIA_TYPE *type) { dshowdebug("libAVPin_Connect(%p, %p, %p)\n", this, pin, type); /* Input pins receive connections. */ return S_FALSE; } long WINAPI libAVPin_ReceiveConnection(libAVPin *this, IPin *pin, const AM_MEDIA_TYPE *type) { enum dshowDeviceType devtype = this->filter->type; dshowdebug("libAVPin_ReceiveConnection(%p)\n", this); if (!pin) return E_POINTER; if (this->connectedto) return VFW_E_ALREADY_CONNECTED; ff_print_AM_MEDIA_TYPE(type); if (devtype == VideoDevice) { if (!IsEqualGUID(&type->majortype, &MEDIATYPE_Video)) return VFW_E_TYPE_NOT_ACCEPTED; } else { if (!IsEqualGUID(&type->majortype, &MEDIATYPE_Audio)) return VFW_E_TYPE_NOT_ACCEPTED; } IPin_AddRef(pin); this->connectedto = pin; ff_copy_dshow_media_type(&this->type, type); return S_OK; } long WINAPI libAVPin_Disconnect(libAVPin *this) { dshowdebug("libAVPin_Disconnect(%p)\n", this); if (this->filter->state != State_Stopped) return VFW_E_NOT_STOPPED; if (!this->connectedto) return S_FALSE; IPin_Release(this->connectedto); this->connectedto = NULL; return S_OK; } long WINAPI libAVPin_ConnectedTo(libAVPin *this, IPin **pin) { dshowdebug("libAVPin_ConnectedTo(%p)\n", this); if (!pin) return E_POINTER; if (!this->connectedto) return VFW_E_NOT_CONNECTED; IPin_AddRef(this->connectedto); *pin = this->connectedto; return S_OK; } long WINAPI libAVPin_ConnectionMediaType(libAVPin *this, AM_MEDIA_TYPE *type) { dshowdebug("libAVPin_ConnectionMediaType(%p)\n", this); if (!type) return E_POINTER; if (!this->connectedto) return VFW_E_NOT_CONNECTED; return ff_copy_dshow_media_type(type, &this->type); } long WINAPI libAVPin_QueryPinInfo(libAVPin *this, PIN_INFO *info) { dshowdebug("libAVPin_QueryPinInfo(%p)\n", this); if (!info) return E_POINTER; if (this->filter) libAVFilter_AddRef(this->filter); info->pFilter = (IBaseFilter *) this->filter; info->dir = PINDIR_INPUT; wcscpy(info->achName, L"Capture"); return S_OK; } long WINAPI libAVPin_QueryDirection(libAVPin *this, PIN_DIRECTION *dir) { dshowdebug("libAVPin_QueryDirection(%p)\n", this); if (!dir) return E_POINTER; *dir = PINDIR_INPUT; return S_OK; } long WINAPI libAVPin_QueryId(libAVPin *this, wchar_t **id) { dshowdebug("libAVPin_QueryId(%p)\n", this); if (!id) return E_POINTER; *id = wcsdup(L"libAV Pin"); return S_OK; } long WINAPI libAVPin_QueryAccept(libAVPin *this, const AM_MEDIA_TYPE *type) { dshowdebug("libAVPin_QueryAccept(%p)\n", this); return S_FALSE; } long WINAPI libAVPin_EnumMediaTypes(libAVPin *this, IEnumMediaTypes **enumtypes) { const AM_MEDIA_TYPE *type = NULL; libAVEnumMediaTypes *new; dshowdebug("libAVPin_EnumMediaTypes(%p)\n", this); if (!enumtypes) return E_POINTER; new = libAVEnumMediaTypes_Create(type); if (!new) return E_OUTOFMEMORY; *enumtypes = (IEnumMediaTypes *) new; return S_OK; } long WINAPI libAVPin_QueryInternalConnections(libAVPin *this, IPin **pin, unsigned long *npin) { dshowdebug("libAVPin_QueryInternalConnections(%p)\n", this); return E_NOTIMPL; } long WINAPI libAVPin_EndOfStream(libAVPin *this) { dshowdebug("libAVPin_EndOfStream(%p)\n", this); /* I don't care. */ return S_OK; } long WINAPI libAVPin_BeginFlush(libAVPin *this) { dshowdebug("libAVPin_BeginFlush(%p)\n", this); /* I don't care. */ return S_OK; } long WINAPI libAVPin_EndFlush(libAVPin *this) { dshowdebug("libAVPin_EndFlush(%p)\n", this); /* I don't care. */ return S_OK; } long WINAPI libAVPin_NewSegment(libAVPin *this, REFERENCE_TIME start, REFERENCE_TIME stop, double rate) { dshowdebug("libAVPin_NewSegment(%p)\n", this); /* I don't care. */ return S_OK; } static int libAVPin_Setup(libAVPin *this, libAVFilter *filter) { IPinVtbl *vtbl = this->vtbl; IMemInputPinVtbl *imemvtbl; if (!filter) return 0; imemvtbl = av_malloc(sizeof(IMemInputPinVtbl)); if (!imemvtbl) return 0; SETVTBL(imemvtbl, libAVMemInputPin, QueryInterface); SETVTBL(imemvtbl, libAVMemInputPin, AddRef); SETVTBL(imemvtbl, libAVMemInputPin, Release); SETVTBL(imemvtbl, libAVMemInputPin, GetAllocator); SETVTBL(imemvtbl, libAVMemInputPin, NotifyAllocator); SETVTBL(imemvtbl, libAVMemInputPin, GetAllocatorRequirements); SETVTBL(imemvtbl, libAVMemInputPin, Receive); SETVTBL(imemvtbl, libAVMemInputPin, ReceiveMultiple); SETVTBL(imemvtbl, libAVMemInputPin, ReceiveCanBlock); this->imemvtbl = imemvtbl; SETVTBL(vtbl, libAVPin, QueryInterface); SETVTBL(vtbl, libAVPin, AddRef); SETVTBL(vtbl, libAVPin, Release); SETVTBL(vtbl, libAVPin, Connect); SETVTBL(vtbl, libAVPin, ReceiveConnection); SETVTBL(vtbl, libAVPin, Disconnect); SETVTBL(vtbl, libAVPin, ConnectedTo); SETVTBL(vtbl, libAVPin, ConnectionMediaType); SETVTBL(vtbl, libAVPin, QueryPinInfo); SETVTBL(vtbl, libAVPin, QueryDirection); SETVTBL(vtbl, libAVPin, QueryId); SETVTBL(vtbl, libAVPin, QueryAccept); SETVTBL(vtbl, libAVPin, EnumMediaTypes); SETVTBL(vtbl, libAVPin, QueryInternalConnections); SETVTBL(vtbl, libAVPin, EndOfStream); SETVTBL(vtbl, libAVPin, BeginFlush); SETVTBL(vtbl, libAVPin, EndFlush); SETVTBL(vtbl, libAVPin, NewSegment); this->filter = filter; return 1; } static void libAVPin_Free(libAVPin *this) { if (!this) return; av_freep(&this->imemvtbl); if (this->type.pbFormat) { CoTaskMemFree(this->type.pbFormat); this->type.pbFormat = NULL; } } DECLARE_CREATE(libAVPin, libAVPin_Setup(this, filter), libAVFilter *filter) DECLARE_DESTROY(libAVPin, libAVPin_Free) /***************************************************************************** * libAVMemInputPin ****************************************************************************/ long WINAPI libAVMemInputPin_QueryInterface(libAVMemInputPin *this, const GUID *riid, void **ppvObject) { libAVPin *pin = (libAVPin *) ((uint8_t *) this - imemoffset); dshowdebug("libAVMemInputPin_QueryInterface(%p)\n", this); return libAVPin_QueryInterface(pin, riid, ppvObject); } unsigned long WINAPI libAVMemInputPin_AddRef(libAVMemInputPin *this) { libAVPin *pin = (libAVPin *) ((uint8_t *) this - imemoffset); dshowdebug("libAVMemInputPin_AddRef(%p)\n", this); return libAVPin_AddRef(pin); } unsigned long WINAPI libAVMemInputPin_Release(libAVMemInputPin *this) { libAVPin *pin = (libAVPin *) ((uint8_t *) this - imemoffset); dshowdebug("libAVMemInputPin_Release(%p)\n", this); return libAVPin_Release(pin); } long WINAPI libAVMemInputPin_GetAllocator(libAVMemInputPin *this, IMemAllocator **alloc) { dshowdebug("libAVMemInputPin_GetAllocator(%p)\n", this); return VFW_E_NO_ALLOCATOR; } long WINAPI libAVMemInputPin_NotifyAllocator(libAVMemInputPin *this, IMemAllocator *alloc, BOOL rdwr) { dshowdebug("libAVMemInputPin_NotifyAllocator(%p)\n", this); return S_OK; } long WINAPI libAVMemInputPin_GetAllocatorRequirements(libAVMemInputPin *this, ALLOCATOR_PROPERTIES *props) { dshowdebug("libAVMemInputPin_GetAllocatorRequirements(%p)\n", this); return E_NOTIMPL; } long WINAPI libAVMemInputPin_Receive(libAVMemInputPin *this, IMediaSample *sample) { libAVPin *pin = (libAVPin *) ((uint8_t *) this - imemoffset); enum dshowDeviceType devtype = pin->filter->type; void *priv_data; AVFormatContext *s; uint8_t *buf; int buf_size; /* todo should be a long? */ int index; int64_t curtime; int64_t orig_curtime; int64_t graphtime; const char *devtypename = (devtype == VideoDevice) ? "video" : "audio"; IReferenceClock *clock = pin->filter->clock; int64_t dummy; struct dshow_ctx *ctx; dshowdebug("libAVMemInputPin_Receive(%p)\n", this); if (!sample) return E_POINTER; IMediaSample_GetTime(sample, &orig_curtime, &dummy); orig_curtime += pin->filter->start_time; IReferenceClock_GetTime(clock, &graphtime); if (devtype == VideoDevice) { /* PTS from video devices is unreliable. */ IReferenceClock_GetTime(clock, &curtime); } else { IMediaSample_GetTime(sample, &curtime, &dummy); if(curtime > 400000000000000000LL) { /* initial frames sometimes start < 0 (shown as a very large number here, like 437650244077016960 which FFmpeg doesn't like. TODO figure out math. For now just drop them. */ av_log(NULL, AV_LOG_DEBUG, "dshow dropping initial (or ending) audio frame with odd PTS too high %"PRId64"\n", curtime); return S_OK; } curtime += pin->filter->start_time; } buf_size = IMediaSample_GetActualDataLength(sample); IMediaSample_GetPointer(sample, &buf); priv_data = pin->filter->priv_data; s = priv_data; ctx = s->priv_data; index = pin->filter->stream_index; av_log(NULL, AV_LOG_VERBOSE, "dshow passing through packet of type %s size %8d " "timestamp %"PRId64" orig timestamp %"PRId64" graph timestamp %"PRId64" diff %"PRId64" %s\n", devtypename, buf_size, curtime, orig_curtime, graphtime, graphtime - orig_curtime, ctx->device_name[devtype]); pin->filter->callback(priv_data, index, buf, buf_size, curtime, devtype); return S_OK; } long WINAPI libAVMemInputPin_ReceiveMultiple(libAVMemInputPin *this, IMediaSample **samples, long n, long *nproc) { int i; dshowdebug("libAVMemInputPin_ReceiveMultiple(%p)\n", this); for (i = 0; i < n; i++) libAVMemInputPin_Receive(this, samples[i]); *nproc = n; return S_OK; } long WINAPI libAVMemInputPin_ReceiveCanBlock(libAVMemInputPin *this) { dshowdebug("libAVMemInputPin_ReceiveCanBlock(%p)\n", this); /* I swear I will not block. */ return S_FALSE; } void libAVMemInputPin_Destroy(libAVMemInputPin *this) { libAVPin *pin = (libAVPin *) ((uint8_t *) this - imemoffset); dshowdebug("libAVMemInputPin_Destroy(%p)\n", this); libAVPin_Destroy(pin); }