From 567824b84185389387131ed57d4f7a79fec10204 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Mon, 29 Nov 2010 09:06:38 +1300 Subject: [PATCH] Bug 578536 - Ensure WebMReader properly tags its decoded samples with file offsets. r=doublec a=blocking2.0 --- content/media/webm/nsWebMReader.cpp | 50 +++++++++++++++-------------- content/media/webm/nsWebMReader.h | 41 ++++++++++++++++++----- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/content/media/webm/nsWebMReader.cpp b/content/media/webm/nsWebMReader.cpp index f8cc73ed7d1..5d50b094d7a 100644 --- a/content/media/webm/nsWebMReader.cpp +++ b/content/media/webm/nsWebMReader.cpp @@ -67,10 +67,10 @@ static const float NS_PER_S = 1e9; static const float MS_PER_S = 1e3; NS_SPECIALIZE_TEMPLATE -class nsAutoRefTraits : public nsPointerRefTraits +class nsAutoRefTraits : public nsPointerRefTraits { 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 @@ -351,7 +351,7 @@ ogg_packet nsWebMReader::InitOggPacket(unsigned char* aData, return packet; } -PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket) +PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset) { mMonitor.AssertCurrentThreadIn(); @@ -449,7 +449,7 @@ PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket) PRInt64 time = tstamp_ms + total_duration; total_samples += samples; - SoundData* s = new SoundData(0, + SoundData* s = new SoundData(aOffset, time, duration, samples, @@ -466,7 +466,7 @@ PRBool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket) return PR_TRUE; } -nsReturnRef nsWebMReader::NextPacket(TrackType aTrackType) +nsReturnRef nsWebMReader::NextPacket(TrackType aTrackType) { // The packet queue that packets will be pushed on if they // are not the type we are interested in. @@ -491,30 +491,31 @@ nsReturnRef nsWebMReader::NextPacket(TrackType aTrackType) // Value of other track PRUint32 otherTrack = aTrackType == VIDEO ? mAudioTrack : mVideoTrack; - nsAutoRef packet; + nsAutoRef 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(); + return nsReturnRef(); } - 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(); + return nsReturnRef(); } if (hasOtherType && otherTrack == track) { // Save the packet for when we want these packets - otherPackets.Push(packet.disown()); + otherPackets.Push(holder.disown()); continue; } @@ -525,7 +526,7 @@ nsReturnRef nsWebMReader::NextPacket(TrackType aTrackType) } while (PR_TRUE); } - return packet.out(); + return holder.out(); } PRBool nsWebMReader::DecodeAudioData() @@ -533,13 +534,13 @@ PRBool nsWebMReader::DecodeAudioData() MonitorAutoEnter mon(mMonitor); NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), "Should be on state machine thread or decode thread."); - nsAutoRef packet(NextPacket(AUDIO)); - if (!packet) { + nsAutoRef holder(NextPacket(AUDIO)); + if (!holder) { mAudioQueue.Finish(); return PR_FALSE; } - return DecodeAudioPacket(packet); + return DecodeAudioPacket(holder->mPacket, holder->mOffset); } PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip, @@ -549,12 +550,13 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip, NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(), "Should be on state machine or decode thread."); - nsAutoRef packet(NextPacket(VIDEO)); - if (!packet) { + nsAutoRef 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) { @@ -579,13 +581,13 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip, // video frame. uint64_t next_tstamp = 0; { - nsAutoRef next_packet(NextPacket(VIDEO)); - if (next_packet) { - r = nestegg_packet_tstamp(next_packet, &next_tstamp); + nsAutoRef 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()); @@ -661,7 +663,7 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip, VideoData *v = VideoData::Create(mInfo, mDecoder->GetImageContainer(), - -1, + holder->mOffset, tstamp_ms, next_tstamp / NS_PER_MS, b, diff --git a/content/media/webm/nsWebMReader.h b/content/media/webm/nsWebMReader.h index 7dfbf708f9d..56fe6459e61 100644 --- a/content/media/webm/nsWebMReader.h +++ b/content/media/webm/nsWebMReader.h @@ -54,10 +54,35 @@ 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(anObject)); + delete static_cast(anObject); return nsnull; } }; @@ -79,23 +104,23 @@ class PacketQueue : private nsDeque { 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(nsDeque::PopFront()); + inline NesteggPacketHolder* PopFront() { + return static_cast(nsDeque::PopFront()); } void Reset() { while (GetSize() > 0) { - nestegg_free_packet(PopFront()); + delete PopFront(); } } }; @@ -144,7 +169,7 @@ private: // 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 NextPacket(TrackType aTrackType); + nsReturnRef NextPacket(TrackType aTrackType); // Returns an initialized ogg packet with data obtained from the WebM container. ogg_packet InitOggPacket(unsigned char* aData, @@ -159,7 +184,7 @@ private: // 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.