зеркало из https://github.com/mozilla/gecko-dev.git
Bug 914479 - Improve MP3FrameParser duration estimation r=padenot
This commit is contained in:
Родитель
8cb8eb6d22
Коммит
a0afa44085
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче