content/media/directshow/DirectShowUtils.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "dshow.h"
michael@0 7 #include "dmodshow.h"
michael@0 8 #include "wmcodecdsp.h"
michael@0 9 #include "dmoreg.h"
michael@0 10 #include "DirectShowUtils.h"
michael@0 11 #include "nsAutoPtr.h"
michael@0 12 #include "mozilla/ArrayUtils.h"
michael@0 13 #include "mozilla/RefPtr.h"
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16
michael@0 17 #if defined(PR_LOGGING)
michael@0 18
michael@0 19 // Create a table which maps GUIDs to a string representation of the GUID.
michael@0 20 // This is useful for debugging purposes, for logging the GUIDs of media types.
michael@0 21 // This is only available when logging is enabled, i.e. not in release builds.
michael@0 22 struct GuidToName {
michael@0 23 const char* name;
michael@0 24 const GUID guid;
michael@0 25 };
michael@0 26
michael@0 27 #pragma push_macro("OUR_GUID_ENTRY")
michael@0 28 #undef OUR_GUID_ENTRY
michael@0 29 #define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
michael@0 30 { #name, {l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8} },
michael@0 31
michael@0 32 static const GuidToName GuidToNameTable[] = {
michael@0 33 #include <uuids.h>
michael@0 34 };
michael@0 35
michael@0 36 #pragma pop_macro("OUR_GUID_ENTRY")
michael@0 37
michael@0 38 const char*
michael@0 39 GetDirectShowGuidName(const GUID& aGuid)
michael@0 40 {
michael@0 41 const size_t len = ArrayLength(GuidToNameTable);
michael@0 42 for (unsigned i = 0; i < len; i++) {
michael@0 43 if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) {
michael@0 44 return GuidToNameTable[i].name;
michael@0 45 }
michael@0 46 }
michael@0 47 return "Unknown";
michael@0 48 }
michael@0 49 #endif // PR_LOGGING
michael@0 50
michael@0 51 void
michael@0 52 RemoveGraphFromRunningObjectTable(DWORD aRotRegister)
michael@0 53 {
michael@0 54 nsRefPtr<IRunningObjectTable> runningObjectTable;
michael@0 55 if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) {
michael@0 56 runningObjectTable->Revoke(aRotRegister);
michael@0 57 }
michael@0 58 }
michael@0 59
michael@0 60 HRESULT
michael@0 61 AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister)
michael@0 62 {
michael@0 63 HRESULT hr;
michael@0 64
michael@0 65 nsRefPtr<IMoniker> moniker;
michael@0 66 nsRefPtr<IRunningObjectTable> runningObjectTable;
michael@0 67
michael@0 68 hr = GetRunningObjectTable(0, getter_AddRefs(runningObjectTable));
michael@0 69 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 70
michael@0 71 const size_t STRING_LENGTH = 256;
michael@0 72 WCHAR wsz[STRING_LENGTH];
michael@0 73
michael@0 74 StringCchPrintfW(wsz,
michael@0 75 STRING_LENGTH,
michael@0 76 L"FilterGraph %08x pid %08x",
michael@0 77 (DWORD_PTR)aUnkGraph,
michael@0 78 GetCurrentProcessId());
michael@0 79
michael@0 80 hr = CreateItemMoniker(L"!", wsz, getter_AddRefs(moniker));
michael@0 81 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 82
michael@0 83 hr = runningObjectTable->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE,
michael@0 84 aUnkGraph,
michael@0 85 moniker,
michael@0 86 aOutRotRegister);
michael@0 87 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 88
michael@0 89 return S_OK;
michael@0 90 }
michael@0 91
michael@0 92 const char*
michael@0 93 GetGraphNotifyString(long evCode)
michael@0 94 {
michael@0 95 #define CASE(x) case x: return #x
michael@0 96 switch(evCode) {
michael@0 97 CASE(EC_ACTIVATE); // A video window is being activated or deactivated.
michael@0 98 CASE(EC_BANDWIDTHCHANGE); // Not supported.
michael@0 99 CASE(EC_BUFFERING_DATA); // The graph is buffering data, or has stopped buffering data.
michael@0 100 CASE(EC_BUILT); // Send by the Video Control when a graph has been built. Not forwarded to applications.
michael@0 101 CASE(EC_CLOCK_CHANGED); // The reference clock has changed.
michael@0 102 CASE(EC_CLOCK_UNSET); // The clock provider was disconnected.
michael@0 103 CASE(EC_CODECAPI_EVENT); // Sent by an encoder to signal an encoding event.
michael@0 104 CASE(EC_COMPLETE); // All data from a particular stream has been rendered.
michael@0 105 CASE(EC_CONTENTPROPERTY_CHANGED); // Not supported.
michael@0 106 CASE(EC_DEVICE_LOST); // A Plug and Play device was removed or has become available again.
michael@0 107 CASE(EC_DISPLAY_CHANGED); // The display mode has changed.
michael@0 108 CASE(EC_END_OF_SEGMENT); // The end of a segment has been reached.
michael@0 109 CASE(EC_EOS_SOON); // Not supported.
michael@0 110 CASE(EC_ERROR_STILLPLAYING); // An asynchronous command to run the graph has failed.
michael@0 111 CASE(EC_ERRORABORT); // An operation was aborted because of an error.
michael@0 112 CASE(EC_ERRORABORTEX); // An operation was aborted because of an error.
michael@0 113 CASE(EC_EXTDEVICE_MODE_CHANGE); // Not supported.
michael@0 114 CASE(EC_FILE_CLOSED); // The source file was closed because of an unexpected event.
michael@0 115 CASE(EC_FULLSCREEN_LOST); // The video renderer is switching out of full-screen mode.
michael@0 116 CASE(EC_GRAPH_CHANGED); // The filter graph has changed.
michael@0 117 CASE(EC_LENGTH_CHANGED); // The length of a source has changed.
michael@0 118 CASE(EC_LOADSTATUS); // Notifies the application of progress when opening a network file.
michael@0 119 CASE(EC_MARKER_HIT); // Not supported.
michael@0 120 CASE(EC_NEED_RESTART); // A filter is requesting that the graph be restarted.
michael@0 121 CASE(EC_NEW_PIN); // Not supported.
michael@0 122 CASE(EC_NOTIFY_WINDOW); // Notifies a filter of the video renderer's window.
michael@0 123 CASE(EC_OLE_EVENT); // A filter is passing a text string to the application.
michael@0 124 CASE(EC_OPENING_FILE); // The graph is opening a file, or has finished opening a file.
michael@0 125 CASE(EC_PALETTE_CHANGED); // The video palette has changed.
michael@0 126 CASE(EC_PAUSED); // A pause request has completed.
michael@0 127 CASE(EC_PLEASE_REOPEN); // The source file has changed.
michael@0 128 CASE(EC_PREPROCESS_COMPLETE); // Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding.
michael@0 129 CASE(EC_PROCESSING_LATENCY); // Indicates the amount of time that a component is taking to process each sample.
michael@0 130 CASE(EC_QUALITY_CHANGE); // The graph is dropping samples, for quality control.
michael@0 131 //CASE(EC_RENDER_FINISHED); // Not supported.
michael@0 132 CASE(EC_REPAINT); // A video renderer requires a repaint.
michael@0 133 CASE(EC_SAMPLE_LATENCY); // Specifies how far behind schedule a component is for processing samples.
michael@0 134 //CASE(EC_SAMPLE_NEEDED); // Requests a new input sample from the Enhanced Video Renderer (EVR) filter.
michael@0 135 CASE(EC_SCRUB_TIME); // Specifies the time stamp for the most recent frame step.
michael@0 136 CASE(EC_SEGMENT_STARTED); // A new segment has started.
michael@0 137 CASE(EC_SHUTTING_DOWN); // The filter graph is shutting down, prior to being destroyed.
michael@0 138 CASE(EC_SNDDEV_IN_ERROR); // A device error has occurred in an audio capture filter.
michael@0 139 CASE(EC_SNDDEV_OUT_ERROR); // A device error has occurred in an audio renderer filter.
michael@0 140 CASE(EC_STARVATION); // A filter is not receiving enough data.
michael@0 141 CASE(EC_STATE_CHANGE); // The filter graph has changed state.
michael@0 142 CASE(EC_STATUS); // Contains two arbitrary status strings.
michael@0 143 CASE(EC_STEP_COMPLETE); // A filter performing frame stepping has stepped the specified number of frames.
michael@0 144 CASE(EC_STREAM_CONTROL_STARTED); // A stream-control start command has taken effect.
michael@0 145 CASE(EC_STREAM_CONTROL_STOPPED); // A stream-control stop command has taken effect.
michael@0 146 CASE(EC_STREAM_ERROR_STILLPLAYING); // An error has occurred in a stream. The stream is still playing.
michael@0 147 CASE(EC_STREAM_ERROR_STOPPED); // A stream has stopped because of an error.
michael@0 148 CASE(EC_TIMECODE_AVAILABLE); // Not supported.
michael@0 149 CASE(EC_UNBUILT); // Send by the Video Control when a graph has been torn down. Not forwarded to applications.
michael@0 150 CASE(EC_USERABORT); // The user has terminated playback.
michael@0 151 CASE(EC_VIDEO_SIZE_CHANGED); // The native video size has changed.
michael@0 152 CASE(EC_VIDEOFRAMEREADY); // A video frame is ready for display.
michael@0 153 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 154 CASE(EC_VMR_RENDERDEVICE_SET); // Sent when the VMR has selected its rendering mechanism.
michael@0 155 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 156 CASE(EC_WINDOW_DESTROYED); // The video renderer was destroyed or removed from the graph.
michael@0 157 CASE(EC_WMT_EVENT); // Sent by the WM ASF Reader filter when it reads ASF files protected by digital rights management (DRM).
michael@0 158 CASE(EC_WMT_INDEX_EVENT); // Sent when an application uses the WM ASF Writer to index Windows Media Video files.
michael@0 159 CASE(S_OK); // Success.
michael@0 160 CASE(VFW_S_AUDIO_NOT_RENDERED); // Partial success; the audio was not rendered.
michael@0 161 CASE(VFW_S_DUPLICATE_NAME); // Success; the Filter Graph Manager modified a filter name to avoid duplication.
michael@0 162 CASE(VFW_S_PARTIAL_RENDER); // Partial success; some of the streams in this movie are in an unsupported format.
michael@0 163 CASE(VFW_S_VIDEO_NOT_RENDERED); // Partial success; the video was not rendered.
michael@0 164 CASE(E_ABORT); // Operation aborted.
michael@0 165 CASE(E_OUTOFMEMORY); // Insufficient memory.
michael@0 166 CASE(E_POINTER); // Null pointer argument.
michael@0 167 CASE(VFW_E_CANNOT_CONNECT); // No combination of intermediate filters could be found to make the connection.
michael@0 168 CASE(VFW_E_CANNOT_RENDER); // No combination of filters could be found to render the stream.
michael@0 169 CASE(VFW_E_NO_ACCEPTABLE_TYPES); // There is no common media type between these pins.
michael@0 170 CASE(VFW_E_NOT_IN_GRAPH);
michael@0 171
michael@0 172 default:
michael@0 173 return "Unknown Code";
michael@0 174 };
michael@0 175 #undef CASE
michael@0 176 }
michael@0 177
michael@0 178 HRESULT
michael@0 179 CreateAndAddFilter(IGraphBuilder* aGraph,
michael@0 180 REFGUID aFilterClsId,
michael@0 181 LPCWSTR aFilterName,
michael@0 182 IBaseFilter **aOutFilter)
michael@0 183 {
michael@0 184 NS_ENSURE_TRUE(aGraph, E_POINTER);
michael@0 185 NS_ENSURE_TRUE(aOutFilter, E_POINTER);
michael@0 186 HRESULT hr;
michael@0 187
michael@0 188 nsRefPtr<IBaseFilter> filter;
michael@0 189 hr = CoCreateInstance(aFilterClsId,
michael@0 190 nullptr,
michael@0 191 CLSCTX_INPROC_SERVER,
michael@0 192 IID_IBaseFilter,
michael@0 193 getter_AddRefs(filter));
michael@0 194 if (FAILED(hr)) {
michael@0 195 // Object probably not available on this system.
michael@0 196 return hr;
michael@0 197 }
michael@0 198
michael@0 199 hr = aGraph->AddFilter(filter, aFilterName);
michael@0 200 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 201
michael@0 202 filter.forget(aOutFilter);
michael@0 203
michael@0 204 return S_OK;
michael@0 205 }
michael@0 206
michael@0 207 HRESULT
michael@0 208 AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
michael@0 209 IBaseFilter **aOutFilter)
michael@0 210 {
michael@0 211 NS_ENSURE_TRUE(aGraph, E_POINTER);
michael@0 212 NS_ENSURE_TRUE(aOutFilter, E_POINTER);
michael@0 213 HRESULT hr;
michael@0 214
michael@0 215 // Create the wrapper filter.
michael@0 216 nsRefPtr<IBaseFilter> filter;
michael@0 217 hr = CoCreateInstance(CLSID_DMOWrapperFilter,
michael@0 218 nullptr,
michael@0 219 CLSCTX_INPROC_SERVER,
michael@0 220 IID_IBaseFilter,
michael@0 221 getter_AddRefs(filter));
michael@0 222 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 223
michael@0 224 // Query for IDMOWrapperFilter.
michael@0 225 nsRefPtr<IDMOWrapperFilter> dmoWrapper;
michael@0 226 hr = filter->QueryInterface(IID_IDMOWrapperFilter,
michael@0 227 getter_AddRefs(dmoWrapper));
michael@0 228 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 229
michael@0 230 hr = dmoWrapper->Init(CLSID_CMP3DecMediaObject, DMOCATEGORY_AUDIO_DECODER);
michael@0 231 if (FAILED(hr)) {
michael@0 232 // Can't instantiate MP3 DMO. It doesn't exist on Windows XP, we're
michael@0 233 // probably hitting that. Don't log warning to console, this is an
michael@0 234 // expected error.
michael@0 235 return hr;
michael@0 236 }
michael@0 237
michael@0 238 // Add the wrapper filter to graph.
michael@0 239 hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO");
michael@0 240 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 241
michael@0 242 filter.forget(aOutFilter);
michael@0 243
michael@0 244 return S_OK;
michael@0 245 }
michael@0 246
michael@0 247 // Match a pin by pin direction and connection state.
michael@0 248 HRESULT
michael@0 249 MatchUnconnectedPin(IPin* aPin,
michael@0 250 PIN_DIRECTION aPinDir,
michael@0 251 bool *aOutMatches)
michael@0 252 {
michael@0 253 NS_ENSURE_TRUE(aPin, E_POINTER);
michael@0 254 NS_ENSURE_TRUE(aOutMatches, E_POINTER);
michael@0 255
michael@0 256 // Ensure the pin is unconnected.
michael@0 257 RefPtr<IPin> peer;
michael@0 258 HRESULT hr = aPin->ConnectedTo(byRef(peer));
michael@0 259 if (hr != VFW_E_NOT_CONNECTED) {
michael@0 260 *aOutMatches = false;
michael@0 261 return hr;
michael@0 262 }
michael@0 263
michael@0 264 // Ensure the pin is of the specified direction.
michael@0 265 PIN_DIRECTION pinDir;
michael@0 266 hr = aPin->QueryDirection(&pinDir);
michael@0 267 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
michael@0 268
michael@0 269 *aOutMatches = (pinDir == aPinDir);
michael@0 270 return S_OK;
michael@0 271 }
michael@0 272
michael@0 273 // Return the first unconnected input pin or output pin.
michael@0 274 TemporaryRef<IPin>
michael@0 275 GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir)
michael@0 276 {
michael@0 277 RefPtr<IEnumPins> enumPins;
michael@0 278
michael@0 279 HRESULT hr = aFilter->EnumPins(byRef(enumPins));
michael@0 280 NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
michael@0 281
michael@0 282 // Test each pin to see if it matches the direction we're looking for.
michael@0 283 RefPtr<IPin> pin;
michael@0 284 while (S_OK == enumPins->Next(1, byRef(pin), nullptr)) {
michael@0 285 bool matches = FALSE;
michael@0 286 if (SUCCEEDED(MatchUnconnectedPin(pin, aPinDir, &matches)) &&
michael@0 287 matches) {
michael@0 288 return pin;
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 return nullptr;
michael@0 293 }
michael@0 294
michael@0 295 HRESULT
michael@0 296 ConnectFilters(IGraphBuilder* aGraph,
michael@0 297 IBaseFilter* aOutputFilter,
michael@0 298 IBaseFilter* aInputFilter)
michael@0 299 {
michael@0 300 RefPtr<IPin> output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT);
michael@0 301 NS_ENSURE_TRUE(output, E_FAIL);
michael@0 302
michael@0 303 RefPtr<IPin> input = GetUnconnectedPin(aInputFilter, PINDIR_INPUT);
michael@0 304 NS_ENSURE_TRUE(output, E_FAIL);
michael@0 305
michael@0 306 return aGraph->Connect(output, input);
michael@0 307 }
michael@0 308
michael@0 309 } // namespace mozilla

mercurial