1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/directshow/DirectShowUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,309 @@ 1.4 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "dshow.h" 1.10 +#include "dmodshow.h" 1.11 +#include "wmcodecdsp.h" 1.12 +#include "dmoreg.h" 1.13 +#include "DirectShowUtils.h" 1.14 +#include "nsAutoPtr.h" 1.15 +#include "mozilla/ArrayUtils.h" 1.16 +#include "mozilla/RefPtr.h" 1.17 + 1.18 +namespace mozilla { 1.19 + 1.20 +#if defined(PR_LOGGING) 1.21 + 1.22 +// Create a table which maps GUIDs to a string representation of the GUID. 1.23 +// This is useful for debugging purposes, for logging the GUIDs of media types. 1.24 +// This is only available when logging is enabled, i.e. not in release builds. 1.25 +struct GuidToName { 1.26 + const char* name; 1.27 + const GUID guid; 1.28 +}; 1.29 + 1.30 +#pragma push_macro("OUR_GUID_ENTRY") 1.31 +#undef OUR_GUID_ENTRY 1.32 +#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ 1.33 +{ #name, {l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8} }, 1.34 + 1.35 +static const GuidToName GuidToNameTable[] = { 1.36 +#include <uuids.h> 1.37 +}; 1.38 + 1.39 +#pragma pop_macro("OUR_GUID_ENTRY") 1.40 + 1.41 +const char* 1.42 +GetDirectShowGuidName(const GUID& aGuid) 1.43 +{ 1.44 + const size_t len = ArrayLength(GuidToNameTable); 1.45 + for (unsigned i = 0; i < len; i++) { 1.46 + if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) { 1.47 + return GuidToNameTable[i].name; 1.48 + } 1.49 + } 1.50 + return "Unknown"; 1.51 +} 1.52 +#endif // PR_LOGGING 1.53 + 1.54 +void 1.55 +RemoveGraphFromRunningObjectTable(DWORD aRotRegister) 1.56 +{ 1.57 + nsRefPtr<IRunningObjectTable> runningObjectTable; 1.58 + if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) { 1.59 + runningObjectTable->Revoke(aRotRegister); 1.60 + } 1.61 +} 1.62 + 1.63 +HRESULT 1.64 +AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister) 1.65 +{ 1.66 + HRESULT hr; 1.67 + 1.68 + nsRefPtr<IMoniker> moniker; 1.69 + nsRefPtr<IRunningObjectTable> runningObjectTable; 1.70 + 1.71 + hr = GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)); 1.72 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.73 + 1.74 + const size_t STRING_LENGTH = 256; 1.75 + WCHAR wsz[STRING_LENGTH]; 1.76 + 1.77 + StringCchPrintfW(wsz, 1.78 + STRING_LENGTH, 1.79 + L"FilterGraph %08x pid %08x", 1.80 + (DWORD_PTR)aUnkGraph, 1.81 + GetCurrentProcessId()); 1.82 + 1.83 + hr = CreateItemMoniker(L"!", wsz, getter_AddRefs(moniker)); 1.84 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.85 + 1.86 + hr = runningObjectTable->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, 1.87 + aUnkGraph, 1.88 + moniker, 1.89 + aOutRotRegister); 1.90 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.91 + 1.92 + return S_OK; 1.93 +} 1.94 + 1.95 +const char* 1.96 +GetGraphNotifyString(long evCode) 1.97 +{ 1.98 +#define CASE(x) case x: return #x 1.99 + switch(evCode) { 1.100 + CASE(EC_ACTIVATE); // A video window is being activated or deactivated. 1.101 + CASE(EC_BANDWIDTHCHANGE); // Not supported. 1.102 + CASE(EC_BUFFERING_DATA); // The graph is buffering data, or has stopped buffering data. 1.103 + CASE(EC_BUILT); // Send by the Video Control when a graph has been built. Not forwarded to applications. 1.104 + CASE(EC_CLOCK_CHANGED); // The reference clock has changed. 1.105 + CASE(EC_CLOCK_UNSET); // The clock provider was disconnected. 1.106 + CASE(EC_CODECAPI_EVENT); // Sent by an encoder to signal an encoding event. 1.107 + CASE(EC_COMPLETE); // All data from a particular stream has been rendered. 1.108 + CASE(EC_CONTENTPROPERTY_CHANGED); // Not supported. 1.109 + CASE(EC_DEVICE_LOST); // A Plug and Play device was removed or has become available again. 1.110 + CASE(EC_DISPLAY_CHANGED); // The display mode has changed. 1.111 + CASE(EC_END_OF_SEGMENT); // The end of a segment has been reached. 1.112 + CASE(EC_EOS_SOON); // Not supported. 1.113 + CASE(EC_ERROR_STILLPLAYING); // An asynchronous command to run the graph has failed. 1.114 + CASE(EC_ERRORABORT); // An operation was aborted because of an error. 1.115 + CASE(EC_ERRORABORTEX); // An operation was aborted because of an error. 1.116 + CASE(EC_EXTDEVICE_MODE_CHANGE); // Not supported. 1.117 + CASE(EC_FILE_CLOSED); // The source file was closed because of an unexpected event. 1.118 + CASE(EC_FULLSCREEN_LOST); // The video renderer is switching out of full-screen mode. 1.119 + CASE(EC_GRAPH_CHANGED); // The filter graph has changed. 1.120 + CASE(EC_LENGTH_CHANGED); // The length of a source has changed. 1.121 + CASE(EC_LOADSTATUS); // Notifies the application of progress when opening a network file. 1.122 + CASE(EC_MARKER_HIT); // Not supported. 1.123 + CASE(EC_NEED_RESTART); // A filter is requesting that the graph be restarted. 1.124 + CASE(EC_NEW_PIN); // Not supported. 1.125 + CASE(EC_NOTIFY_WINDOW); // Notifies a filter of the video renderer's window. 1.126 + CASE(EC_OLE_EVENT); // A filter is passing a text string to the application. 1.127 + CASE(EC_OPENING_FILE); // The graph is opening a file, or has finished opening a file. 1.128 + CASE(EC_PALETTE_CHANGED); // The video palette has changed. 1.129 + CASE(EC_PAUSED); // A pause request has completed. 1.130 + CASE(EC_PLEASE_REOPEN); // The source file has changed. 1.131 + CASE(EC_PREPROCESS_COMPLETE); // Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding. 1.132 + CASE(EC_PROCESSING_LATENCY); // Indicates the amount of time that a component is taking to process each sample. 1.133 + CASE(EC_QUALITY_CHANGE); // The graph is dropping samples, for quality control. 1.134 + //CASE(EC_RENDER_FINISHED); // Not supported. 1.135 + CASE(EC_REPAINT); // A video renderer requires a repaint. 1.136 + CASE(EC_SAMPLE_LATENCY); // Specifies how far behind schedule a component is for processing samples. 1.137 + //CASE(EC_SAMPLE_NEEDED); // Requests a new input sample from the Enhanced Video Renderer (EVR) filter. 1.138 + CASE(EC_SCRUB_TIME); // Specifies the time stamp for the most recent frame step. 1.139 + CASE(EC_SEGMENT_STARTED); // A new segment has started. 1.140 + CASE(EC_SHUTTING_DOWN); // The filter graph is shutting down, prior to being destroyed. 1.141 + CASE(EC_SNDDEV_IN_ERROR); // A device error has occurred in an audio capture filter. 1.142 + CASE(EC_SNDDEV_OUT_ERROR); // A device error has occurred in an audio renderer filter. 1.143 + CASE(EC_STARVATION); // A filter is not receiving enough data. 1.144 + CASE(EC_STATE_CHANGE); // The filter graph has changed state. 1.145 + CASE(EC_STATUS); // Contains two arbitrary status strings. 1.146 + CASE(EC_STEP_COMPLETE); // A filter performing frame stepping has stepped the specified number of frames. 1.147 + CASE(EC_STREAM_CONTROL_STARTED); // A stream-control start command has taken effect. 1.148 + CASE(EC_STREAM_CONTROL_STOPPED); // A stream-control stop command has taken effect. 1.149 + CASE(EC_STREAM_ERROR_STILLPLAYING); // An error has occurred in a stream. The stream is still playing. 1.150 + CASE(EC_STREAM_ERROR_STOPPED); // A stream has stopped because of an error. 1.151 + CASE(EC_TIMECODE_AVAILABLE); // Not supported. 1.152 + CASE(EC_UNBUILT); // Send by the Video Control when a graph has been torn down. Not forwarded to applications. 1.153 + CASE(EC_USERABORT); // The user has terminated playback. 1.154 + CASE(EC_VIDEO_SIZE_CHANGED); // The native video size has changed. 1.155 + CASE(EC_VIDEOFRAMEREADY); // A video frame is ready for display. 1.156 + 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. 1.157 + CASE(EC_VMR_RENDERDEVICE_SET); // Sent when the VMR has selected its rendering mechanism. 1.158 + CASE(EC_VMR_SURFACE_FLIPPED); // Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented. 1.159 + CASE(EC_WINDOW_DESTROYED); // The video renderer was destroyed or removed from the graph. 1.160 + CASE(EC_WMT_EVENT); // Sent by the WM ASF Reader filter when it reads ASF files protected by digital rights management (DRM). 1.161 + CASE(EC_WMT_INDEX_EVENT); // Sent when an application uses the WM ASF Writer to index Windows Media Video files. 1.162 + CASE(S_OK); // Success. 1.163 + CASE(VFW_S_AUDIO_NOT_RENDERED); // Partial success; the audio was not rendered. 1.164 + CASE(VFW_S_DUPLICATE_NAME); // Success; the Filter Graph Manager modified a filter name to avoid duplication. 1.165 + CASE(VFW_S_PARTIAL_RENDER); // Partial success; some of the streams in this movie are in an unsupported format. 1.166 + CASE(VFW_S_VIDEO_NOT_RENDERED); // Partial success; the video was not rendered. 1.167 + CASE(E_ABORT); // Operation aborted. 1.168 + CASE(E_OUTOFMEMORY); // Insufficient memory. 1.169 + CASE(E_POINTER); // Null pointer argument. 1.170 + CASE(VFW_E_CANNOT_CONNECT); // No combination of intermediate filters could be found to make the connection. 1.171 + CASE(VFW_E_CANNOT_RENDER); // No combination of filters could be found to render the stream. 1.172 + CASE(VFW_E_NO_ACCEPTABLE_TYPES); // There is no common media type between these pins. 1.173 + CASE(VFW_E_NOT_IN_GRAPH); 1.174 + 1.175 + default: 1.176 + return "Unknown Code"; 1.177 + }; 1.178 +#undef CASE 1.179 +} 1.180 + 1.181 +HRESULT 1.182 +CreateAndAddFilter(IGraphBuilder* aGraph, 1.183 + REFGUID aFilterClsId, 1.184 + LPCWSTR aFilterName, 1.185 + IBaseFilter **aOutFilter) 1.186 +{ 1.187 + NS_ENSURE_TRUE(aGraph, E_POINTER); 1.188 + NS_ENSURE_TRUE(aOutFilter, E_POINTER); 1.189 + HRESULT hr; 1.190 + 1.191 + nsRefPtr<IBaseFilter> filter; 1.192 + hr = CoCreateInstance(aFilterClsId, 1.193 + nullptr, 1.194 + CLSCTX_INPROC_SERVER, 1.195 + IID_IBaseFilter, 1.196 + getter_AddRefs(filter)); 1.197 + if (FAILED(hr)) { 1.198 + // Object probably not available on this system. 1.199 + return hr; 1.200 + } 1.201 + 1.202 + hr = aGraph->AddFilter(filter, aFilterName); 1.203 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.204 + 1.205 + filter.forget(aOutFilter); 1.206 + 1.207 + return S_OK; 1.208 +} 1.209 + 1.210 +HRESULT 1.211 +AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, 1.212 + IBaseFilter **aOutFilter) 1.213 +{ 1.214 + NS_ENSURE_TRUE(aGraph, E_POINTER); 1.215 + NS_ENSURE_TRUE(aOutFilter, E_POINTER); 1.216 + HRESULT hr; 1.217 + 1.218 + // Create the wrapper filter. 1.219 + nsRefPtr<IBaseFilter> filter; 1.220 + hr = CoCreateInstance(CLSID_DMOWrapperFilter, 1.221 + nullptr, 1.222 + CLSCTX_INPROC_SERVER, 1.223 + IID_IBaseFilter, 1.224 + getter_AddRefs(filter)); 1.225 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.226 + 1.227 + // Query for IDMOWrapperFilter. 1.228 + nsRefPtr<IDMOWrapperFilter> dmoWrapper; 1.229 + hr = filter->QueryInterface(IID_IDMOWrapperFilter, 1.230 + getter_AddRefs(dmoWrapper)); 1.231 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.232 + 1.233 + hr = dmoWrapper->Init(CLSID_CMP3DecMediaObject, DMOCATEGORY_AUDIO_DECODER); 1.234 + if (FAILED(hr)) { 1.235 + // Can't instantiate MP3 DMO. It doesn't exist on Windows XP, we're 1.236 + // probably hitting that. Don't log warning to console, this is an 1.237 + // expected error. 1.238 + return hr; 1.239 + } 1.240 + 1.241 + // Add the wrapper filter to graph. 1.242 + hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO"); 1.243 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.244 + 1.245 + filter.forget(aOutFilter); 1.246 + 1.247 + return S_OK; 1.248 +} 1.249 + 1.250 +// Match a pin by pin direction and connection state. 1.251 +HRESULT 1.252 +MatchUnconnectedPin(IPin* aPin, 1.253 + PIN_DIRECTION aPinDir, 1.254 + bool *aOutMatches) 1.255 +{ 1.256 + NS_ENSURE_TRUE(aPin, E_POINTER); 1.257 + NS_ENSURE_TRUE(aOutMatches, E_POINTER); 1.258 + 1.259 + // Ensure the pin is unconnected. 1.260 + RefPtr<IPin> peer; 1.261 + HRESULT hr = aPin->ConnectedTo(byRef(peer)); 1.262 + if (hr != VFW_E_NOT_CONNECTED) { 1.263 + *aOutMatches = false; 1.264 + return hr; 1.265 + } 1.266 + 1.267 + // Ensure the pin is of the specified direction. 1.268 + PIN_DIRECTION pinDir; 1.269 + hr = aPin->QueryDirection(&pinDir); 1.270 + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); 1.271 + 1.272 + *aOutMatches = (pinDir == aPinDir); 1.273 + return S_OK; 1.274 +} 1.275 + 1.276 +// Return the first unconnected input pin or output pin. 1.277 +TemporaryRef<IPin> 1.278 +GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir) 1.279 +{ 1.280 + RefPtr<IEnumPins> enumPins; 1.281 + 1.282 + HRESULT hr = aFilter->EnumPins(byRef(enumPins)); 1.283 + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); 1.284 + 1.285 + // Test each pin to see if it matches the direction we're looking for. 1.286 + RefPtr<IPin> pin; 1.287 + while (S_OK == enumPins->Next(1, byRef(pin), nullptr)) { 1.288 + bool matches = FALSE; 1.289 + if (SUCCEEDED(MatchUnconnectedPin(pin, aPinDir, &matches)) && 1.290 + matches) { 1.291 + return pin; 1.292 + } 1.293 + } 1.294 + 1.295 + return nullptr; 1.296 +} 1.297 + 1.298 +HRESULT 1.299 +ConnectFilters(IGraphBuilder* aGraph, 1.300 + IBaseFilter* aOutputFilter, 1.301 + IBaseFilter* aInputFilter) 1.302 +{ 1.303 + RefPtr<IPin> output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT); 1.304 + NS_ENSURE_TRUE(output, E_FAIL); 1.305 + 1.306 + RefPtr<IPin> input = GetUnconnectedPin(aInputFilter, PINDIR_INPUT); 1.307 + NS_ENSURE_TRUE(output, E_FAIL); 1.308 + 1.309 + return aGraph->Connect(output, input); 1.310 +} 1.311 + 1.312 +} // namespace mozilla