Bug 920867 - Split VideoInfo into Video and Audio objects, then encapsulate in new MediaInfo object. r=cpearce
authorMatthew Gregan <kinetik@flim.org>
Fri, 27 Sep 2013 17:22:38 +1200
changeset 163710 473c72edc9ca5589bf416364fe0b50335b7161f1
parent 163709 a98471699e919ec06e6f6d555e9e7608862fbc93
child 163711 808a8b288ef635d1f0837546dd9508bdc225c2f7
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs920867
milestone27.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 920867 - Split VideoInfo into Video and Audio objects, then encapsulate in new MediaInfo object. r=cpearce
content/media/MediaDecoderReader.cpp
content/media/MediaDecoderReader.h
content/media/MediaDecoderStateMachine.cpp
content/media/MediaDecoderStateMachine.h
content/media/apple/AppleMP3Reader.cpp
content/media/apple/AppleMP3Reader.h
content/media/dash/DASHReader.cpp
content/media/dash/DASHReader.h
content/media/directshow/DirectShowReader.cpp
content/media/directshow/DirectShowReader.h
content/media/gstreamer/GStreamerReader.cpp
content/media/gstreamer/GStreamerReader.h
content/media/mediasource/MediaSourceDecoder.cpp
content/media/ogg/OggReader.cpp
content/media/ogg/OggReader.h
content/media/omx/MediaOmxReader.cpp
content/media/omx/MediaOmxReader.h
content/media/plugins/MediaPluginReader.cpp
content/media/plugins/MediaPluginReader.h
content/media/raw/RawReader.cpp
content/media/raw/RawReader.h
content/media/wave/WaveReader.cpp
content/media/wave/WaveReader.h
content/media/webaudio/MediaBufferDecoder.cpp
content/media/webm/WebMReader.cpp
content/media/webm/WebMReader.h
content/media/wmf/WMFReader.cpp
content/media/wmf/WMFReader.h
--- a/content/media/MediaDecoderReader.cpp
+++ b/content/media/MediaDecoderReader.cpp
@@ -530,18 +530,18 @@ nsresult MediaDecoderReader::DecodeToTar
           if (mDecoder->IsShutdown()) {
             return NS_ERROR_FAILURE;
           }
         }
       }
       const AudioData* audio = AudioQueue().PeekFront();
       if (!audio)
         break;
-      CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudioRate);
-      CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudioRate);
+      CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudio.mRate);
+      CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudio.mRate);
       if (!startFrame.isValid() || !targetFrame.isValid()) {
         return NS_ERROR_FAILURE;
       }
       if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
         // Our seek target lies after the frames in this AudioData. Pop it
         // off the queue, and keep decoding forwards.
         delete AudioQueue().PopFront();
         audio = nullptr;
@@ -575,17 +575,17 @@ nsresult MediaDecoderReader::DecodeToTar
         break;
       }
       uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
       uint32_t channels = audio->mChannels;
       nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
       memcpy(audioData.get(),
              audio->mAudioData.get() + (framesToPrune * channels),
              frames * channels * sizeof(AudioDataValue));
-      CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudioRate);
+      CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
       if (!duration.isValid()) {
         return NS_ERROR_FAILURE;
       }
       nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
                                               aTarget,
                                               duration.value(),
                                               frames,
                                               audioData.forget(),
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -26,50 +26,77 @@ class ImageContainer;
 namespace dom {
 class TimeRanges;
 }
 
 // Stores info relevant to presenting media frames.
 class VideoInfo {
 public:
   VideoInfo()
-    : mAudioRate(44100),
-      mAudioChannels(2),
-      mDisplay(0,0),
+    : mDisplay(0,0),
       mStereoMode(STEREO_MODE_MONO),
-      mHasAudio(false),
       mHasVideo(false)
   {}
 
   // Returns true if it's safe to use aPicture as the picture to be
   // extracted inside a frame of size aFrame, and scaled up to and displayed
   // at a size of aDisplay. You should validate the frame, picture, and
   // display regions before using them to display video frames.
   static bool ValidateVideoRegion(const nsIntSize& aFrame,
                                   const nsIntRect& aPicture,
                                   const nsIntSize& aDisplay);
 
-  // Sample rate.
-  uint32_t mAudioRate;
-
-  // Number of audio channels.
-  uint32_t mAudioChannels;
-
   // Size in pixels at which the video is rendered. This is after it has
   // been scaled by its aspect ratio.
   nsIntSize mDisplay;
 
   // Indicates the frame layout for single track stereo videos.
   StereoMode mStereoMode;
 
+  // True if we have an active video bitstream.
+  bool mHasVideo;
+};
+
+class AudioInfo {
+public:
+  AudioInfo()
+    : mRate(44100),
+      mChannels(2),
+      mHasAudio(false)
+  {}
+
+  // Sample rate.
+  uint32_t mRate;
+
+  // Number of audio channels.
+  uint32_t mChannels;
+
   // True if we have an active audio bitstream.
   bool mHasAudio;
+};
 
-  // True if we have an active video bitstream.
-  bool mHasVideo;
+class MediaInfo {
+public:
+  bool HasVideo() const
+  {
+    return mVideo.mHasVideo;
+  }
+
+  bool HasAudio() const
+  {
+    return mAudio.mHasAudio;
+  }
+
+  bool HasValidMedia() const
+  {
+    return HasVideo() || HasAudio();
+  }
+
+  VideoInfo mVideo;
+  AudioInfo mAudio;
 };
 
 // Holds chunk a decoded audio frames.
 class AudioData {
 public:
 
   AudioData(int64_t aOffset,
             int64_t aTime,
@@ -443,17 +470,17 @@ public:
 
   virtual bool HasAudio() = 0;
   virtual bool HasVideo() = 0;
 
   // Read header data for all bitstreams in the file. Fills aInfo with
   // the data required to present the media, and optionally fills *aTags
   // with tag metadata from the file.
   // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags) = 0;
 
   // Stores the presentation time of the first frame we'd be able to play if
   // we started playback at the current position. Returns the first video
   // frame, if we have video.
   virtual VideoData* FindStartTime(int64_t& aOutStartTime);
 
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
@@ -553,17 +580,17 @@ protected:
   // Pumps the decode until we reach frames required to play at time aTarget
   // (usecs).
   nsresult DecodeToTarget(int64_t aTarget);
 
   // Reference to the owning decoder object.
   AbstractMediaDecoder* mDecoder;
 
   // Stores presentation info required for playback.
-  VideoInfo mInfo;
+  MediaInfo mInfo;
 
   // Whether we should accept media that we know we can't play
   // directly, because they have a number of channel higher than
   // what we support.
   bool mIgnoreAudioOutputFormat;
 };
 
 } // namespace mozilla
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -553,19 +553,19 @@ void MediaDecoderStateMachine::SendStrea
     // ignore packet that we've already processed
     return;
   }
   aStream->mLastAudioPacketTime = aAudio->mTime;
   aStream->mLastAudioPacketEndTime = aAudio->GetEnd();
 
   // This logic has to mimic AudioLoop closely to make sure we write
   // the exact same silences
-  CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudioRate,
+  CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudio.mRate,
       aStream->mInitialTime + mStartTime) + aStream->mAudioFramesWritten;
-  CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudioRate, aAudio->mTime);
+  CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime);
   if (!audioWrittenOffset.isValid() || !frameOffset.isValid())
     return;
   if (audioWrittenOffset.value() < frameOffset.value()) {
     // Write silence to catch up
     LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of silence to MediaStream",
                        mDecoder.get(), int32_t(frameOffset.value() - audioWrittenOffset.value())));
     AudioSegment silence;
     silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value());
@@ -636,28 +636,28 @@ void MediaDecoderStateMachine::SendStrea
   if (mAudioThread)
     return;
 
   int64_t minLastAudioPacketTime = INT64_MAX;
   SourceMediaStream* mediaStream = stream->mStream;
   StreamTime endPosition = 0;
 
   if (!stream->mStreamInitialized) {
-    if (mInfo.mHasAudio) {
+    if (mInfo.HasAudio()) {
       AudioSegment* audio = new AudioSegment();
-      mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudioRate, 0, audio);
+      mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudio.mRate, 0, audio);
     }
-    if (mInfo.mHasVideo) {
+    if (mInfo.HasVideo()) {
       VideoSegment* video = new VideoSegment();
       mediaStream->AddTrack(TRACK_VIDEO, RATE_VIDEO, 0, video);
     }
     stream->mStreamInitialized = true;
   }
 
-  if (mInfo.mHasAudio) {
+  if (mInfo.HasAudio()) {
     nsAutoTArray<AudioData*,10> audio;
     // It's OK to hold references to the AudioData because while audio
     // is captured, only the decoder thread pops from the queue (see below).
     mReader->AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio);
     AudioSegment output;
     for (uint32_t i = 0; i < audio.Length(); ++i) {
       SendStreamAudio(audio[i], stream, &output);
     }
@@ -665,20 +665,20 @@ void MediaDecoderStateMachine::SendStrea
       mediaStream->AppendToTrack(TRACK_AUDIO, &output);
     }
     if (mReader->AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
       mediaStream->EndTrack(TRACK_AUDIO);
       stream->mHaveSentFinishAudio = true;
     }
     minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
     endPosition = std::max(endPosition,
-        TicksToTimeRoundDown(mInfo.mAudioRate, stream->mAudioFramesWritten));
+        TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten));
   }
 
-  if (mInfo.mHasVideo) {
+  if (mInfo.HasVideo()) {
     nsAutoTArray<VideoData*,10> video;
     // It's OK to hold references to the VideoData only the decoder thread
     // pops from the queue.
     mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime + mStartTime, &video);
     VideoSegment output;
     for (uint32_t i = 0; i < video.Length(); ++i) {
       VideoData* v = video[i];
       if (stream->mNextVideoTime + mStartTime < v->mTime) {
@@ -718,18 +718,18 @@ void MediaDecoderStateMachine::SendStrea
         TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
   }
 
   if (!stream->mHaveSentFinish) {
     stream->mStream->AdvanceKnownTracksTime(endPosition);
   }
 
   bool finished =
-      (!mInfo.mHasAudio || mReader->AudioQueue().IsFinished()) &&
-      (!mInfo.mHasVideo || mReader->VideoQueue().IsFinished());
+      (!mInfo.HasAudio() || mReader->AudioQueue().IsFinished()) &&
+      (!mInfo.HasVideo() || mReader->VideoQueue().IsFinished());
   if (finished && !stream->mHaveSentFinish) {
     stream->mHaveSentFinish = true;
     stream->mStream->Finish();
   }
 
   if (mAudioCaptured) {
     // Discard audio packets that are no longer needed.
     while (true) {
@@ -1040,18 +1040,18 @@ void MediaDecoderStateMachine::AudioLoop
   bool setPreservesPitch;
   AudioChannelType audioChannelType;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioCompleted = false;
     audioStartTime = mAudioStartTime;
     NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
-    channels = mInfo.mAudioChannels;
-    rate = mInfo.mAudioRate;
+    channels = mInfo.mAudio.mChannels;
+    rate = mInfo.mAudio.mRate;
 
     audioChannelType = mDecoder->GetAudioChannelType();
     volume = mVolume;
     preservesPitch = mPreservesPitch;
     playbackRate = mPlaybackRate;
   }
 
   {
@@ -1893,31 +1893,31 @@ nsresult MediaDecoderStateMachine::Decod
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   NS_ASSERTION(mState == DECODER_STATE_DECODING_METADATA,
                "Only call when in metadata decoding state");
 
   LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder.get()));
   nsresult res;
-  VideoInfo info;
+  MediaInfo info;
   MetadataTags* tags;
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     res = mReader->ReadMetadata(&info, &tags);
   }
   if (NS_SUCCEEDED(res) && (mState == DECODER_STATE_DECODING_METADATA) && (mReader->IsWaitingMediaResources())) {
     // change state to DECODER_STATE_WAIT_FOR_RESOURCES
     StartWaitForResources();
     return NS_OK;
   }
 
   mInfo = info;
 
-  if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
+  if (NS_FAILED(res) || (!info.HasValidMedia())) {
     // Dispatch the event to call DecodeError synchronously. This ensures
     // we're in shutdown state by the time we exit the decode thread.
     // If we just moved to shutdown state here on the decode thread, we may
     // cause the state machine to shutdown/free memory without closing its
     // media stream properly, and we'll get callbacks from the media stream
     // causing a crash. Note the state machine shutdown joins this decode
     // thread during shutdown (and other state machines can run on the state
     // machine thread while the join is waiting), so it's safe to do this
@@ -1952,28 +1952,28 @@ nsresult MediaDecoderStateMachine::Decod
                      mDecoder.get(), mStartTime, mEndTime, GetDuration(),
                      mTransportSeekable, mMediaSeekable));
 
   // Inform the element that we've loaded the metadata and the first frame,
   // setting the default framebuffer size for audioavailable events.  Also,
   // if there is audio, let the MozAudioAvailable event manager know about
   // the metadata.
   if (HasAudio()) {
-    mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
+    mEventManager.Init(mInfo.mAudio.mChannels, mInfo.mAudio.mRate);
     // Set the buffer length at the decoder level to be able, to be able
     // to retrive the value via media element method. The RequestFrameBufferLength
     // will call the MediaDecoderStateMachine::SetFrameBufferLength().
-    uint32_t frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
+    uint32_t frameBufferLength = mInfo.mAudio.mChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
     mDecoder->RequestFrameBufferLength(frameBufferLength);
   }
 
   nsCOMPtr<nsIRunnable> metadataLoadedEvent =
     new AudioMetadataEventRunner(mDecoder,
-                                 mInfo.mAudioChannels,
-                                 mInfo.mAudioRate,
+                                 mInfo.mAudio.mChannels,
+                                 mInfo.mAudio.mRate,
                                  HasAudio(),
                                  HasVideo(),
                                  tags);
   NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
 
   if (mState == DECODER_STATE_DECODING_METADATA) {
     LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
     StartDecoding();
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -223,24 +223,24 @@ public:
 
   // State machine thread run function. Defers to RunStateMachine().
   NS_IMETHOD Run() MOZ_OVERRIDE;
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   bool HasAudio() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
-    return mInfo.mHasAudio;
+    return mInfo.HasAudio();
   }
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   bool HasVideo() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
-    return mInfo.mHasVideo;
+    return mInfo.HasVideo();
   }
 
   // Should be called by main thread.
   bool HaveNextFrameData() const;
 
   // Must be called with the decode monitor held.
   bool IsBuffering() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
@@ -805,17 +805,17 @@ private:
 
   // Manager for queuing and dispatching MozAudioAvailable events.  The
   // event manager is accessed from the state machine and audio threads,
   // and takes care of synchronizing access to its internal queue.
   AudioAvailableEventManager mEventManager;
 
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
-  VideoInfo mInfo;
+  MediaInfo mInfo;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
 };
 
 } // namespace mozilla;
 #endif
--- a/content/media/apple/AppleMP3Reader.cpp
+++ b/content/media/apple/AppleMP3Reader.cpp
@@ -355,17 +355,17 @@ GetProperty(AudioFileStreamID aAudioFile
   rv = AudioFileStreamGetProperty(aAudioFileStream, aPropertyID,
                                   &size, aData);
 
   return NS_OK;
 }
 
 
 nsresult
-AppleMP3Reader::ReadMetadata(VideoInfo* aInfo,
+AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
                              MetadataTags** aTags)
 {
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
 
   *aTags = nullptr;
 
   /*
    * Feed bytes into the parser until we have all the metadata we need to
@@ -393,19 +393,19 @@ AppleMP3Reader::ReadMetadata(VideoInfo* 
     return NS_ERROR_FAILURE;
   }
 
   if (!mAudioConverter) {
     LOGE("Failed to setup the AudioToolbox audio decoder\n");
     return NS_ERROR_FAILURE;
   }
 
-  aInfo->mAudioRate = mAudioSampleRate;
-  aInfo->mAudioChannels = mAudioChannels;
-  aInfo->mHasAudio = mStreamReady;
+  aInfo->mAudio.mRate = mAudioSampleRate;
+  aInfo->mAudio.mChannels = mAudioChannels;
+  aInfo->mAudio.mHasAudio = mStreamReady;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(mDuration);
   }
 
   return NS_OK;
 }
--- a/content/media/apple/AppleMP3Reader.h
+++ b/content/media/apple/AppleMP3Reader.h
@@ -25,17 +25,17 @@ public:
 
   virtual bool DecodeAudioData() MOZ_OVERRIDE;
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
                                 int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   virtual bool HasAudio() MOZ_OVERRIDE;
   virtual bool HasVideo() MOZ_OVERRIDE;
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags) MOZ_OVERRIDE;
 
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime) MOZ_OVERRIDE;
 
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
--- a/content/media/dash/DASHReader.cpp
+++ b/content/media/dash/DASHReader.cpp
@@ -167,17 +167,17 @@ DASHReader::DecodeVideoFrame(bool &aKeyf
 bool
 DASHReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   return (mAudioReader ? mAudioReader->DecodeAudioData() : false);
 }
 
 nsresult
-DASHReader::ReadMetadata(VideoInfo* aInfo,
+DASHReader::ReadMetadata(MediaInfo* aInfo,
                          MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   // Wait for MPD to be parsed and child readers created.
   LOG1("Waiting for metadata download.");
   nsresult rv = WaitForMetadata();
   // If we get an abort, return silently; the decoder is shutting down.
@@ -186,40 +186,36 @@ DASHReader::ReadMetadata(VideoInfo* aInf
   }
   // Verify no other errors before continuing.
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(aTags, "Called with null MetadataTags**.");
   *aTags = nullptr;
 
   // Get metadata from child readers.
-  VideoInfo audioInfo, videoInfo;
+  MediaInfo audioInfo, videoInfo;
 
   // Read metadata for all video streams.
   for (uint i = 0; i < mVideoReaders.Length(); i++) {
     // Use an nsAutoPtr here to ensure |tags| memory does not leak.
     nsAutoPtr<HTMLMediaElement::MetadataTags> tags;
     rv = mVideoReaders[i]->ReadMetadata(&videoInfo, getter_Transfers(tags));
     NS_ENSURE_SUCCESS(rv, rv);
     // Use metadata from current video sub reader to populate aInfo.
     if (mVideoReaders[i] == mVideoReader) {
-      mInfo.mHasVideo      = videoInfo.mHasVideo;
-      mInfo.mDisplay       = videoInfo.mDisplay;
+      mInfo.mVideo = videoInfo.mVideo;
     }
   }
   // Read metadata for audio stream.
   // Note: Getting metadata tags from audio reader only for now.
   // XXX Audio stream switching not yet supported.
   if (mAudioReader) {
     rv = mAudioReader->ReadMetadata(&audioInfo, aTags);
     NS_ENSURE_SUCCESS(rv, rv);
-    mInfo.mHasAudio      = audioInfo.mHasAudio;
-    mInfo.mAudioRate     = audioInfo.mAudioRate;
-    mInfo.mAudioChannels = audioInfo.mAudioChannels;
-    mInfo.mStereoMode    = audioInfo.mStereoMode;
+    mInfo.mAudio = audioInfo.mAudio;
   }
 
   *aInfo = mInfo;
 
   return NS_OK;
 }
 
 nsresult
--- a/content/media/dash/DASHReader.h
+++ b/content/media/dash/DASHReader.h
@@ -33,17 +33,17 @@ public:
 
   // Adds a pointer to a audio/video reader for a media |Representation|.
   // Called on the main thread only.
   void AddAudioReader(DASHRepReader* aAudioReader);
   void AddVideoReader(DASHRepReader* aVideoReader);
 
   // Waits for metadata bytes to be downloaded, then reads and parses them.
   // Called on the decode thread only.
-  nsresult ReadMetadata(VideoInfo* aInfo,
+  nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) MOZ_OVERRIDE;
 
   // Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
   // notification, whichever comes first. Ensures no attempt to read metadata
   // during |DASHDecoder|::|Shutdown|. Called on decode thread only.
   nsresult WaitForMetadata() {
     NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
     ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
--- a/content/media/directshow/DirectShowReader.cpp
+++ b/content/media/directshow/DirectShowReader.cpp
@@ -68,17 +68,17 @@ DirectShowReader::Init(MediaDecoderReade
 }
 
 // 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(VideoInfo* aInfo,
+DirectShowReader::ReadMetadata(MediaInfo* aInfo,
                                MetadataTags** aTags)
 {
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   HRESULT hr;
   nsresult rv;
 
   // Create the filter graph, reference it by the GraphBuilder interface,
   // to make graph building more convenient.
@@ -159,21 +159,20 @@ DirectShowReader::ReadMetadata(VideoInfo
 
   hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter);
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   WAVEFORMATEX format;
   mAudioSinkFilter->GetSampleSink()->GetAudioFormat(&format);
   NS_ENSURE_TRUE(format.wFormatTag == WAVE_FORMAT_PCM, NS_ERROR_FAILURE);
 
-  mInfo.mAudioChannels = mNumChannels = format.nChannels;
-  mInfo.mAudioRate = mAudioRate = format.nSamplesPerSec;
+  mInfo.mAudio.mChannels = mNumChannels = format.nChannels;
+  mInfo.mAudio.mRate = mAudioRate = format.nSamplesPerSec;
   mBytesPerSample = format.wBitsPerSample / 8;
-  mInfo.mHasAudio = true;
-  mInfo.mHasVideo = false;
+  mInfo.mAudio.mHasAudio = true;
 
   *aInfo = mInfo;
   // Note: The SourceFilter strips ID3v2 tags out of the stream.
   *aTags = nullptr;
 
   // Begin decoding!
   hr = mControl->Run();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
@@ -189,18 +188,18 @@ DirectShowReader::ReadMetadata(VideoInfo
   hr = mMediaSeeking->GetDuration(&duration);
   if (SUCCEEDED(hr)) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(RefTimeToUsecs(duration));
   }
 
   LOG("Successfully initialized DirectShow MP3 decoder.");
   LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d",
-      mInfo.mAudioChannels,
-      mInfo.mAudioRate,
+      mInfo.mAudio.mChannels,
+      mInfo.mAudio.mRate,
       RefTimeToUsecs(duration),
       mBytesPerSample);
 
   return NS_OK;
 }
 
 inline float
 UnsignedByteToAudioSample(uint8_t aValue)
--- a/content/media/directshow/DirectShowReader.h
+++ b/content/media/directshow/DirectShowReader.h
@@ -52,17 +52,17 @@ public:
 
   bool DecodeAudioData() MOZ_OVERRIDE;
   bool DecodeVideoFrame(bool &aKeyframeSkip,
                         int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   bool HasAudio() MOZ_OVERRIDE;
   bool HasVideo() MOZ_OVERRIDE;
 
-  nsresult ReadMetadata(VideoInfo* aInfo,
+  nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) MOZ_OVERRIDE;
 
   nsresult Seek(int64_t aTime,
                 int64_t aStartTime,
                 int64_t aEndTime,
                 int64_t aCurrentTime) MOZ_OVERRIDE;
 
   nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered,
--- a/content/media/gstreamer/GStreamerReader.cpp
+++ b/content/media/gstreamer/GStreamerReader.cpp
@@ -238,17 +238,17 @@ void GStreamerReader::PlayBinSourceSetup
   GstCaps *caps =
     GStreamerFormatHelper::ConvertFormatsToCaps(mDecoder->GetResource()->GetContentType().get(),
                                                 nullptr);
 
   gst_app_src_set_caps(aSource, caps);
   gst_caps_unref(caps);
 }
 
-nsresult GStreamerReader::ReadMetadata(VideoInfo* aInfo,
+nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
                                        MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   nsresult ret = NS_OK;
 
   /* We do 3 attempts here: decoding audio and video, decoding video only,
    * decoding audio only. This allows us to play streams that have one broken
    * stream but that are otherwise decodeable.
@@ -349,18 +349,18 @@ nsresult GStreamerReader::ReadMetadata(V
     duration = GST_TIME_AS_USECONDS (duration);
     mDecoder->SetMediaDuration(duration);
   } else {
     mDecoder->SetMediaSeekable(false);
   }
 
   int n_video = 0, n_audio = 0;
   g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr);
-  mInfo.mHasVideo = n_video != 0;
-  mInfo.mHasAudio = n_audio != 0;
+  mInfo.mVideo.mHasVideo = n_video != 0;
+  mInfo.mAudio.mHasAudio = n_audio != 0;
 
   *aInfo = mInfo;
 
   *aTags = nullptr;
 
   // Watch the pipeline for fatal errors
   gst_bus_set_sync_handler(mBus, GStreamerReader::ErrorCb, this);
 
@@ -485,22 +485,22 @@ bool GStreamerReader::DecodeAudioData()
       GST_FORMAT_TIME, timestamp);
   timestamp = GST_TIME_AS_USECONDS(timestamp);
   int64_t duration = 0;
   if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer)))
     duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer));
 
   int64_t offset = GST_BUFFER_OFFSET(buffer);
   unsigned int size = GST_BUFFER_SIZE(buffer);
-  int32_t frames = (size / sizeof(AudioDataValue)) / mInfo.mAudioChannels;
+  int32_t frames = (size / sizeof(AudioDataValue)) / mInfo.mAudio.mChannels;
   ssize_t outSize = static_cast<size_t>(size / sizeof(AudioDataValue));
   nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outSize]);
   memcpy(data, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
   AudioData* audio = new AudioData(offset, timestamp, duration,
-      frames, data.forget(), mInfo.mAudioChannels);
+      frames, data.forget(), mInfo.mAudio.mChannels);
 
   mAudioQueue.Push(audio);
   gst_buffer_unref(buffer);
 
   return true;
 }
 
 bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip,
@@ -618,17 +618,17 @@ bool GStreamerReader::DecodeVideoFrame(b
         i, width);
     b.mPlanes[i].mOffset = 0;
     b.mPlanes[i].mSkip = 0;
   }
 
   isKeyframe = !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT);
   /* XXX ? */
   int64_t offset = 0;
-  VideoData* video = VideoData::Create(mInfo, image, offset,
+  VideoData* video = VideoData::Create(mInfo.mVideo, image, offset,
                                        timestamp, nextTimestamp, b,
                                        isKeyframe, -1, mPicture);
   mVideoQueue.Push(video);
   gst_buffer_unref(buffer);
 
   return true;
 }
 
@@ -651,17 +651,17 @@ nsresult GStreamerReader::Seek(int64_t a
   LOG(PR_LOG_DEBUG, ("seek succeeded"));
 
   return DecodeToTarget(aTarget);
 }
 
 nsresult GStreamerReader::GetBuffered(TimeRanges* aBuffered,
                                       int64_t aStartTime)
 {
-  if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
+  if (!mInfo.HasValidMedia()) {
     return NS_OK;
   }
 
   GstFormat format = GST_FORMAT_TIME;
   MediaResource* resource = mDecoder->GetResource();
   nsTArray<MediaByteRange> ranges;
   resource->GetCachedRanges(ranges);
 
@@ -928,40 +928,40 @@ GstFlowReturn GStreamerReader::NewPrerol
 
 void GStreamerReader::AudioPreroll()
 {
   /* The first audio buffer has reached the audio sink. Get rate and channels */
   LOG(PR_LOG_DEBUG, ("Audio preroll"));
   GstPad* sinkpad = gst_element_get_pad(GST_ELEMENT(mAudioAppSink), "sink");
   GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad);
   GstStructure* s = gst_caps_get_structure(caps, 0);
-  mInfo.mAudioRate = mInfo.mAudioChannels = 0;
-  gst_structure_get_int(s, "rate", (gint*) &mInfo.mAudioRate);
-  gst_structure_get_int(s, "channels", (gint*) &mInfo.mAudioChannels);
-  NS_ASSERTION(mInfo.mAudioRate != 0, ("audio rate is zero"));
-  NS_ASSERTION(mInfo.mAudioChannels != 0, ("audio channels is zero"));
-  NS_ASSERTION(mInfo.mAudioChannels > 0 && mInfo.mAudioChannels <= MAX_CHANNELS,
+  mInfo.mAudio.mRate = mInfo.mAudio.mChannels = 0;
+  gst_structure_get_int(s, "rate", (gint*) &mInfo.mAudio.mRate);
+  gst_structure_get_int(s, "channels", (gint*) &mInfo.mAudio.mChannels);
+  NS_ASSERTION(mInfo.mAudio.mRate != 0, ("audio rate is zero"));
+  NS_ASSERTION(mInfo.mAudio.mChannels != 0, ("audio channels is zero"));
+  NS_ASSERTION(mInfo.mAudio.mChannels > 0 && mInfo.mAudio.mChannels <= MAX_CHANNELS,
       "invalid audio channels number");
-  mInfo.mHasAudio = true;
+  mInfo.mAudio.mHasAudio = true;
   gst_caps_unref(caps);
   gst_object_unref(sinkpad);
 }
 
 void GStreamerReader::VideoPreroll()
 {
   /* The first video buffer has reached the video sink. Get width and height */
   LOG(PR_LOG_DEBUG, ("Video preroll"));
   GstPad* sinkpad = gst_element_get_pad(GST_ELEMENT(mVideoAppSink), "sink");
   GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad);
   gst_video_format_parse_caps(caps, &mFormat, &mPicture.width, &mPicture.height);
   GstStructure* structure = gst_caps_get_structure(caps, 0);
   gst_structure_get_fraction(structure, "framerate", &fpsNum, &fpsDen);
   NS_ASSERTION(mPicture.width && mPicture.height, "invalid video resolution");
-  mInfo.mDisplay = nsIntSize(mPicture.width, mPicture.height);
-  mInfo.mHasVideo = true;
+  mInfo.mVideo.mDisplay = nsIntSize(mPicture.width, mPicture.height);
+  mInfo.mVideo.mHasVideo = true;
   gst_caps_unref(caps);
   gst_object_unref(sinkpad);
 }
 
 GstFlowReturn GStreamerReader::NewBufferCb(GstAppSink* aSink,
                                            gpointer aUserData)
 {
   GStreamerReader* reader = reinterpret_cast<GStreamerReader*>(aUserData);
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -39,30 +39,30 @@ public:
   GStreamerReader(AbstractMediaDecoder* aDecoder);
   virtual ~GStreamerReader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
   virtual nsresult ResetDecode();
   virtual bool DecodeAudioData();
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
                                 int64_t aTimeThreshold);
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime);
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
 
   virtual bool HasAudio() {
-    return mInfo.mHasAudio;
+    return mInfo.HasAudio();
   }
 
   virtual bool HasVideo() {
-    return mInfo.mHasVideo;
+    return mInfo.HasVideo();
   }
 
 private:
 
   void ReadAndPushData(guint aLength);
   int64_t QueryDuration();
 
   /* Called once the pipeline is setup to check that the stream only contains
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -58,25 +58,25 @@ public:
     if (GetVideoReader()) {
       return GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
     }
     return false;
   }
 
   bool HasVideo() MOZ_OVERRIDE
   {
-    return mInfo.mHasVideo;
+    return mInfo.HasVideo();
   }
 
   bool HasAudio() MOZ_OVERRIDE
   {
-    return mInfo.mHasAudio;
+    return mInfo.HasAudio();
   }
 
-  nsresult ReadMetadata(VideoInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
+  nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
 
   nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                 int64_t aCurrentTime) MOZ_OVERRIDE
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE
@@ -172,41 +172,37 @@ MediaSourceDecoder::CreateSubDecoder(con
   LOG(PR_LOG_DEBUG, ("Registered subdecoder %p subreader %p", decoder.get(), reader.get()));
   mon.NotifyAll();
 
   decoder->SetReader(reader.forget());
   return decoder;
 }
 
 nsresult
-MediaSourceReader::ReadMetadata(VideoInfo* aInfo, MetadataTags** aTags)
+MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
 {
   mDecoder->SetMediaSeekable(true);
   mDecoder->SetTransportSeekable(false);
 
   MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
   const nsTArray<MediaDecoderReader*>& readers = decoder->GetReaders();
   for (uint32_t i = 0; i < readers.Length(); ++i) {
     MediaDecoderReader* reader = readers[i];
-    VideoInfo vi;
-    nsresult rv = reader->ReadMetadata(&vi, aTags);
+    MediaInfo mi;
+    nsresult rv = reader->ReadMetadata(&mi, aTags);
     LOG(PR_LOG_DEBUG, ("ReadMetadata on SB reader %p", reader));
     if (NS_FAILED(rv)) {
       return rv;
     }
-    if (vi.mHasVideo && !mInfo.mHasVideo) {
-      mInfo.mDisplay = vi.mDisplay;
-      mInfo.mStereoMode = vi.mStereoMode;
-      mInfo.mHasVideo = true;
+    if (mi.HasVideo() && !mInfo.HasVideo()) {
+      mInfo.mVideo = mi.mVideo;
       decoder->SetVideoReader(reader);
     }
-    if (vi.mHasAudio && !mInfo.mHasAudio) {
-      mInfo.mAudioRate = vi.mAudioRate;
-      mInfo.mAudioChannels = vi.mAudioChannels;
-      mInfo.mHasAudio = true;
+    if (mi.HasAudio() && !mInfo.HasAudio()) {
+      mInfo.mAudio = mi.mAudio;
       decoder->SetAudioReader(reader);
     }
   }
   *aInfo = mInfo;
 
   return NS_OK;
 }
 
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -163,18 +163,18 @@ void OggReader::BuildSerialList(nsTArray
     if (mVorbisState) {
       aTracks.AppendElement(mVorbisState->mSerial);
     } else if(mOpusState) {
       aTracks.AppendElement(mOpusState->mSerial);
     }
   }
 }
 
-nsresult OggReader::ReadMetadata(VideoInfo* aInfo,
-                                   MetadataTags** aTags)
+nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
+                                 MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   // We read packets until all bitstreams have read all their header packets.
   // We record the offset of the first non-header page so that we know
   // what page to seek to when seeking to the media start.
 
   NS_ASSERTION(aTags, "Called with null MetadataTags**.");
@@ -272,50 +272,50 @@ nsresult OggReader::ReadMetadata(VideoIn
     // Apply the aspect ratio to produce the intrinsic display size we report
     // to the element.
     ScaleDisplayByAspectRatio(displaySize, mTheoraState->mPixelAspectRatio);
 
     nsIntSize frameSize(mTheoraState->mInfo.frame_width,
                         mTheoraState->mInfo.frame_height);
     if (VideoInfo::ValidateVideoRegion(frameSize, picture, displaySize)) {
       // Video track's frame sizes will not overflow. Activate the video track.
-      mInfo.mHasVideo = true;
-      mInfo.mDisplay = displaySize;
+      mInfo.mVideo.mHasVideo = true;
+      mInfo.mVideo.mDisplay = displaySize;
       mPicture = picture;
 
       VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
       if (container) {
         container->SetCurrentFrame(gfxIntSize(displaySize.width, displaySize.height),
                                    nullptr,
                                    TimeStamp::Now());
       }
 
       // Copy Theora info data for time computations on other threads.
       memcpy(&mTheoraInfo, &mTheoraState->mInfo, sizeof(mTheoraInfo));
       mTheoraSerial = mTheoraState->mSerial;
     }
   }
 
   if (mVorbisState && ReadHeaders(mVorbisState)) {
-    mInfo.mHasAudio = true;
-    mInfo.mAudioRate = mVorbisState->mInfo.rate;
-    mInfo.mAudioChannels = mVorbisState->mInfo.channels > 2 ? 2 : mVorbisState->mInfo.channels;
+    mInfo.mAudio.mHasAudio = true;
+    mInfo.mAudio.mRate = mVorbisState->mInfo.rate;
+    mInfo.mAudio.mChannels = mVorbisState->mInfo.channels > 2 ? 2 : mVorbisState->mInfo.channels;
     // Copy Vorbis info data for time computations on other threads.
     memcpy(&mVorbisInfo, &mVorbisState->mInfo, sizeof(mVorbisInfo));
     mVorbisInfo.codec_setup = NULL;
     mVorbisSerial = mVorbisState->mSerial;
     *aTags = mVorbisState->GetTags();
   } else {
     memset(&mVorbisInfo, 0, sizeof(mVorbisInfo));
   }
 #ifdef MOZ_OPUS
   if (mOpusState && ReadHeaders(mOpusState)) {
-    mInfo.mHasAudio = true;
-    mInfo.mAudioRate = mOpusState->mRate;
-    mInfo.mAudioChannels = mOpusState->mChannels > 2 ? 2 : mOpusState->mChannels;
+    mInfo.mAudio.mHasAudio = true;
+    mInfo.mAudio.mRate = mOpusState->mRate;
+    mInfo.mAudio.mChannels = mOpusState->mChannels > 2 ? 2 : mOpusState->mChannels;
     mOpusSerial = mOpusState->mSerial;
     mOpusPreSkip = mOpusState->mPreSkip;
 
     *aTags = mOpusState->GetTags();
   }
 #endif
   if (mSkeletonState) {
     if (!HasAudio() && !HasVideo()) {
@@ -794,17 +794,17 @@ nsresult OggReader::DecodeTheora(ogg_pac
     for (uint32_t i=0; i < 3; ++i) {
       b.mPlanes[i].mData = buffer[i].data;
       b.mPlanes[i].mHeight = buffer[i].height;
       b.mPlanes[i].mWidth = buffer[i].width;
       b.mPlanes[i].mStride = buffer[i].stride;
       b.mPlanes[i].mOffset = b.mPlanes[i].mSkip = 0;
     }
 
-    VideoData *v = VideoData::Create(mInfo,
+    VideoData *v = VideoData::Create(mInfo.mVideo,
                                      mDecoder->GetImageContainer(),
                                      mDecoder->GetResource()->Tell(),
                                      time,
                                      endTime,
                                      b,
                                      isKeyframe,
                                      aPacket->granulepos,
                                      mPicture);
@@ -1763,17 +1763,17 @@ nsresult OggReader::GetBuffered(TimeRang
   }
   GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
 
   return NS_OK;
 #else
   // HasAudio and HasVideo are not used here as they take a lock and cause
   // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
   // after metadata is read.
-  if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
+  if (!mInfo.HasValidMedia()) {
     // No need to search through the file if there are no audio or video tracks
     return NS_OK;
   }
 
   MediaResource* resource = mDecoder->GetResource();
   nsTArray<MediaByteRange> ranges;
   nsresult res = resource->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(res, res);
--- a/content/media/ogg/OggReader.h
+++ b/content/media/ogg/OggReader.h
@@ -65,17 +65,17 @@ public:
     return (mVorbisState != 0 && mVorbisState->mActive) ||
            (mOpusState != 0 && mOpusState->mActive);
   }
 
   virtual bool HasVideo() {
     return mTheoraState != 0 && mTheoraState->mActive;
   }
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
 
 private:
   // This monitor should be taken when reading or writing to mIsChained.
   ReentrantMonitor mMonitor;
 
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -64,17 +64,17 @@ bool MediaOmxReader::IsDormantNeeded()
 void MediaOmxReader::ReleaseMediaResources()
 {
   ResetDecode();
   if (mOmxDecoder.get()) {
     mOmxDecoder->ReleaseMediaResources();
   }
 }
 
-nsresult MediaOmxReader::ReadMetadata(VideoInfo* aInfo,
+nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
                                       MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   *aTags = nullptr;
 
   if (!mOmxDecoder.get()) {
     mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
@@ -108,34 +108,34 @@ nsresult MediaOmxReader::ReadMetadata(Vi
     // that our video frame creation code doesn't overflow.
     nsIntSize displaySize(width, height);
     nsIntSize frameSize(width, height);
     if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
       return NS_ERROR_FAILURE;
     }
 
     // Video track's frame sizes will not overflow. Activate the video track.
-    mHasVideo = mInfo.mHasVideo = true;
-    mInfo.mDisplay = displaySize;
+    mHasVideo = mInfo.mVideo.mHasVideo = true;
+    mInfo.mVideo.mDisplay = displaySize;
     mPicture = pictureRect;
     mInitialFrame = frameSize;
     VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
     if (container) {
       container->SetCurrentFrame(gfxIntSize(displaySize.width, displaySize.height),
                                  nullptr,
                                  mozilla::TimeStamp::Now());
     }
   }
 
   if (mOmxDecoder->HasAudio()) {
     int32_t numChannels, sampleRate;
     mOmxDecoder->GetAudioParameters(&numChannels, &sampleRate);
-    mHasAudio = mInfo.mHasAudio = true;
-    mInfo.mAudioChannels = numChannels;
-    mInfo.mAudioRate = sampleRate;
+    mHasAudio = mInfo.mAudio.mHasAudio = true;
+    mInfo.mAudio.mChannels = numChannels;
+    mInfo.mAudio.mRate = sampleRate;
   }
 
  *aInfo = mInfo;
 
   return NS_OK;
 }
 
 // Resets all state related to decoding, emptying all buffers etc.
@@ -227,27 +227,27 @@ bool MediaOmxReader::DecodeVideoFrame(bo
 
       b.mPlanes[2].mData = static_cast<uint8_t *>(frame.Cr.mData);
       b.mPlanes[2].mStride = frame.Cr.mStride;
       b.mPlanes[2].mHeight = frame.Cr.mHeight;
       b.mPlanes[2].mWidth = frame.Cr.mWidth;
       b.mPlanes[2].mOffset = frame.Cr.mOffset;
       b.mPlanes[2].mSkip = frame.Cr.mSkip;
 
-      v = VideoData::Create(mInfo,
+      v = VideoData::Create(mInfo.mVideo,
                             mDecoder->GetImageContainer(),
                             pos,
                             frame.mTimeUs,
                             frame.mTimeUs+1, // We don't know the end time.
                             b,
                             frame.mKeyFrame,
                             -1,
                             picture);
     } else {
-      v = VideoData::Create(mInfo,
+      v = VideoData::Create(mInfo.mVideo,
                             mDecoder->GetImageContainer(),
                             pos,
                             frame.mTimeUs,
                             frame.mTimeUs+1, // We don't know the end time.
                             frame.mGraphicBuffer,
                             frame.mKeyFrame,
                             -1,
                             picture);
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -57,17 +57,17 @@ public:
     return mHasVideo;
   }
 
   virtual bool IsWaitingMediaResources();
 
   virtual bool IsDormantNeeded();
   virtual void ReleaseMediaResources();
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime);
 
   virtual void OnDecodeThreadStart() MOZ_OVERRIDE;
 
   virtual void OnDecodeThreadFinish() MOZ_OVERRIDE;
 };
--- a/content/media/plugins/MediaPluginReader.cpp
+++ b/content/media/plugins/MediaPluginReader.cpp
@@ -36,18 +36,18 @@ MediaPluginReader::~MediaPluginReader()
   ResetDecode();
 }
 
 nsresult MediaPluginReader::Init(MediaDecoderReader* aCloneDonor)
 {
   return NS_OK;
 }
 
-nsresult MediaPluginReader::ReadMetadata(VideoInfo* aInfo,
-                                           MetadataTags** aTags)
+nsresult MediaPluginReader::ReadMetadata(MediaInfo* aInfo,
+                                         MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   if (!mPlugin) {
     mPlugin = GetMediaPluginHost()->CreateDecoder(mDecoder->GetResource(), mType);
     if (!mPlugin) {
       return NS_ERROR_FAILURE;
     }
@@ -70,34 +70,34 @@ nsresult MediaPluginReader::ReadMetadata
     // that our video frame creation code doesn't overflow.
     nsIntSize displaySize(width, height);
     nsIntSize frameSize(width, height);
     if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
       return NS_ERROR_FAILURE;
     }
 
     // Video track's frame sizes will not overflow. Activate the video track.
-    mHasVideo = mInfo.mHasVideo = true;
-    mInfo.mDisplay = displaySize;
+    mHasVideo = mInfo.mVideo.mHasVideo = true;
+    mInfo.mVideo.mDisplay = displaySize;
     mPicture = pictureRect;
     mInitialFrame = frameSize;
     VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
     if (container) {
       container->SetCurrentFrame(gfxIntSize(displaySize.width, displaySize.height),
                                  nullptr,
                                  mozilla::TimeStamp::Now());
     }
   }
 
   if (mPlugin->HasAudio(mPlugin)) {
     int32_t numChannels, sampleRate;
     mPlugin->GetAudioParameters(mPlugin, &numChannels, &sampleRate);
-    mHasAudio = mInfo.mHasAudio = true;
-    mInfo.mAudioChannels = numChannels;
-    mInfo.mAudioRate = sampleRate;
+    mHasAudio = mInfo.mAudio.mHasAudio = true;
+    mInfo.mAudio.mChannels = numChannels;
+    mInfo.mAudio.mRate = sampleRate;
   }
 
  *aInfo = mInfo;
  *aTags = nullptr;
   return NS_OK;
 }
 
 // Resets all state related to decoding, emptying all buffers etc.
@@ -181,17 +181,17 @@ bool MediaPluginReader::DecodeVideoFrame
         // and we will preserve the ratio of the crop rectangle as it
         // was reported relative to the picture size reported by the container.
         picture.x = (mPicture.x * frameSize.width) / mInitialFrame.width;
         picture.y = (mPicture.y * frameSize.height) / mInitialFrame.height;
         picture.width = (frameSize.width * mPicture.width) / mInitialFrame.width;
         picture.height = (frameSize.height * mPicture.height) / mInitialFrame.height;
       }
 
-      v = VideoData::CreateFromImage(mInfo,
+      v = VideoData::CreateFromImage(mInfo.mVideo,
                                      mDecoder->GetImageContainer(),
                                      pos,
                                      frame.mTimeUs,
                                      frame.mTimeUs+1, // We don't know the end time.
                                      currentImage,
                                      frame.mKeyFrame,
                                      -1,
                                      picture);
@@ -227,17 +227,17 @@ bool MediaPluginReader::DecodeVideoFrame
         // was reported relative to the picture size reported by the container.
         picture.x = (mPicture.x * frame.Y.mWidth) / mInitialFrame.width;
         picture.y = (mPicture.y * frame.Y.mHeight) / mInitialFrame.height;
         picture.width = (frame.Y.mWidth * mPicture.width) / mInitialFrame.width;
         picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height;
       }
 
       // This is the approximate byte position in the stream.
-      v = VideoData::Create(mInfo,
+      v = VideoData::Create(mInfo.mVideo,
                             mDecoder->GetImageContainer(),
                             pos,
                             frame.mTimeUs,
                             frame.mTimeUs+1, // We don't know the end time.
                             b,
                             frame.mKeyFrame,
                             -1,
                             picture);
--- a/content/media/plugins/MediaPluginReader.h
+++ b/content/media/plugins/MediaPluginReader.h
@@ -56,17 +56,17 @@ public:
     return mHasAudio;
   }
 
   virtual bool HasVideo()
   {
     return mHasVideo;
   }
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime);
   class ImageBufferCallback : public MPAPI::BufferCallback {
     typedef mozilla::layers::Image Image;
   public:
     ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
     void *operator()(size_t aWidth, size_t aHeight,
--- a/content/media/raw/RawReader.cpp
+++ b/content/media/raw/RawReader.cpp
@@ -30,18 +30,18 @@ nsresult RawReader::Init(MediaDecoderRea
 }
 
 nsresult RawReader::ResetDecode()
 {
   mCurrentFrame = 0;
   return MediaDecoderReader::ResetDecode();
 }
 
-nsresult RawReader::ReadMetadata(VideoInfo* aInfo,
-                                   MetadataTags** aTags)
+nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
+                                 MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(),
                "Should be on decode thread.");
 
   MediaResource* resource = mDecoder->GetResource();
   NS_ASSERTION(resource, "Decoder has no media resource");
 
   if (!ReadFromResource(resource, reinterpret_cast<uint8_t*>(&mMetadata),
@@ -70,19 +70,18 @@ nsresult RawReader::ReadMetadata(VideoIn
   ScaleDisplayByAspectRatio(display, pixelAspectRatio);
   mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight);
   nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight);
   if (!VideoInfo::ValidateVideoRegion(frameSize, mPicture, display)) {
     // Video track's frame sizes will overflow. Fail.
     return NS_ERROR_FAILURE;
   }
 
-  mInfo.mHasVideo = true;
-  mInfo.mHasAudio = false;
-  mInfo.mDisplay = display;
+  mInfo.mVideo.mHasVideo = true;
+  mInfo.mVideo.mDisplay = display;
 
   mFrameRate = static_cast<float>(mMetadata.framerateNumerator) /
                mMetadata.framerateDenominator;
 
   // Make some sanity checks
   if (mFrameRate > 45 ||
       mFrameRate == 0 ||
       pixelAspectRatio == 0 ||
@@ -203,17 +202,17 @@ bool RawReader::DecodeVideoFrame(bool &a
   b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
 
   b.mPlanes[2].mData = b.mPlanes[1].mData + mMetadata.frameHeight * cbcrStride / 2;
   b.mPlanes[2].mStride = cbcrStride;
   b.mPlanes[2].mHeight = mMetadata.frameHeight / 2;
   b.mPlanes[2].mWidth = mMetadata.frameWidth / 2;
   b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
 
-  VideoData *v = VideoData::Create(mInfo,
+  VideoData *v = VideoData::Create(mInfo.mVideo,
                                    mDecoder->GetImageContainer(),
                                    -1,
                                    currentFrameTime,
                                    currentFrameTime + (USECS_PER_S / mFrameRate),
                                    b,
                                    1, // In raw video every frame is a keyframe
                                    -1,
                                    mPicture);
--- a/content/media/raw/RawReader.h
+++ b/content/media/raw/RawReader.h
@@ -29,17 +29,17 @@ public:
     return false;
   }
 
   virtual bool HasVideo()
   {
     return true;
   }
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
 
 private:
   bool ReadFromResource(MediaResource *aResource, uint8_t *aBuf, uint32_t aLength);
 
   RawVideoHeader mMetadata;
--- a/content/media/wave/WaveReader.cpp
+++ b/content/media/wave/WaveReader.cpp
@@ -120,37 +120,36 @@ WaveReader::~WaveReader()
   MOZ_COUNT_DTOR(WaveReader);
 }
 
 nsresult WaveReader::Init(MediaDecoderReader* aCloneDonor)
 {
   return NS_OK;
 }
 
-nsresult WaveReader::ReadMetadata(VideoInfo* aInfo,
-                                    MetadataTags** aTags)
+nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
+                                  MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   bool loaded = LoadRIFFChunk();
   if (!loaded) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoPtr<dom::HTMLMediaElement::MetadataTags> tags;
 
   bool loadAllChunks = LoadAllChunks(tags);
   if (!loadAllChunks) {
     return NS_ERROR_FAILURE;
   }
 
-  mInfo.mHasAudio = true;
-  mInfo.mHasVideo = false;
-  mInfo.mAudioRate = mSampleRate;
-  mInfo.mAudioChannels = mChannels;
+  mInfo.mAudio.mHasAudio = true;
+  mInfo.mAudio.mRate = mSampleRate;
+  mInfo.mAudio.mChannels = mChannels;
 
   *aInfo = mInfo;
 
   *aTags = tags.forget();
 
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   mDecoder->SetMediaDuration(
@@ -268,17 +267,17 @@ nsresult WaveReader::Seek(int64_t aTarge
 }
 
 static double RoundToUsecs(double aSeconds) {
   return floor(aSeconds * USECS_PER_S) / USECS_PER_S;
 }
 
 nsresult WaveReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
 {
-  if (!mInfo.mHasAudio) {
+  if (!mInfo.HasAudio()) {
     return NS_OK;
   }
   int64_t startOffset = mDecoder->GetResource()->GetNextCachedData(mWavePCMOffset);
   while (startOffset >= 0) {
     int64_t endOffset = mDecoder->GetResource()->GetCachedDataEnd(startOffset);
     // Bytes [startOffset..endOffset] are cached.
     NS_ASSERTION(startOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
     NS_ASSERTION(endOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
--- a/content/media/wave/WaveReader.h
+++ b/content/media/wave/WaveReader.h
@@ -33,17 +33,17 @@ public:
     return true;
   }
 
   virtual bool HasVideo()
   {
     return false;
   }
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
 
   // To seek in a buffered range, we just have to seek the stream.
   virtual bool IsSeekableInBufferedRanges() {
     return true;
   }
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -263,19 +263,19 @@ MediaDecodeTask::Decode()
 
   // Tell the decoder reader that we are not going to play the data directly,
   // and that we should not reject files with more channels than the audio
   // bakend support.
   mDecoderReader->SetIgnoreAudioOutputFormat();
 
   mDecoderReader->OnDecodeThreadStart();
 
-  VideoInfo videoInfo;
+  MediaInfo mediaInfo;
   nsAutoPtr<MetadataTags> tags;
-  nsresult rv = mDecoderReader->ReadMetadata(&videoInfo, getter_Transfers(tags));
+  nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags));
   if (NS_FAILED(rv)) {
     ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
     return;
   }
 
   if (!mDecoderReader->HasAudio()) {
     ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio);
     return;
@@ -285,18 +285,18 @@ MediaDecodeTask::Decode()
     // consume all of the buffer
     continue;
   }
 
   mDecoderReader->OnDecodeThreadFinish();
 
   MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue();
   uint32_t frameCount = audioQueue.FrameCount();
-  uint32_t channelCount = videoInfo.mAudioChannels;
-  uint32_t sampleRate = videoInfo.mAudioRate;
+  uint32_t channelCount = mediaInfo.mAudio.mChannels;
+  uint32_t sampleRate = mediaInfo.mAudio.mRate;
 
   if (!frameCount || !channelCount || !sampleRate) {
     ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
     return;
   }
 
   const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate();
   AutoResampler resampler;
--- a/content/media/webm/WebMReader.cpp
+++ b/content/media/webm/WebMReader.cpp
@@ -251,18 +251,18 @@ nsresult WebMReader::ResetDecode()
 void WebMReader::Cleanup()
 {
   if (mContext) {
     nestegg_destroy(mContext);
     mContext = nullptr;
   }
 }
 
-nsresult WebMReader::ReadMetadata(VideoInfo* aInfo,
-                                    MetadataTags** aTags)
+nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
+                                  MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
 #ifdef MOZ_DASH
   LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: Reading WebM Metadata: "
                      "init bytes [%d - %d] cues bytes [%d - %d]",
                      this, mDecoder,
                      mInitByteRange.mStart, mInitByteRange.mEnd,
@@ -292,18 +292,16 @@ nsresult WebMReader::ReadMetadata(VideoI
 
   unsigned int ntracks = 0;
   r = nestegg_track_count(mContext, &ntracks);
   if (r == -1) {
     Cleanup();
     return NS_ERROR_FAILURE;
   }
 
-  mInfo.mHasAudio = false;
-  mInfo.mHasVideo = false;
   for (uint32_t track = 0; track < ntracks; ++track) {
     int id = nestegg_track_codec_id(mContext, track);
     if (id == -1) {
       Cleanup();
       return NS_ERROR_FAILURE;
     }
     int type = nestegg_track_type(mContext, track);
     if (!mHasVideo && type == NESTEGG_TRACK_VIDEO) {
@@ -339,51 +337,51 @@ nsresult WebMReader::ReadMetadata(VideoI
       nsIntSize frameSize(params.width, params.height);
       if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
         // Video track's frame sizes will overflow. Ignore the video track.
         continue;
       }
 
       mVideoTrack = track;
       mHasVideo = true;
-      mInfo.mHasVideo = true;
+      mInfo.mVideo.mHasVideo = true;
 
-      mInfo.mDisplay = displaySize;
+      mInfo.mVideo.mDisplay = displaySize;
       mPicture = pictureRect;
       mInitialFrame = frameSize;
 
       switch (params.stereo_mode) {
       case NESTEGG_VIDEO_MONO:
-        mInfo.mStereoMode = STEREO_MODE_MONO;
+        mInfo.mVideo.mStereoMode = STEREO_MODE_MONO;
         break;
       case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
-        mInfo.mStereoMode = STEREO_MODE_LEFT_RIGHT;
+        mInfo.mVideo.mStereoMode = STEREO_MODE_LEFT_RIGHT;
         break;
       case NESTEGG_VIDEO_STEREO_BOTTOM_TOP:
-        mInfo.mStereoMode = STEREO_MODE_BOTTOM_TOP;
+        mInfo.mVideo.mStereoMode = STEREO_MODE_BOTTOM_TOP;
         break;
       case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
-        mInfo.mStereoMode = STEREO_MODE_TOP_BOTTOM;
+        mInfo.mVideo.mStereoMode = STEREO_MODE_TOP_BOTTOM;
         break;
       case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
-        mInfo.mStereoMode = STEREO_MODE_RIGHT_LEFT;
+        mInfo.mVideo.mStereoMode = STEREO_MODE_RIGHT_LEFT;
         break;
       }
     }
     else if (!mHasAudio && type == NESTEGG_TRACK_AUDIO) {
       nestegg_audio_params params;
       r = nestegg_track_audio_params(mContext, track, &params);
       if (r == -1) {
         Cleanup();
         return NS_ERROR_FAILURE;
       }
 
       mAudioTrack = track;
       mHasAudio = true;
-      mInfo.mHasAudio = true;
+      mInfo.mAudio.mHasAudio = true;
 
       // Get the Vorbis header data
       unsigned int nheaders = 0;
       r = nestegg_track_codec_data_count(mContext, track, &nheaders);
       if (r == -1 || nheaders != 3) {
         Cleanup();
         return NS_ERROR_FAILURE;
       }
@@ -416,19 +414,19 @@ nsresult WebMReader::ReadMetadata(VideoI
       }
 
       r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
       if (r != 0) {
         Cleanup();
         return NS_ERROR_FAILURE;
       }
 
-      mInfo.mAudioRate = mVorbisDsp.vi->rate;
-      mInfo.mAudioChannels = mVorbisDsp.vi->channels;
-      mChannels = mInfo.mAudioChannels;
+      mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
+      mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
+      mChannels = mInfo.mAudio.mChannels;
     }
   }
 
 #ifdef MOZ_DASH
   // Byte range for cues has been specified; load them.
   if (!mCuesByteRange.IsNull()) {
     maxOffset = mCuesByteRange.mEnd;
 
@@ -900,17 +898,17 @@ bool WebMReader::DecodeVideoFrame(bool &
         // in WebM, and we will preserve the ratio of the crop rectangle as it
         // was reported relative to the picture size reported by the container.
         picture.x = (mPicture.x * img->d_w) / mInitialFrame.width;
         picture.y = (mPicture.y * img->d_h) / mInitialFrame.height;
         picture.width = (img->d_w * mPicture.width) / mInitialFrame.width;
         picture.height = (img->d_h * mPicture.height) / mInitialFrame.height;
       }
 
-      VideoData *v = VideoData::Create(mInfo,
+      VideoData *v = VideoData::Create(mInfo.mVideo,
                                        mDecoder->GetImageContainer(),
                                        holder->mOffset,
                                        tstamp_usecs,
                                        next_tstamp / NS_PER_USEC,
                                        b,
                                        si.is_kf,
                                        -1,
                                        picture);
--- a/content/media/webm/WebMReader.h
+++ b/content/media/webm/WebMReader.h
@@ -128,17 +128,17 @@ public:
   }
 
   virtual bool HasVideo()
   {
     NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
     return mHasVideo;
   }
 
-  virtual nsresult ReadMetadata(VideoInfo* aInfo,
+  virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
 #ifdef MOZ_DASH
   virtual void SetMainReader(DASHReader *aMainReader) MOZ_OVERRIDE {
     NS_ASSERTION(aMainReader, "aMainReader is null.");
--- a/content/media/wmf/WMFReader.cpp
+++ b/content/media/wmf/WMFReader.cpp
@@ -409,17 +409,17 @@ WMFReader::ConfigureVideoFrameGeometry(I
   nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
   ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
   if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
     // Video track's frame sizes will overflow. Ignore the video track.
     return E_FAIL;
   }
 
   // Success! Save state.
-  mInfo.mDisplay = displaySize;
+  mInfo.mVideo.mDisplay = displaySize;
   GetDefaultStride(aMediaType, &mVideoStride);
   mVideoWidth = width;
   mVideoHeight = height;
   mPictureRegion = pictureRegion;
 
   LOG("WMFReader frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d",
       width, height,
       mVideoStride,
@@ -465,17 +465,17 @@ WMFReader::ConfigureVideoDecoder()
 
   if (FAILED(ConfigureVideoFrameGeometry(mediaType))) {
     NS_WARNING("Failed configured video frame dimensions");
     return hr;
   }
 
   LOG("Successfully configured video stream");
 
-  mHasVideo = mInfo.mHasVideo = true;
+  mHasVideo = mInfo.mVideo.mHasVideo = true;
 
   return S_OK;
 }
 
 void
 WMFReader::GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs)
 {
   MOZ_ASSERT(aCodecs);
@@ -529,28 +529,28 @@ WMFReader::ConfigureAudioDecoder()
     NS_WARNING("Failed to get configured audio media type");
     return hr;
   }
 
   mAudioRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
   mAudioChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0);
   mAudioBytesPerSample = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16) / 8;
 
-  mInfo.mAudioChannels = mAudioChannels;
-  mInfo.mAudioRate = mAudioRate;
-  mHasAudio = mInfo.mHasAudio = true;
+  mInfo.mAudio.mChannels = mAudioChannels;
+  mInfo.mAudio.mRate = mAudioRate;
+  mHasAudio = mInfo.mAudio.mHasAudio = true;
 
   LOG("Successfully configured audio stream. rate=%u channels=%u bitsPerSample=%u",
       mAudioRate, mAudioChannels, mAudioBytesPerSample);
 
   return S_OK;
 }
 
 nsresult
-WMFReader::ReadMetadata(VideoInfo* aInfo,
+WMFReader::ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   LOG("WMFReader::ReadMetadata()");
   HRESULT hr;
 
   RefPtr<IMFAttributes> attr;
@@ -573,17 +573,17 @@ WMFReader::ReadMetadata(VideoInfo* aInfo
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   hr = ConfigureVideoDecoder();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   hr = ConfigureAudioDecoder();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
-  if (mUseHwAccel && mInfo.mHasVideo) {
+  if (mUseHwAccel && mInfo.mVideo.mHasVideo) {
     RefPtr<IMFTransform> videoDecoder;
     hr = mSourceReader->GetServiceForStream(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                                             GUID_NULL,
                                             IID_IMFTransform,
                                             (void**)(IMFTransform**)(byRef(videoDecoder)));
 
     if (SUCCEEDED(hr)) {
       ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
@@ -603,22 +603,22 @@ WMFReader::ReadMetadata(VideoInfo* aInfo
       // Re-run the configuration process, so that the output video format
       // is set correctly to reflect that hardware acceleration is disabled.
       // Without this, we'd be running with !mUseHwAccel and the output format
       // set to NV12, which is the format we expect when using hardware
       // acceleration. This would cause us to misinterpret the frame contents.
       hr = ConfigureVideoDecoder();
     }
   }
-  if (mInfo.mHasVideo) {
+  if (mInfo.HasVideo()) {
     LOG("Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
   }
 
   // Abort if both video and audio failed to initialize.
-  NS_ENSURE_TRUE(mInfo.mHasAudio || mInfo.mHasVideo, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(mInfo.HasValidMedia(), NS_ERROR_FAILURE);
 
   // Get the duration, and report it to the decoder if we have it.
   int64_t duration = 0;
   hr = GetSourceReaderDuration(mSourceReader, duration);
   if (SUCCEEDED(hr)) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(duration);
   }
@@ -836,17 +836,17 @@ WMFReader::CreateBasicVideoFrame(IMFSamp
   // V plane (Cr)
   b.mPlanes[2].mData = data + y_size;
   b.mPlanes[2].mStride = halfStride;
   b.mPlanes[2].mHeight = halfHeight;
   b.mPlanes[2].mWidth = halfWidth;
   b.mPlanes[2].mOffset = 0;
   b.mPlanes[2].mSkip = 0;
 
-  VideoData *v = VideoData::Create(mInfo,
+  VideoData *v = VideoData::Create(mInfo.mVideo,
                                    mDecoder->GetImageContainer(),
                                    aOffsetBytes,
                                    aTimestampUsecs,
                                    aTimestampUsecs + aDurationUsecs,
                                    b,
                                    false,
                                    -1,
                                    mPictureRegion);
@@ -879,17 +879,17 @@ WMFReader::CreateD3DVideoFrame(IMFSample
   nsRefPtr<Image> image;
   hr = mDXVA2Manager->CopyToImage(aSample,
                                   mPictureRegion,
                                   mDecoder->GetImageContainer(),
                                   getter_AddRefs(image));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   NS_ENSURE_TRUE(image, E_FAIL);
 
-  VideoData *v = VideoData::CreateFromImage(mInfo,
+  VideoData *v = VideoData::CreateFromImage(mInfo.mVideo,
                                             mDecoder->GetImageContainer(),
                                             aOffsetBytes,
                                             aTimestampUsecs,
                                             aTimestampUsecs + aDurationUsecs,
                                             image.forget(),
                                             false,
                                             -1,
                                             mPictureRegion);
--- a/content/media/wmf/WMFReader.h
+++ b/content/media/wmf/WMFReader.h
@@ -35,17 +35,17 @@ public:
 
   bool DecodeAudioData() MOZ_OVERRIDE;
   bool DecodeVideoFrame(bool &aKeyframeSkip,
                         int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   bool HasAudio() MOZ_OVERRIDE;
   bool HasVideo() MOZ_OVERRIDE;
 
-  nsresult ReadMetadata(VideoInfo* aInfo,
+  nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) MOZ_OVERRIDE;
 
   nsresult Seek(int64_t aTime,
                 int64_t aStartTime,
                 int64_t aEndTime,
                 int64_t aCurrentTime) MOZ_OVERRIDE;
 
   nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered,