Bug 578536 - Ensure WebMReader properly tags its decoded samples with file offsets. r=doublec a=blocking2.0

This commit is contained in:
Chris Pearce 2010-11-29 09:06:38 +13:00
parent 398df2694e
commit ad76fe42d0
2 changed files with 59 additions and 32 deletions

View File

@ -67,10 +67,10 @@ 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
@ -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<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.
@ -491,30 +491,31 @@ nsReturnRef<nestegg_packet> nsWebMReader::NextPacket(TrackType aTrackType)
// 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;
}
@ -525,7 +526,7 @@ nsReturnRef<nestegg_packet> 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<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,
@ -549,12 +550,13 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
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) {
@ -579,13 +581,13 @@ PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
// 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());
@ -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,

View File

@ -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<nestegg_packet*>(anObject));
delete static_cast<NesteggPacketHolder*>(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<nestegg_packet*>(nsDeque::PopFront());
inline NesteggPacketHolder* PopFront() {
return static_cast<NesteggPacketHolder*>(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<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,
@ -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.