michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "dshow.h" michael@0: #include "dmodshow.h" michael@0: #include "wmcodecdsp.h" michael@0: #include "dmoreg.h" michael@0: #include "DirectShowUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/RefPtr.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: #if defined(PR_LOGGING) michael@0: michael@0: // Create a table which maps GUIDs to a string representation of the GUID. michael@0: // This is useful for debugging purposes, for logging the GUIDs of media types. michael@0: // This is only available when logging is enabled, i.e. not in release builds. michael@0: struct GuidToName { michael@0: const char* name; michael@0: const GUID guid; michael@0: }; michael@0: michael@0: #pragma push_macro("OUR_GUID_ENTRY") michael@0: #undef OUR_GUID_ENTRY michael@0: #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ michael@0: { #name, {l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8} }, michael@0: michael@0: static const GuidToName GuidToNameTable[] = { michael@0: #include michael@0: }; michael@0: michael@0: #pragma pop_macro("OUR_GUID_ENTRY") michael@0: michael@0: const char* michael@0: GetDirectShowGuidName(const GUID& aGuid) michael@0: { michael@0: const size_t len = ArrayLength(GuidToNameTable); michael@0: for (unsigned i = 0; i < len; i++) { michael@0: if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) { michael@0: return GuidToNameTable[i].name; michael@0: } michael@0: } michael@0: return "Unknown"; michael@0: } michael@0: #endif // PR_LOGGING michael@0: michael@0: void michael@0: RemoveGraphFromRunningObjectTable(DWORD aRotRegister) michael@0: { michael@0: nsRefPtr runningObjectTable; michael@0: if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) { michael@0: runningObjectTable->Revoke(aRotRegister); michael@0: } michael@0: } michael@0: michael@0: HRESULT michael@0: AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: nsRefPtr moniker; michael@0: nsRefPtr runningObjectTable; michael@0: michael@0: hr = GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: const size_t STRING_LENGTH = 256; michael@0: WCHAR wsz[STRING_LENGTH]; michael@0: michael@0: StringCchPrintfW(wsz, michael@0: STRING_LENGTH, michael@0: L"FilterGraph %08x pid %08x", michael@0: (DWORD_PTR)aUnkGraph, michael@0: GetCurrentProcessId()); michael@0: michael@0: hr = CreateItemMoniker(L"!", wsz, getter_AddRefs(moniker)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: hr = runningObjectTable->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, michael@0: aUnkGraph, michael@0: moniker, michael@0: aOutRotRegister); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: const char* michael@0: GetGraphNotifyString(long evCode) michael@0: { michael@0: #define CASE(x) case x: return #x michael@0: switch(evCode) { michael@0: CASE(EC_ACTIVATE); // A video window is being activated or deactivated. michael@0: CASE(EC_BANDWIDTHCHANGE); // Not supported. michael@0: CASE(EC_BUFFERING_DATA); // The graph is buffering data, or has stopped buffering data. michael@0: CASE(EC_BUILT); // Send by the Video Control when a graph has been built. Not forwarded to applications. michael@0: CASE(EC_CLOCK_CHANGED); // The reference clock has changed. michael@0: CASE(EC_CLOCK_UNSET); // The clock provider was disconnected. michael@0: CASE(EC_CODECAPI_EVENT); // Sent by an encoder to signal an encoding event. michael@0: CASE(EC_COMPLETE); // All data from a particular stream has been rendered. michael@0: CASE(EC_CONTENTPROPERTY_CHANGED); // Not supported. michael@0: CASE(EC_DEVICE_LOST); // A Plug and Play device was removed or has become available again. michael@0: CASE(EC_DISPLAY_CHANGED); // The display mode has changed. michael@0: CASE(EC_END_OF_SEGMENT); // The end of a segment has been reached. michael@0: CASE(EC_EOS_SOON); // Not supported. michael@0: CASE(EC_ERROR_STILLPLAYING); // An asynchronous command to run the graph has failed. michael@0: CASE(EC_ERRORABORT); // An operation was aborted because of an error. michael@0: CASE(EC_ERRORABORTEX); // An operation was aborted because of an error. michael@0: CASE(EC_EXTDEVICE_MODE_CHANGE); // Not supported. michael@0: CASE(EC_FILE_CLOSED); // The source file was closed because of an unexpected event. michael@0: CASE(EC_FULLSCREEN_LOST); // The video renderer is switching out of full-screen mode. michael@0: CASE(EC_GRAPH_CHANGED); // The filter graph has changed. michael@0: CASE(EC_LENGTH_CHANGED); // The length of a source has changed. michael@0: CASE(EC_LOADSTATUS); // Notifies the application of progress when opening a network file. michael@0: CASE(EC_MARKER_HIT); // Not supported. michael@0: CASE(EC_NEED_RESTART); // A filter is requesting that the graph be restarted. michael@0: CASE(EC_NEW_PIN); // Not supported. michael@0: CASE(EC_NOTIFY_WINDOW); // Notifies a filter of the video renderer's window. michael@0: CASE(EC_OLE_EVENT); // A filter is passing a text string to the application. michael@0: CASE(EC_OPENING_FILE); // The graph is opening a file, or has finished opening a file. michael@0: CASE(EC_PALETTE_CHANGED); // The video palette has changed. michael@0: CASE(EC_PAUSED); // A pause request has completed. michael@0: CASE(EC_PLEASE_REOPEN); // The source file has changed. michael@0: CASE(EC_PREPROCESS_COMPLETE); // Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding. michael@0: CASE(EC_PROCESSING_LATENCY); // Indicates the amount of time that a component is taking to process each sample. michael@0: CASE(EC_QUALITY_CHANGE); // The graph is dropping samples, for quality control. michael@0: //CASE(EC_RENDER_FINISHED); // Not supported. michael@0: CASE(EC_REPAINT); // A video renderer requires a repaint. michael@0: CASE(EC_SAMPLE_LATENCY); // Specifies how far behind schedule a component is for processing samples. michael@0: //CASE(EC_SAMPLE_NEEDED); // Requests a new input sample from the Enhanced Video Renderer (EVR) filter. michael@0: CASE(EC_SCRUB_TIME); // Specifies the time stamp for the most recent frame step. michael@0: CASE(EC_SEGMENT_STARTED); // A new segment has started. michael@0: CASE(EC_SHUTTING_DOWN); // The filter graph is shutting down, prior to being destroyed. michael@0: CASE(EC_SNDDEV_IN_ERROR); // A device error has occurred in an audio capture filter. michael@0: CASE(EC_SNDDEV_OUT_ERROR); // A device error has occurred in an audio renderer filter. michael@0: CASE(EC_STARVATION); // A filter is not receiving enough data. michael@0: CASE(EC_STATE_CHANGE); // The filter graph has changed state. michael@0: CASE(EC_STATUS); // Contains two arbitrary status strings. michael@0: CASE(EC_STEP_COMPLETE); // A filter performing frame stepping has stepped the specified number of frames. michael@0: CASE(EC_STREAM_CONTROL_STARTED); // A stream-control start command has taken effect. michael@0: CASE(EC_STREAM_CONTROL_STOPPED); // A stream-control stop command has taken effect. michael@0: CASE(EC_STREAM_ERROR_STILLPLAYING); // An error has occurred in a stream. The stream is still playing. michael@0: CASE(EC_STREAM_ERROR_STOPPED); // A stream has stopped because of an error. michael@0: CASE(EC_TIMECODE_AVAILABLE); // Not supported. michael@0: CASE(EC_UNBUILT); // Send by the Video Control when a graph has been torn down. Not forwarded to applications. michael@0: CASE(EC_USERABORT); // The user has terminated playback. michael@0: CASE(EC_VIDEO_SIZE_CHANGED); // The native video size has changed. michael@0: CASE(EC_VIDEOFRAMEREADY); // A video frame is ready for display. michael@0: CASE(EC_VMR_RECONNECTION_FAILED); // Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder. michael@0: CASE(EC_VMR_RENDERDEVICE_SET); // Sent when the VMR has selected its rendering mechanism. michael@0: CASE(EC_VMR_SURFACE_FLIPPED); // Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented. michael@0: CASE(EC_WINDOW_DESTROYED); // The video renderer was destroyed or removed from the graph. michael@0: CASE(EC_WMT_EVENT); // Sent by the WM ASF Reader filter when it reads ASF files protected by digital rights management (DRM). michael@0: CASE(EC_WMT_INDEX_EVENT); // Sent when an application uses the WM ASF Writer to index Windows Media Video files. michael@0: CASE(S_OK); // Success. michael@0: CASE(VFW_S_AUDIO_NOT_RENDERED); // Partial success; the audio was not rendered. michael@0: CASE(VFW_S_DUPLICATE_NAME); // Success; the Filter Graph Manager modified a filter name to avoid duplication. michael@0: CASE(VFW_S_PARTIAL_RENDER); // Partial success; some of the streams in this movie are in an unsupported format. michael@0: CASE(VFW_S_VIDEO_NOT_RENDERED); // Partial success; the video was not rendered. michael@0: CASE(E_ABORT); // Operation aborted. michael@0: CASE(E_OUTOFMEMORY); // Insufficient memory. michael@0: CASE(E_POINTER); // Null pointer argument. michael@0: CASE(VFW_E_CANNOT_CONNECT); // No combination of intermediate filters could be found to make the connection. michael@0: CASE(VFW_E_CANNOT_RENDER); // No combination of filters could be found to render the stream. michael@0: CASE(VFW_E_NO_ACCEPTABLE_TYPES); // There is no common media type between these pins. michael@0: CASE(VFW_E_NOT_IN_GRAPH); michael@0: michael@0: default: michael@0: return "Unknown Code"; michael@0: }; michael@0: #undef CASE michael@0: } michael@0: michael@0: HRESULT michael@0: CreateAndAddFilter(IGraphBuilder* aGraph, michael@0: REFGUID aFilterClsId, michael@0: LPCWSTR aFilterName, michael@0: IBaseFilter **aOutFilter) michael@0: { michael@0: NS_ENSURE_TRUE(aGraph, E_POINTER); michael@0: NS_ENSURE_TRUE(aOutFilter, E_POINTER); michael@0: HRESULT hr; michael@0: michael@0: nsRefPtr filter; michael@0: hr = CoCreateInstance(aFilterClsId, michael@0: nullptr, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_IBaseFilter, michael@0: getter_AddRefs(filter)); michael@0: if (FAILED(hr)) { michael@0: // Object probably not available on this system. michael@0: return hr; michael@0: } michael@0: michael@0: hr = aGraph->AddFilter(filter, aFilterName); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: filter.forget(aOutFilter); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT michael@0: AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, michael@0: IBaseFilter **aOutFilter) michael@0: { michael@0: NS_ENSURE_TRUE(aGraph, E_POINTER); michael@0: NS_ENSURE_TRUE(aOutFilter, E_POINTER); michael@0: HRESULT hr; michael@0: michael@0: // Create the wrapper filter. michael@0: nsRefPtr filter; michael@0: hr = CoCreateInstance(CLSID_DMOWrapperFilter, michael@0: nullptr, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_IBaseFilter, michael@0: getter_AddRefs(filter)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: // Query for IDMOWrapperFilter. michael@0: nsRefPtr dmoWrapper; michael@0: hr = filter->QueryInterface(IID_IDMOWrapperFilter, michael@0: getter_AddRefs(dmoWrapper)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: hr = dmoWrapper->Init(CLSID_CMP3DecMediaObject, DMOCATEGORY_AUDIO_DECODER); michael@0: if (FAILED(hr)) { michael@0: // Can't instantiate MP3 DMO. It doesn't exist on Windows XP, we're michael@0: // probably hitting that. Don't log warning to console, this is an michael@0: // expected error. michael@0: return hr; michael@0: } michael@0: michael@0: // Add the wrapper filter to graph. michael@0: hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO"); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: filter.forget(aOutFilter); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: // Match a pin by pin direction and connection state. michael@0: HRESULT michael@0: MatchUnconnectedPin(IPin* aPin, michael@0: PIN_DIRECTION aPinDir, michael@0: bool *aOutMatches) michael@0: { michael@0: NS_ENSURE_TRUE(aPin, E_POINTER); michael@0: NS_ENSURE_TRUE(aOutMatches, E_POINTER); michael@0: michael@0: // Ensure the pin is unconnected. michael@0: RefPtr peer; michael@0: HRESULT hr = aPin->ConnectedTo(byRef(peer)); michael@0: if (hr != VFW_E_NOT_CONNECTED) { michael@0: *aOutMatches = false; michael@0: return hr; michael@0: } michael@0: michael@0: // Ensure the pin is of the specified direction. michael@0: PIN_DIRECTION pinDir; michael@0: hr = aPin->QueryDirection(&pinDir); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: *aOutMatches = (pinDir == aPinDir); michael@0: return S_OK; michael@0: } michael@0: michael@0: // Return the first unconnected input pin or output pin. michael@0: TemporaryRef michael@0: GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir) michael@0: { michael@0: RefPtr enumPins; michael@0: michael@0: HRESULT hr = aFilter->EnumPins(byRef(enumPins)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); michael@0: michael@0: // Test each pin to see if it matches the direction we're looking for. michael@0: RefPtr pin; michael@0: while (S_OK == enumPins->Next(1, byRef(pin), nullptr)) { michael@0: bool matches = FALSE; michael@0: if (SUCCEEDED(MatchUnconnectedPin(pin, aPinDir, &matches)) && michael@0: matches) { michael@0: return pin; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: HRESULT michael@0: ConnectFilters(IGraphBuilder* aGraph, michael@0: IBaseFilter* aOutputFilter, michael@0: IBaseFilter* aInputFilter) michael@0: { michael@0: RefPtr output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT); michael@0: NS_ENSURE_TRUE(output, E_FAIL); michael@0: michael@0: RefPtr input = GetUnconnectedPin(aInputFilter, PINDIR_INPUT); michael@0: NS_ENSURE_TRUE(output, E_FAIL); michael@0: michael@0: return aGraph->Connect(output, input); michael@0: } michael@0: michael@0: } // namespace mozilla