Bug 1322070: P7. Construct VideoInfo object from Theora CodecState. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sun, 11 Dec 2016 16:54:19 -1000
changeset 448647 f2bf734c35553ceaad971c90d3259fc7ec051f03
parent 448646 80a4a2137fa2db48def26ffef7de387aceb997d3
child 448648 a56adbcccaaa675638ad6e277bcef3533870d80c
push id38389
push userbmo:jyavenard@mozilla.com
push dateMon, 12 Dec 2016 05:01:11 +0000
reviewersgerald
bugs1322070
milestone53.0a1
Bug 1322070: P7. Construct VideoInfo object from Theora CodecState. r?gerald MozReview-Commit-ID: E4gR2Ff7qo
dom/media/ogg/OggCodecState.cpp
dom/media/ogg/OggCodecState.h
dom/media/ogg/OggDemuxer.cpp
dom/media/ogg/OggDemuxer.h
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -142,16 +142,34 @@ OggCodecState::AddVorbisComment(Metadata
   if (!IsUTF8(value)) {
     LOG(LogLevel::Debug, ("Skipping comment: invalid UTF-8 in value"));
     return false;
   }
   aTags->Put(key, value);
   return true;
 }
 
+bool
+OggCodecState::SetCodecSpecificConfig(MediaByteBuffer* aBuffer,
+                                      OggPacketQueue& aHeaders)
+{
+  nsTArray<const unsigned char*> headers;
+  nsTArray<size_t> headerLens;
+  for (size_t i = 0; i < aHeaders.Length(); i++) {
+    headers.AppendElement(aHeaders[i]->packet);
+    headerLens.AppendElement(aHeaders[i]->bytes);
+  }
+  // Save header packets for the decoder
+  if (!XiphHeadersToExtradata(aBuffer, headers, headerLens)) {
+    return false;
+  }
+  aHeaders.Erase();
+  return true;
+}
+
 void
 VorbisState::RecordVorbisPacketSamples(ogg_packet* aPacket, long aSamples)
 {
 #ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
   mVorbisPacketSamples[aPacket] = aSamples;
 #endif
 }
 
@@ -314,67 +332,83 @@ OggCodecState::PacketOutUntilGranulepos(
   }
   return NS_OK;
 }
 
 TheoraState::TheoraState(ogg_page* aBosPage)
   : OggCodecState(aBosPage, true)
   , mSetup(0)
   , mCtx(0)
-  , mPixelAspectRatio(0)
 {
   MOZ_COUNT_CTOR(TheoraState);
-  th_info_init(&mInfo);
+  th_info_init(&mTheoraInfo);
   th_comment_init(&mComment);
 }
 
 TheoraState::~TheoraState()
 {
   MOZ_COUNT_DTOR(TheoraState);
   th_setup_free(mSetup);
   th_decode_free(mCtx);
   th_comment_clear(&mComment);
-  th_info_clear(&mInfo);
+  th_info_clear(&mTheoraInfo);
+  Reset();
 }
 
 bool
 TheoraState::Init()
 {
   if (!mActive) {
     return false;
   }
 
-  int64_t n = mInfo.aspect_numerator;
-  int64_t d = mInfo.aspect_denominator;
+  int64_t n = mTheoraInfo.aspect_numerator;
+  int64_t d = mTheoraInfo.aspect_denominator;
 
-  mPixelAspectRatio = (n == 0 || d == 0)
+  float aspectRatio = (n == 0 || d == 0)
     ? 1.0f : static_cast<float>(n) / static_cast<float>(d);
 
   // Ensure the frame and picture regions aren't larger than our prescribed
   // maximum, or zero sized.
-  nsIntSize frame(mInfo.frame_width, mInfo.frame_height);
-  nsIntRect picture(mInfo.pic_x, mInfo.pic_y, mInfo.pic_width, mInfo.pic_height);
-  if (!IsValidVideoRegion(frame, picture, frame)) {
+  nsIntSize frame(mTheoraInfo.frame_width, mTheoraInfo.frame_height);
+  nsIntRect picture(mTheoraInfo.pic_x, mTheoraInfo.pic_y, mTheoraInfo.pic_width, mTheoraInfo.pic_height);
+  nsIntSize display(mTheoraInfo.pic_width, mTheoraInfo.pic_height);
+  ScaleDisplayByAspectRatio(display, aspectRatio);
+  if (!IsValidVideoRegion(frame, picture, display)) {
     return mActive = false;
   }
 
-  mCtx = th_decode_alloc(&mInfo, mSetup);
+  mCtx = th_decode_alloc(&mTheoraInfo, mSetup);
   if (!mCtx) {
     return mActive = false;
   }
 
-  return true;
+  // Video track's frame sizes will not overflow. Activate the video track.
+  mInfo.mMimeType = NS_LITERAL_CSTRING("video/theora");
+  mInfo.mDisplay = display;
+  mInfo.mImage = frame;
+  mInfo.SetImageRect(picture);
+  mKeyframe_granule_shift = mTheoraInfo.keyframe_granule_shift;
+
+  return mActive = SetCodecSpecificConfig(mInfo.mCodecSpecificConfig, mHeaders);
+}
+
+nsresult
+TheoraState::Reset()
+{
+  mHeaders.Erase();
+  return OggCodecState::Reset();
 }
 
 bool
 TheoraState::DecodeHeader(ogg_packet* aPacket)
 {
-  nsAutoRef<ogg_packet> autoRelease(aPacket);
+  mHeaders.Append(aPacket);
   mPacketCount++;
-  int ret = th_decode_headerin(&mInfo,
+  int ret = th_decode_headerin(&mTheoraInfo,
                                &mComment,
                                &mSetup,
                                aPacket);
 
   // We must determine when we've read the last header packet.
   // th_decode_headerin() does not tell us when it's read the last header, so
   // we must keep track of the headers externally.
   //
@@ -402,17 +436,17 @@ TheoraState::DecodeHeader(ogg_packet* aP
 }
 
 int64_t
 TheoraState::Time(int64_t granulepos)
 {
   if (!mActive) {
     return -1;
   }
-  return TheoraState::Time(&mInfo, granulepos);
+  return TheoraState::Time(&mTheoraInfo, granulepos);
 }
 
 bool
 TheoraState::IsHeader(ogg_packet* aPacket)
 {
   return th_packet_isheader(aPacket);
 }
 
@@ -439,54 +473,55 @@ TheoraState::Time(th_info* aInfo, int64_
     return -1;
   }
   t /= aInfo->fps_numerator;
   return t.isValid() ? t.value() : -1;
 }
 
 int64_t TheoraState::StartTime(int64_t granulepos)
 {
-  if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
+  if (granulepos < 0 || !mActive || mTheoraInfo.fps_numerator == 0) {
     return -1;
   }
   CheckedInt64 t =
     (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S)
-    * mInfo.fps_denominator;
+    * mTheoraInfo.fps_denominator;
   if (!t.isValid()) {
     return -1;
   }
-  return t.value() / mInfo.fps_numerator;
+  return t.value() / mTheoraInfo.fps_numerator;
 }
 
 int64_t
 TheoraState::PacketDuration(ogg_packet* aPacket)
 {
-  if (!mActive || mInfo.fps_numerator == 0) {
+  if (!mActive || mTheoraInfo.fps_numerator == 0) {
     return -1;
   }
-  CheckedInt64 t =
-    SaferMultDiv(mInfo.fps_denominator, USECS_PER_S, mInfo.fps_numerator);
+  CheckedInt64 t = SaferMultDiv(mTheoraInfo.fps_denominator, USECS_PER_S,
+                                mTheoraInfo.fps_numerator);
   return t.isValid() ? t.value() : -1;
 }
 
 int64_t
 TheoraState::MaxKeyframeOffset()
 {
   // Determine the maximum time in microseconds by which a key frame could
   // offset for the theora bitstream. Theora granulepos encode time as:
   // ((key_frame_number << granule_shift) + frame_offset).
   // Therefore the maximum possible time by which any frame could be offset
   // from a keyframe is the duration of (1 << granule_shift) - 1) frames.
   int64_t frameDuration;
 
   // Max number of frames keyframe could possibly be offset.
-  int64_t keyframeDiff = (1 << mInfo.keyframe_granule_shift) - 1;
+  int64_t keyframeDiff = (1 << mTheoraInfo.keyframe_granule_shift) - 1;
 
   // Length of frame in usecs.
-  frameDuration = (mInfo.fps_denominator * USECS_PER_S) / mInfo.fps_numerator;
+  frameDuration =
+    (mTheoraInfo.fps_denominator * USECS_PER_S) / mTheoraInfo.fps_numerator;
 
   // Total time in usecs keyframe can be offset from any given frame.
   return frameDuration * keyframeDiff;
 }
 
 bool
 TheoraState::IsKeyframe(ogg_packet* pkt)
 {
@@ -548,18 +583,18 @@ TheoraState::ReconstructTheoraGranulepos
   }
   ogg_int64_t lastGranulepos = mUnstamped[mUnstamped.Length() - 1]->granulepos;
   NS_ASSERTION(lastGranulepos != -1, "Must know last granulepos");
 
   // Reconstruct the granulepos (and thus timestamps) of the decoded
   // frames. Granulepos are stored as ((keyframe<<shift)+offset). We
   // know the granulepos of the last frame in the list, so we can infer
   // the granulepos of the intermediate frames using their frame numbers.
-  ogg_int64_t shift = mInfo.keyframe_granule_shift;
-  ogg_int64_t version_3_2_1 = TheoraVersion(&mInfo,3,2,1);
+  ogg_int64_t shift = mTheoraInfo.keyframe_granule_shift;
+  ogg_int64_t version_3_2_1 = TheoraVersion(&mTheoraInfo,3,2,1);
   ogg_int64_t lastFrame = th_granule_frame(mCtx,
                                            lastGranulepos) + version_3_2_1;
   ogg_int64_t firstFrame = lastFrame - mUnstamped.Length() + 1;
 
   // Until we encounter a keyframe, we'll assume that the "keyframe"
   // segment of the granulepos is the first frame, or if that causes
   // the "offset" segment to overflow, we assume the required
   // keyframe is maximumally offset. Until we encounter a keyframe
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -262,16 +262,18 @@ protected:
   // can be pushed over to mPackets. Used by PageIn() implementations in
   // subclasses.
   nsresult PacketOutUntilGranulepos(bool& aFoundGranulepos);
 
   // Temporary buffer in which to store packets while we're reading packets
   // in order to capture granulepos.
   nsTArray<ogg_packet*> mUnstamped;
 
+  bool SetCodecSpecificConfig(MediaByteBuffer* aBuffer, OggPacketQueue& aHeaders);
+
 private:
   bool InternalInit();
 };
 
 class VorbisState : public OggCodecState
 {
 public:
   explicit VorbisState(ogg_page* aBosPage);
@@ -355,35 +357,39 @@ public:
   virtual ~TheoraState();
 
   CodecType GetType() override { return TYPE_THEORA; }
   bool DecodeHeader(ogg_packet* aPacket) override;
   int64_t Time(int64_t granulepos) override;
   int64_t StartTime(int64_t granulepos) override;
   int64_t PacketDuration(ogg_packet* aPacket) override;
   bool Init() override;
+  nsresult Reset() override;
   bool IsHeader(ogg_packet* aPacket) override;
   bool IsKeyframe(ogg_packet* aPacket) override;
   nsresult PageIn(ogg_page* aPage) override;
+  const TrackInfo* GetInfo() const override { return &mInfo; }
 
   // Returns the maximum number of microseconds which a keyframe can be offset
   // from any given interframe.
   int64_t MaxKeyframeOffset();
+  // Public access for mTheoraInfo.keyframe_granule_shift
+  int32_t mKeyframe_granule_shift;
 
+private:
   // Returns the end time that a granulepos represents.
   static int64_t Time(th_info* aInfo, int64_t aGranulePos);
 
-  th_info mInfo;
+  th_info mTheoraInfo;
   th_comment mComment;
   th_setup_info* mSetup;
   th_dec_ctx* mCtx;
 
-  float mPixelAspectRatio;
-
-private:
+  VideoInfo mInfo;
+  OggPacketQueue mHeaders;
 
   // Reconstructs the granulepos of Theora packets stored in the
   // mUnstamped array. mUnstamped must be filled with consecutive packets from
   // the stream, with the last packet having a known granulepos. Using this
   // known granulepos, and the known frame numbers, we recover the granulepos
   // of all frames in the array. This enables us to determine their timestamps.
   void ReconstructTheoraGranulepos();
 
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -139,17 +139,16 @@ OggDemuxer::OggDemuxer(MediaResource* aR
   , mOpusSerial(0)
   , mTheoraSerial(0)
   , mFlacSerial(0)
   , mIsChained(false)
   , mTimedMetadataEvent(nullptr)
   , mOnSeekableEvent(nullptr)
 {
   MOZ_COUNT_CTOR(OggDemuxer);
-  PodZero(&mTheoraInfo);
 }
 
 OggDemuxer::~OggDemuxer()
 {
   MOZ_COUNT_DTOR(OggDemuxer);
   Reset(TrackInfo::kAudioTrack);
   Reset(TrackInfo::kVideoTrack);
   if (HasAudio() || HasVideo()) {
@@ -374,49 +373,19 @@ OggDemuxer::BuildSerialList(nsTArray<uin
 
 void
 OggDemuxer::SetupTargetTheora(TheoraState* aTheoraState, OggHeaders& aHeaders)
 {
   if (mTheoraState) {
     mTheoraState->Reset();
   }
 
-  nsIntRect picture = nsIntRect(aTheoraState->mInfo.pic_x,
-                                aTheoraState->mInfo.pic_y,
-                                aTheoraState->mInfo.pic_width,
-                                aTheoraState->mInfo.pic_height);
-
-  nsIntSize displaySize = nsIntSize(aTheoraState->mInfo.pic_width,
-                                    aTheoraState->mInfo.pic_height);
-
-  // Apply the aspect ratio to produce the intrinsic display size we report
-  // to the element.
-  ScaleDisplayByAspectRatio(displaySize, aTheoraState->mPixelAspectRatio);
-
-  nsIntSize frameSize(aTheoraState->mInfo.frame_width,
-                      aTheoraState->mInfo.frame_height);
-  if (IsValidVideoRegion(frameSize, picture, displaySize)) {
-    // Video track's frame sizes will not overflow. Activate the video track.
-    mInfo.mVideo.mMimeType = "video/theora";
-    mInfo.mVideo.mDisplay = displaySize;
-    mInfo.mVideo.mImage = frameSize;
-    mInfo.mVideo.SetImageRect(picture);
-
-    // Copy Theora info data for time computations on other threads.
-    memcpy(&mTheoraInfo, &aTheoraState->mInfo, sizeof(mTheoraInfo));
-
-    // Save header packets for the decoder
-    if (!XiphHeadersToExtradata(mInfo.mVideo.mCodecSpecificConfig,
-                                aHeaders.mHeaders, aHeaders.mHeaderLens)) {
-      return;
-    }
-
-    mTheoraState = aTheoraState;
-    mTheoraSerial = aTheoraState->mSerial;
-  }
+  mInfo.mVideo = *aTheoraState->GetInfo()->GetAsVideoInfo();
+  mTheoraState = aTheoraState;
+  mTheoraSerial = aTheoraState->mSerial;
 }
 
 void
 OggDemuxer::SetupTargetVorbis(VorbisState* aVorbisState, OggHeaders& aHeaders)
 {
   if (mVorbisState) {
     mVorbisState->Reset();
   }
@@ -497,32 +466,21 @@ OggDemuxer::SetupMediaTracksInfo(const n
     }
 
     if (codecState->GetType() == OggCodecState::TYPE_THEORA) {
       TheoraState* theoraState = static_cast<TheoraState*>(codecState);
       if (!(mTheoraState && mTheoraState->mSerial == theoraState->mSerial)) {
         continue;
       }
 
+      mInfo.mVideo = *theoraState->GetInfo()->GetAsVideoInfo();
+
       if (msgInfo) {
         InitTrack(msgInfo, &mInfo.mVideo, mTheoraState == theoraState);
       }
-
-      nsIntRect picture = nsIntRect(theoraState->mInfo.pic_x,
-                                    theoraState->mInfo.pic_y,
-                                    theoraState->mInfo.pic_width,
-                                    theoraState->mInfo.pic_height);
-      nsIntSize displaySize = nsIntSize(theoraState->mInfo.pic_width,
-                                        theoraState->mInfo.pic_height);
-      nsIntSize frameSize(theoraState->mInfo.frame_width,
-                          theoraState->mInfo.frame_height);
-      ScaleDisplayByAspectRatio(displaySize, theoraState->mPixelAspectRatio);
-      if (IsValidVideoRegion(frameSize, picture, displaySize)) {
-        mInfo.mVideo.mDisplay = displaySize;
-      }
     } else if (codecState->GetType() == OggCodecState::TYPE_VORBIS) {
       VorbisState* vorbisState = static_cast<VorbisState*>(codecState);
       if (!(mVorbisState && mVorbisState->mSerial == vorbisState->mSerial)) {
         continue;
       }
 
       mInfo.mAudio = *vorbisState->GetInfo()->GetAsAudioInfo();
 
@@ -1090,17 +1048,17 @@ OggDemuxer::GetBuffered(TrackInfo::Track
         startTime = mOpusState->Time(granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kAudioTrack && mFlacState &&
                  serial == mFlacSerial) {
         startTime = mFlacState->Time(granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (aType == TrackInfo::kVideoTrack && mTheoraState &&
                  serial == mTheoraSerial) {
-        startTime = TheoraState::Time(&mTheoraInfo, granulepos);
+        startTime = mTheoraState->Time(granulepos);
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       } else if (mCodecStore.Contains(serial)) {
         // Stream is not the theora or vorbis stream we're playing,
         // but is one that we have header data for.
         startOffset += page.header_len + page.body_len;
         continue;
       } else {
         // Page is for a stream we don't know about (possibly a chained
@@ -1828,21 +1786,22 @@ OggDemuxer::SeekInBufferedRange(TrackInf
     // We have an active Theora bitstream. Peek the next Theora frame, and
     // extract its keyframe's time.
     DemuxUntilPacketAvailable(aType, mTheoraState);
     ogg_packet* packet = mTheoraState->PacketPeek();
     if (packet && !mTheoraState->IsKeyframe(packet)) {
       // First post-seek frame isn't a keyframe, seek back to previous keyframe,
       // otherwise we'll get visual artifacts.
       NS_ASSERTION(packet->granulepos != -1, "Must have a granulepos");
-      int shift = mTheoraState->mInfo.keyframe_granule_shift;
+      int shift = mTheoraState->mKeyframe_granule_shift;
       int64_t keyframeGranulepos = (packet->granulepos >> shift) << shift;
       int64_t keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
-      SEEK_LOG(LogLevel::Debug, ("Keyframe for %lld is at %lld, seeking back to it",
-                              frameTime, keyframeTime));
+      SEEK_LOG(LogLevel::Debug,
+               ("Keyframe for %lld is at %lld, seeking back to it", frameTime,
+                keyframeTime));
       aAdjustedTarget = std::min(aAdjustedTarget, keyframeTime);
     }
   }
 
   nsresult res = NS_OK;
   if (aAdjustedTarget < aTarget) {
     SeekRange k = SelectSeekRange(aType,
                                   aRanges,
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -303,18 +303,16 @@ private:
   // data in the codec states due to threading issues. You must check the
   // associated mTheoraState or mVorbisState pointer is non-null before
   // using this codec data.
   uint32_t mVorbisSerial;
   uint32_t mOpusSerial;
   uint32_t mTheoraSerial;
   uint32_t mFlacSerial;
 
-  th_info mTheoraInfo;
-
   Maybe<int64_t> mStartTime;
 
   // Booleans to indicate if we have audio and/or video data
   bool HasVideo() const;
   bool HasAudio() const;
   bool HasSkeleton() const
   {
     return mSkeletonState != 0 && mSkeletonState->mActive;