Fri, 16 Jan 2015 04:50:19 +0100
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 |