Bug 918135 - Windows-specific changes for MP3FrameParser refactor r=cpearce
authorEdwin Flores <eflores@mozilla.com>
Tue, 03 Dec 2013 10:25:27 +1300
changeset 174028 6596cdfe549646565e8f0f3b7bed178ca1fa751d
parent 174027 9b50d00dca640d0df62f5b3c18950cbaf79f79de
child 174029 fbaf0512e37c3dd71e479e2af4f52ef597b82de2
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs918135
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 918135 - Windows-specific changes for MP3FrameParser refactor r=cpearce
content/media/directshow/DirectShowReader.cpp
content/media/directshow/SourceFilter.cpp
content/media/directshow/SourceFilter.h
--- a/content/media/directshow/DirectShowReader.cpp
+++ b/content/media/directshow/DirectShowReader.cpp
@@ -62,16 +62,39 @@ DirectShowReader::~DirectShowReader()
 
 nsresult
 DirectShowReader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   return NS_OK;
 }
 
+// Try to parse the MP3 stream to make sure this is indeed an MP3, get the
+// estimated duration of the stream, and find the offset of the actual MP3
+// frames in the stream, as DirectShow doesn't like large ID3 sections.
+static nsresult
+ParseMP3Headers(MP3FrameParser *aParser, MediaResource *aResource)
+{
+  const uint32_t MAX_READ_SIZE = 4096;
+
+  uint64_t offset = 0;
+  while (aParser->NeedsData() && !aParser->ParsedHeaders()) {
+    uint32_t bytesRead;
+    char buffer[MAX_READ_SIZE];
+    nsresult rv = aResource->ReadAt(offset, buffer,
+                                    MAX_READ_SIZE, &bytesRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aParser->Parse(buffer, bytesRead, offset);
+    offset += bytesRead;
+  }
+
+  return aParser->IsMP3() ? NS_OK : NS_ERROR_FAILURE;
+}
+
 // Windows XP's MP3 decoder filter. This is available on XP only, on Vista
 // and later we can use the DMO Wrapper filter and MP3 decoder DMO.
 static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER =
 { 0x38BE3000, 0xDBF4, 0x11D0, 0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D };
 
 nsresult
 DirectShowReader::ReadMetadata(MediaInfo* aInfo,
                                MetadataTags** aTags)
@@ -84,16 +107,19 @@ DirectShowReader::ReadMetadata(MediaInfo
   // to make graph building more convenient.
   hr = CoCreateInstance(CLSID_FilterGraph,
                         nullptr,
                         CLSCTX_INPROC_SERVER,
                         IID_IGraphBuilder,
                         reinterpret_cast<void**>(static_cast<IGraphBuilder**>(byRef(mGraph))));
   NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE);
 
+  rv = ParseMP3Headers(&mMP3FrameParser, mDecoder->GetResource());
+  NS_ENSURE_SUCCESS(rv, rv);
+
   #ifdef DEBUG
   // Add the graph to the Running Object Table so that we can connect
   // to this graph with GraphEdit/GraphStudio. Note: on Vista and up you must
   // also regsvr32 proppage.dll from the Windows SDK.
   // See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx
   hr = AddGraphToRunningObjectTable(mGraph, &mRotRegister);
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
   #endif
@@ -108,17 +134,17 @@ DirectShowReader::ReadMetadata(MediaInfo
   // Build the graph. Create the filters we need, and connect them. We
   // build the entire graph ourselves to prevent other decoders installed
   // on the system being created and used.
 
   // Our source filters, wraps the MediaResource.
   mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_MPEG1Audio);
   NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_FAILURE);
 
-  rv = mSourceFilter->Init(mDecoder->GetResource());
+  rv = mSourceFilter->Init(mDecoder->GetResource(), mMP3FrameParser.GetMP3Offset());
   NS_ENSURE_SUCCESS(rv, rv);
 
   hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource");
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   // The MPEG demuxer.
   RefPtr<IBaseFilter> demuxer;
   hr = CreateAndAddFilter(mGraph,
@@ -179,21 +205,20 @@ DirectShowReader::ReadMetadata(MediaInfo
 
   DWORD seekCaps = 0;
   hr = mMediaSeeking->GetCapabilities(&seekCaps);
   bool canSeek = ((AM_SEEKING_CanSeekAbsolute & seekCaps) == AM_SEEKING_CanSeekAbsolute);
   if (!canSeek) {
     mDecoder->SetMediaSeekable(false);
   }
 
-  int64_t duration = 0;
-  hr = mMediaSeeking->GetDuration(&duration);
+  int64_t duration = mMP3FrameParser.GetDuration();
   if (SUCCEEDED(hr)) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    mDecoder->SetMediaDuration(RefTimeToUsecs(duration));
+    mDecoder->SetMediaDuration(duration);
   }
 
   LOG("Successfully initialized DirectShow MP3 decoder.");
   LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d",
       mInfo.mAudio.mChannels,
       mInfo.mAudio.mRate,
       RefTimeToUsecs(duration),
       mBytesPerSample);
--- a/content/media/directshow/SourceFilter.cpp
+++ b/content/media/directshow/SourceFilter.cpp
@@ -663,83 +663,25 @@ SourceFilter::GetPin(int n)
 
 // Get's the media type we're supplying.
 const MediaType*
 SourceFilter::GetMediaType() const
 {
   return &mMediaType;
 }
 
-static uint32_t
-Read(MediaResource* aResource,
-     const int64_t aOffset,
-     char* aBuffer,
-     const uint32_t aBytesToRead)
-{
-  uint32_t totalBytesRead = 0;
-  while (totalBytesRead < aBytesToRead) {
-    uint32_t bytesRead = 0;
-    nsresult rv = aResource->ReadAt(aOffset + totalBytesRead,
-                                    aBuffer+totalBytesRead,
-                                    aBytesToRead-totalBytesRead,
-                                    &bytesRead);
-    if (NS_FAILED(rv) || bytesRead == 0) {
-      // Error or end of stream?
-      break;
-    }
-    totalBytesRead += bytesRead;
-  }
-  return totalBytesRead;
-}
-
-// Parses the MP3 stream and returns the offset of the first MP3
-// sync frame after the ID3v2 headers. This is used to trim off
-// the ID3v2 headers, as DirectShow can't handle large ID3v2 tags.
-static nsresult
-GetMP3DataOffset(MediaResource* aResource, int64_t* aOutOffset)
-{
-  MP3FrameParser parser;
-  int64_t offset = 0;
-  const uint32_t len = 1024;
-  char buffer[len];
-  do {
-    uint32_t bytesRead = Read(aResource, offset, buffer, len);
-    if (bytesRead == 0) {
-      break;
-    }
-    parser.Parse(buffer, bytesRead, offset);
-    offset += bytesRead;
-  } while (parser.GetMP3Offset() == -1 && parser.IsMP3());
-
-  if (!parser.IsMP3() || parser.GetMP3Offset() == -1) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *aOutOffset = parser.GetMP3Offset();
-  return NS_OK;
-}
-
 nsresult
-SourceFilter::Init(MediaResource* aResource)
+SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset)
 {
   LOG("SourceFilter::Init()");
 
-  // Get the offset of MP3 data in the stream, and pass that into
-  // the output pin so that the stream that we present to DirectShow
-  // does not contain ID3v2 tags. DirectShow can't properly parse some
-  // streams' ID3v2 tags.
-  int64_t mp3DataOffset = 0;
-  nsresult rv = GetMP3DataOffset(aResource, &mp3DataOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-  LOG("First MP3 sync/data frame lies at offset %lld", mp3DataOffset);
-
   mOutputPin = new OutputPin(aResource,
                              this,
                              mLock,
-                             mp3DataOffset);
+                             aMP3Offset);
   NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 uint32_t
 SourceFilter::GetAndResetBytesConsumedCount()
 {
--- a/content/media/directshow/SourceFilter.h
+++ b/content/media/directshow/SourceFilter.h
@@ -38,17 +38,17 @@ class DECLSPEC_UUID("5c2a7ad0-ba82-4659-
 SourceFilter : public media::BaseFilter
 {
 public:
 
   // Constructs source filter to deliver given media type.
   SourceFilter(const GUID& aMajorType, const GUID& aSubType);
   ~SourceFilter();
 
-  nsresult Init(MediaResource *aResource);
+  nsresult Init(MediaResource *aResource, int64_t aMP3Offset);
 
   // BaseFilter overrides.
   // Only one output - the byte stream.
   int GetPinCount() MOZ_OVERRIDE { return 1; }
 
   media::BasePin* GetPin(int n) MOZ_OVERRIDE;
 
   // Get's the media type we're supplying.