content/media/directshow/DirectShowUtils.cpp

changeset 0
6474c204b198
     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

mercurial