Bug 914479 - Improve MP3FrameParser duration estimation r=padenot

This commit is contained in:
Edwin Flores 2013-09-14 13:14:42 +12:00
Родитель 8cb8eb6d22
Коммит a0afa44085
2 изменённых файлов: 46 добавлений и 13 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include <algorithm>
#include "nsMemory.h"
#include "MP3FrameParser.h"
#include "VideoUtils.h"
namespace mozilla {
@ -84,6 +85,7 @@ public:
mDurationUs(0),
mNumFrames(0),
mBitRateSum(0),
mSampleRate(0),
mFrameSizeSum(0)
{
MOZ_ASSERT(mBuffer || !mLength);
@ -103,6 +105,10 @@ public:
return mBitRateSum;
}
int16_t GetSampleRate() const {
return mSampleRate;
}
int64_t GetFrameSizeSum() const {
return mFrameSizeSum;
}
@ -133,6 +139,7 @@ private:
static nsresult DecodeFrameHeader(const uint8_t* aBuffer,
uint32_t* aFrameSize,
uint32_t* aBitRate,
uint16_t* aSampleRate,
uint64_t* aDuration);
static const uint16_t sBitRate[16];
@ -150,6 +157,9 @@ private:
// The sum of all frame's bit rates.
int64_t mBitRateSum;
// The number of audio samples per second
int16_t mSampleRate;
// The sum of all frame's sizes in byte.
int32_t mFrameSizeSum;
};
@ -207,6 +217,7 @@ uint32_t MP3Buffer::ExtractFrameHeader(const uint8_t* aBuffer)
nsresult MP3Buffer::DecodeFrameHeader(const uint8_t* aBuffer,
uint32_t* aFrameSize,
uint32_t* aBitRate,
uint16_t* aSampleRate,
uint64_t* aDuration)
{
uint32_t header = ExtractFrameHeader(aBuffer);
@ -230,6 +241,9 @@ nsresult MP3Buffer::DecodeFrameHeader(const uint8_t* aBuffer,
MOZ_ASSERT(aDuration);
*aDuration = (uint64_t(MP3_DURATION_CONST) * frameSize) / bitRate;
MOZ_ASSERT(aSampleRate);
*aSampleRate = sampleRate;
return NS_OK;
}
@ -246,9 +260,11 @@ nsresult MP3Buffer::Parse()
uint32_t frameSize;
uint32_t bitRate;
uint16_t sampleRate;
uint64_t duration;
nsresult rv = DecodeFrameHeader(buffer, &frameSize, &bitRate, &duration);
nsresult rv = DecodeFrameHeader(buffer, &frameSize, &bitRate,
&sampleRate, &duration);
if (NS_FAILED(rv)) {
return rv;
}
@ -259,6 +275,8 @@ nsresult MP3Buffer::Parse()
mFrameSizeSum += frameSize;
mSampleRate = sampleRate;
if (frameSize <= length) {
length -= frameSize;
} else {
@ -276,17 +294,24 @@ nsresult MP3Buffer::Parse()
// we're not parsing an MP3 stream.
static const uint32_t MAX_SKIPPED_BYTES = 200 * 1024;
// The number of audio samples per MP3 frame. This is constant over all MP3
// streams. With this constant, the stream's sample rate, and an estimated
// number of frames in the stream, we can estimate the stream's duration
// fairly accurately.
static const uint32_t SAMPLES_PER_FRAME = 1152;
MP3FrameParser::MP3FrameParser(int64_t aLength)
: mBufferLength(0),
mLock("MP3FrameParser.mLock"),
mDurationUs(0),
mBitRateSum(0),
mTotalFrameSize(0),
mNumFrames(0),
mOffset(0),
mUnhandled(0),
mLength(aLength),
mMP3Offset(-1),
mSkippedBytes(0),
mSampleRate(0),
mIsMP3(MAYBE_MP3)
{ }
@ -329,6 +354,8 @@ nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer,
}
mDurationUs += mp3Buffer.GetDuration();
mBitRateSum += mp3Buffer.GetBitRateSum();
mTotalFrameSize += mp3Buffer.GetFrameSizeSum();
mSampleRate = mp3Buffer.GetSampleRate();
mNumFrames += mp3Buffer.GetNumberOfFrames();
bufferOffset += mp3Buffer.GetFrameSizeSum();
} else {
@ -368,7 +395,7 @@ void MP3FrameParser::Parse(const char* aBuffer, uint32_t aLength, int64_t aOffse
aOffset = lastChunkEnd;
} else if (aOffset > lastChunkEnd) {
// Fragment comes after current position, store difference.
mUnhandled += aOffset - lastChunkEnd;
mOffset += aOffset - lastChunkEnd;
mSkippedBytes = 0;
}
@ -428,15 +455,18 @@ int64_t MP3FrameParser::GetDuration()
return -1; // Not a single frame decoded yet
}
// Compute the duration of the unhandled fragments from
// the average bitrate.
int64_t avgBitRate = mBitRateSum / mNumFrames;
NS_ENSURE_TRUE(avgBitRate > 0, mDurationUs);
// Estimate the total number of frames in the file from the average frame
// size we've seen so far, and the length of the file.
double avgFrameSize = (double)mTotalFrameSize / mNumFrames;
MOZ_ASSERT(mLength >= mOffset);
int64_t unhandled = mUnhandled + (mLength-mOffset);
// Need to cut out the header here. Ignore everything up to the first MP3
// frames.
double estimatedFrames = (double)(mLength - mMP3Offset) / avgFrameSize;
return mDurationUs + (uint64_t(MP3Buffer::MP3_DURATION_CONST) * unhandled) / avgBitRate;
// The duration of each frame is constant over a given stream.
double usPerFrame = USECS_PER_S * SAMPLES_PER_FRAME / mSampleRate;
return estimatedFrames * usPerFrame;
}
int64_t MP3FrameParser::GetMP3Offset()

Просмотреть файл

@ -79,6 +79,7 @@ private:
// All fields below are protected by mLock
uint64_t mDurationUs;
uint64_t mBitRateSum;
uint64_t mTotalFrameSize;
uint64_t mNumFrames;
// Offset of the last data parsed. This is the end offset of the last data
@ -86,10 +87,9 @@ private:
// call to Parse().
int64_t mOffset;
// Count of the number of bytes that the parser hasn't seen so far. This
// happens when the stream seeks.
int64_t mUnhandled;
// Total length of the stream in bytes.
int64_t mLength;
// Offset of first MP3 frame in the bitstream. Has value -1 until the
// first MP3 frame is found.
int64_t mMP3Offset;
@ -99,6 +99,9 @@ private:
// the stream either isn't MP3, or is corrupt.
uint32_t mSkippedBytes;
// Number of audio samples per second. Fixed through the whole file.
uint16_t mSampleRate;
enum eIsMP3 {
MAYBE_MP3, // We're giving the stream the benefit of the doubt...
DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame.