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