Bug 578536 - Ensure WebMReader properly tags its decoded samples with file offsets. r=doublec a=blocking2.0
authorChris Pearce <chris@pearce.org.nz>
Mon, 29 Nov 2010 09:06:38 +1300
changeset 58313 00ea82fd6c59e927f4d78d4d5a78fd2a3329ff31
parent 58312 7db5a5d297c67361a110e1b947d017a17e00f7a6
child 58314 c194928bb4a9dd39006ab33f114aceff96518f28
push id17239
push usercpearce@mozilla.com
push dateSun, 28 Nov 2010 21:13:43 +0000
treeherdermozilla-central@00ea82fd6c59 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdoublec, blocking2.0
bugs578536
milestone2.0b8pre
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 578536 - Ensure WebMReader properly tags its decoded samples with file offsets. r=doublec a=blocking2.0
content/media/webm/nsWebMReader.cpp
content/media/webm/nsWebMReader.h
--- a/content/media/webm/nsWebMReader.cpp
+++ b/content/media/webm/nsWebMReader.cpp
@@ -62,20 +62,20 @@ extern PRLogModuleInfo* gBuiltinDecoderL
 #define SEEK_LOG(type, msg)
 #endif
 
 static const unsigned NS_PER_MS = 1000000;
 static const float NS_PER_S = 1e9;
 static const float MS_PER_S = 1e3;
 
 NS_SPECIALIZE_TEMPLATE
-class nsAutoRefTraits<nestegg_packet> : public nsPointerRefTraits<nestegg_packet>
+class nsAutoRefTraits<NesteggPacketHolder> : public nsPointerRefTraits<NesteggPacketHolder>
 {
 public:
-  static void Release(nestegg_packet* aPacket) { nestegg_free_packet(aPacket); }
+  static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
 };
 
 // Functions for reading and seeking using nsMediaStream required for
 // nestegg_io. The 'user data' passed to these functions is the
 // decoder from which the media stream is obtained.
 static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
@@ -346,17 +346,17 @@ ogg_packet nsWebMReader::InitOggPacket(u
   packet.bytes = aLength;
   packet.b_o_s = aBOS;
   packet.e_o_s = aEOS;
   packet.granulepos = aGranulepos;
   packet.packetno = mPacketCount++;
   return packet;
 }
  
-PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket)
+PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset)
 {
   mMonitor.AssertCurrentThreadIn();
 
   int r = 0;
   unsigned int count = 0;
   r = nestegg_packet_count(aPacket, &count);
   if (r == -1) {
     return PR_FALSE;
@@ -444,34 +444,34 @@ PRBool nsWebMReader::DecodeAudioPacket(n
       PRInt64 total_duration = 0;
       if (!SamplesToMs(total_samples, rate, total_duration)) {
         NS_WARNING("Int overflow converting WebM audio total_duration");
         return PR_FALSE;
       }
       
       PRInt64 time = tstamp_ms + total_duration;
       total_samples += samples;
-      SoundData* s = new SoundData(0,
+      SoundData* s = new SoundData(aOffset,
                                    time,
                                    duration,
                                    samples,
                                    buffer,
                                    mChannels);
       mAudioQueue.Push(s);
       mAudioSamples += samples;
       if (vorbis_synthesis_read(&mVorbisDsp, samples) != 0) {
         return PR_FALSE;
       }
     }
   }
 
   return PR_TRUE;
 }
 
-nsReturnRef<nestegg_packet> nsWebMReader::NextPacket(TrackType aTrackType)
+nsReturnRef<NesteggPacketHolder> nsWebMReader::NextPacket(TrackType aTrackType)
 {
   // The packet queue that packets will be pushed on if they
   // are not the type we are interested in.
   PacketQueue& otherPackets = 
     aTrackType == VIDEO ? mAudioPackets : mVideoPackets;
 
   // The packet queue for the type that we are interested in.
   PacketQueue &packets =
@@ -486,80 +486,82 @@ nsReturnRef<nestegg_packet> nsWebMReader
   PRPackedBool hasOtherType = aTrackType == VIDEO ? mHasAudio : mHasVideo;
 
   // Track we are interested in
   PRUint32 ourTrack = aTrackType == VIDEO ? mVideoTrack : mAudioTrack;
 
   // Value of other track
   PRUint32 otherTrack = aTrackType == VIDEO ? mAudioTrack : mVideoTrack;
 
-  nsAutoRef<nestegg_packet> packet;
+  nsAutoRef<NesteggPacketHolder> holder;
 
   if (packets.GetSize() > 0) {
-    packet.own(packets.PopFront());
+    holder.own(packets.PopFront());
   } else {
     // Keep reading packets until we find a packet
     // for the track we want.
     do {
-      nestegg_packet* p;
-      int r = nestegg_read_packet(mContext, &p);
+      nestegg_packet* packet;
+      int r = nestegg_read_packet(mContext, &packet);
       if (r <= 0) {
-        return nsReturnRef<nestegg_packet>();
+        return nsReturnRef<NesteggPacketHolder>();
       }
-      packet.own(p);
+      PRInt64 offset = mDecoder->GetCurrentStream()->Tell();
+      holder.own(new NesteggPacketHolder(packet, offset));
 
       unsigned int track = 0;
       r = nestegg_packet_track(packet, &track);
       if (r == -1) {
-        return nsReturnRef<nestegg_packet>();
+        return nsReturnRef<NesteggPacketHolder>();
       }
 
       if (hasOtherType && otherTrack == track) {
         // Save the packet for when we want these packets
-        otherPackets.Push(packet.disown());
+        otherPackets.Push(holder.disown());
         continue;
       }
 
       // The packet is for the track we want to play
       if (hasType && ourTrack == track) {
         break;
       }
     } while (PR_TRUE);
   }
 
-  return packet.out();
+  return holder.out();
 }
 
 PRBool nsWebMReader::DecodeAudioData()
 {
   MonitorAutoEnter mon(mMonitor);
   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
     "Should be on state machine thread or decode thread.");
-  nsAutoRef<nestegg_packet> packet(NextPacket(AUDIO));
-  if (!packet) {
+  nsAutoRef<NesteggPacketHolder> holder(NextPacket(AUDIO));
+  if (!holder) {
     mAudioQueue.Finish();
     return PR_FALSE;
   }
 
-  return DecodeAudioPacket(packet);
+  return DecodeAudioPacket(holder->mPacket, holder->mOffset);
 }
 
 PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
                                       PRInt64 aTimeThreshold)
 {
   MonitorAutoEnter mon(mMonitor);
   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
-  nsAutoRef<nestegg_packet> packet(NextPacket(VIDEO));
-  if (!packet) {
+  nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
+  if (!holder) {
     mVideoQueue.Finish();
     return PR_FALSE;
   }
 
+  nestegg_packet* packet = holder->mPacket;
   unsigned int track = 0;
   int r = nestegg_packet_track(packet, &track);
   if (r == -1) {
     return PR_FALSE;
   }
 
   unsigned int count = 0;
   r = nestegg_packet_count(packet, &count);
@@ -574,23 +576,23 @@ PRBool nsWebMReader::DecodeVideoFrame(PR
   }
 
   // The end time of this frame is the start time of the next frame.  Fetch
   // the timestamp of the next packet for this track.  If we've reached the
   // end of the stream, use the file's duration as the end time of this
   // video frame.
   uint64_t next_tstamp = 0;
   {
-    nsAutoRef<nestegg_packet> next_packet(NextPacket(VIDEO));
-    if (next_packet) {
-      r = nestegg_packet_tstamp(next_packet, &next_tstamp);
+    nsAutoRef<NesteggPacketHolder> next_holder(NextPacket(VIDEO));
+    if (next_holder) {
+      r = nestegg_packet_tstamp(next_holder->mPacket, &next_tstamp);
       if (r == -1) {
         return PR_FALSE;
       }
-      mVideoPackets.PushFront(next_packet.disown());
+      mVideoPackets.PushFront(next_holder.disown());
     } else {
       MonitorAutoExit exitMon(mMonitor);
       MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
       nsBuiltinDecoderStateMachine* s =
         static_cast<nsBuiltinDecoderStateMachine*>(mDecoder->GetStateMachine());
       PRInt64 endTime = s->GetEndMediaTime();
       if (endTime == -1) {
         return PR_FALSE;
@@ -656,17 +658,17 @@ PRBool nsWebMReader::DecodeVideoFrame(PR
  
       b.mPlanes[2].mData = img->planes[2];
       b.mPlanes[2].mStride = img->stride[2];
       b.mPlanes[2].mHeight = img->d_h >> img->y_chroma_shift;
       b.mPlanes[2].mWidth = img->d_w >> img->x_chroma_shift;
   
       VideoData *v = VideoData::Create(mInfo,
                                        mDecoder->GetImageContainer(),
-                                       -1,
+                                       holder->mOffset,
                                        tstamp_ms,
                                        next_tstamp / NS_PER_MS,
                                        b,
                                        si.is_kf,
                                        -1);
       if (!v) {
         return PR_FALSE;
       }
--- a/content/media/webm/nsWebMReader.h
+++ b/content/media/webm/nsWebMReader.h
@@ -49,20 +49,45 @@
 #ifdef MOZ_TREMOR
 #include "tremor/ivorbiscodec.h"
 #else
 #include "vorbis/codec.h"
 #endif
 
 class nsMediaDecoder;
 
+// Holds a nestegg_packet, and its file offset. This is needed so we
+// know the offset in the file we've played up to, in order to calculate
+// whether it's likely we can play through to the end without needing
+// to stop to buffer, given the current download rate.
+class NesteggPacketHolder {
+public:
+  NesteggPacketHolder(nestegg_packet* aPacket, PRInt64 aOffset)
+    : mPacket(aPacket), mOffset(aOffset)
+  {
+    MOZ_COUNT_CTOR(NesteggPacketHolder);
+  }
+  ~NesteggPacketHolder() {
+    MOZ_COUNT_DTOR(NesteggPacketHolder);
+    nestegg_free_packet(mPacket);
+  }
+  nestegg_packet* mPacket;
+  // Offset in bytes. This is the offset of the end of the Block
+  // which contains the packet.
+  PRInt64 mOffset;
+private:
+  // Copy constructor and assignment operator not implemented. Don't use them!
+  NesteggPacketHolder(const NesteggPacketHolder &aOther);
+  NesteggPacketHolder& operator= (NesteggPacketHolder const& aOther);
+};
+
 // Thread and type safe wrapper around nsDeque.
 class PacketQueueDeallocator : public nsDequeFunctor {
   virtual void* operator() (void* anObject) {
-    nestegg_free_packet(static_cast<nestegg_packet*>(anObject));
+    delete static_cast<NesteggPacketHolder*>(anObject);
     return nsnull;
   }
 };
 
 // Typesafe queue for holding nestegg packets. It has
 // ownership of the items in the queue and will free them
 // when destroyed.
 class PacketQueue : private nsDeque {
@@ -74,33 +99,33 @@ class PacketQueue : private nsDeque {
   ~PacketQueue() {
     Reset();
   }
 
   inline PRInt32 GetSize() { 
     return nsDeque::GetSize();
   }
   
-  inline void Push(nestegg_packet* aItem) {
+  inline void Push(NesteggPacketHolder* aItem) {
     NS_ASSERTION(aItem, "NULL pushed to PacketQueue");
     nsDeque::Push(aItem);
   }
   
-  inline void PushFront(nestegg_packet* aItem) {
+  inline void PushFront(NesteggPacketHolder* aItem) {
     NS_ASSERTION(aItem, "NULL pushed to PacketQueue");
     nsDeque::PushFront(aItem);
   }
 
-  inline nestegg_packet* PopFront() {
-    return static_cast<nestegg_packet*>(nsDeque::PopFront());
+  inline NesteggPacketHolder* PopFront() {
+    return static_cast<NesteggPacketHolder*>(nsDeque::PopFront());
   }
   
   void Reset() {
     while (GetSize() > 0) {
-      nestegg_free_packet(PopFront());
+      delete PopFront();
     }
   }
 };
 
 class nsWebMReader : public nsBuiltinDecoderReader
 {
 public:
   nsWebMReader(nsBuiltinDecoder* aDecoder);
@@ -139,32 +164,32 @@ private:
   enum TrackType {
     VIDEO = 0,
     AUDIO = 1
   };
 
   // Read a packet from the nestegg file. Returns NULL if all packets for
   // the particular track have been read. Pass VIDEO or AUDIO to indicate the
   // type of the packet we want to read.
-  nsReturnRef<nestegg_packet> NextPacket(TrackType aTrackType);
+  nsReturnRef<NesteggPacketHolder> NextPacket(TrackType aTrackType);
 
   // Returns an initialized ogg packet with data obtained from the WebM container.
   ogg_packet InitOggPacket(unsigned char* aData,
                            size_t aLength,
                            PRBool aBOS,
                            PRBool aEOS,
                            PRInt64 aGranulepos);
                      
   // Decode a nestegg packet of audio data. Push the audio data on the
   // audio queue. Returns PR_TRUE when there's more audio to decode,
   // PR_FALSE if the audio is finished, end of file has been reached,
   // or an un-recoverable read error has occured. The reader's monitor
   // must be held during this call. This function will free the packet
   // so the caller must not use the packet after calling.
-  PRBool DecodeAudioPacket(nestegg_packet* aPacket);
+  PRBool DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset);
 
   // Release context and set to null. Called when an error occurs during
   // reading metadata or destruction of the reader itself.
   void Cleanup();
 
 private:
   // libnestegg context for webm container. Access on state machine thread
   // or decoder thread only.