Performance improvements to mov_reader

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13209 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
David McPaul
2005-06-19 06:02:57 +00:00
parent 5f8e88545e
commit 9658f6abba
6 changed files with 162 additions and 102 deletions
@@ -231,35 +231,39 @@ bool MOVFileReader::IsAudio(uint32 stream_index)
return false;
}
uint32 MOVFileReader::getFirstFrameInChunk(uint32 stream_index, uint32 pChunkID)
{
// Find Track
AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index);
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
return aTrakAtom->getFirstSampleInChunk(pChunkID);
}
return 0;
}
uint32 MOVFileReader::getNoFramesInChunk(uint32 stream_index, uint32 pFrameNo)
{
// Find Track
AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index);
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
uint32 ChunkNo = 1;
if (IsAudio(stream_index)) {
uint32 OffsetInChunk;
uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
// Go back to first Sample in Chunk
while (aTrakAtom->getChunkForSample(SampleNo-1, &OffsetInChunk) == ChunkNo) {
SampleNo--;
}
// Add up all sample sizes in chunk
while (aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk) == ChunkNo) {
SampleNo++;
}
return SampleNo ;
ChunkNo = pFrameNo;
}
if (IsVideo(stream_index)) {
return 1;
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
uint32 OffsetInChunk;
ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
}
return aTrakAtom->getNoSamplesInChunk(ChunkNo);
}
return 0;
@@ -267,54 +271,47 @@ uint32 MOVFileReader::getNoFramesInChunk(uint32 stream_index, uint32 pFrameNo)
uint64 MOVFileReader::getOffsetForFrame(uint32 stream_index, uint32 pFrameNo)
{
// BStopWatch *watch;
// Find Track
AtomBase *aAtomBase = GetChildAtom(uint32('trak'),stream_index);
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
if (pFrameNo < aTrakAtom->FrameCount()) {
// Convert Frame to Track Time
// uint32 FrameTime = aTrakAtom->getTimeForFrame(pFrameNo, getMovieTimeScale());
// Get Sample for Frame
// watch = new BStopWatch("getSampleForFrame");
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
// delete watch;
// Get Chunk For Sample and the offset for the frame within that chunk
// watch = new BStopWatch("getChunkForSample");
uint32 OffsetInChunk;
uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
// delete watch;
// Get Offset For Chunk
// printf("Stream: %ld Frame %ld %ld <%ld/%ld> (",stream_index, pFrameNo, SampleNo, ChunkNo, OffsetInChunk);
// watch = new BStopWatch("getOffsetForChunk");
uint64 OffsetNo = aTrakAtom->getOffsetForChunk(ChunkNo);
// delete watch;
// printf("Frame %ld {%lld} <%ld/%ld> (",pFrameNo, OffsetNo, ChunkNo, OffsetInChunk);
if (IsAudio(stream_index)) {
// FrameNo is really chunk No for audio
uint32 ChunkNo = pFrameNo;
if (ChunkNo != 0) {
uint32 SampleSize;
// Adjust the Offset for the Offset in the chunk
// watch = new BStopWatch("getSizeForSample");
if (aTrakAtom->IsSingleSampleSize()) {
SampleSize = aTrakAtom->getSizeForSample(SampleNo);
OffsetNo = OffsetNo + (OffsetInChunk * SampleSize);
} else {
// This is bad news performance wise
for (uint32 i=1;i<=OffsetInChunk;i++) {
SampleSize = aTrakAtom->getSizeForSample(SampleNo-i);
// printf(" %ld ",SampleSize);
OffsetNo = OffsetNo + SampleSize;
// Get Offset For Chunk
return aTrakAtom->getOffsetForChunk(ChunkNo);
}
if (IsVideo(stream_index)) {
if (pFrameNo < aTrakAtom->FrameCount()) {
// Get Sample for Frame
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
// Get Chunk For Sample and the offset for the frame within that chunk
uint32 OffsetInChunk;
uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
// Get Offset For Chunk
uint64 OffsetNo = aTrakAtom->getOffsetForChunk(ChunkNo);
if (ChunkNo != 0) {
uint32 SampleSize;
// Adjust the Offset for the Offset in the chunk
if (aTrakAtom->IsSingleSampleSize()) {
SampleSize = aTrakAtom->getSizeForSample(SampleNo);
OffsetNo = OffsetNo + (OffsetInChunk * SampleSize);
} else {
// This is bad news performance wise
for (uint32 i=1;i<=OffsetInChunk;i++) {
SampleSize = aTrakAtom->getSizeForSample(SampleNo-i);
OffsetNo = OffsetNo + SampleSize;
}
}
}
// printf(") %lld\n",OffsetNo);
// delete watch;
}
// printf("%ld:%ld:%ld:%lld\n",pFrameNo, SampleNo, ChunkNo, OffsetNo);
// printf("\n");
return OffsetNo;
return OffsetNo;
}
}
}
@@ -495,33 +492,32 @@ uint32 MOVFileReader::getChunkSize(uint32 stream_index, uint32 pFrameNo)
if (aAtomBase) {
TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
if (pFrameNo < aTrakAtom->FrameCount()) {
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
if (IsAudio(stream_index)) {
if (IsAudio(stream_index)) {
uint32 OffsetInChunk;
uint32 ChunkNo = aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk);
// Go back to first Sample in Chunk
while (aTrakAtom->getChunkForSample(SampleNo-1, &OffsetInChunk) == ChunkNo) {
SampleNo--;
}
// Add up all sample sizes in chunk
uint32 ChunkSize = 0;
// We read audio in chunk by chunk so chunk size is chunk size
uint32 ChunkNo = pFrameNo;
while (aTrakAtom->getChunkForSample(SampleNo, &OffsetInChunk) == ChunkNo) {
ChunkSize += aTrakAtom->getSizeForSample(SampleNo);
SampleNo++;
}
return ChunkSize;
// Get first sample in chunk
uint32 SampleNo = aTrakAtom->getFirstSampleInChunk(ChunkNo);
// Add up all sample sizes in chunk
uint32 ChunkSize = 0;
for (uint32 i=0;i<aTrakAtom->getNoSamplesInChunk(ChunkNo);i++) {
ChunkSize += aTrakAtom->getSizeForSample(SampleNo);
SampleNo++;
}
return ChunkSize;
}
if (IsVideo(stream_index)) {
if (IsVideo(stream_index)) {
if (pFrameNo < aTrakAtom->FrameCount()) {
// We read video in Sample by Sample so chunk size is Sample Size
uint32 SampleNo = aTrakAtom->getSampleForFrame(pFrameNo);
return aTrakAtom->getSizeForSample(SampleNo);
}
}
}
return 0;
@@ -550,9 +546,7 @@ bool MOVFileReader::GetNextChunkInfo(uint32 stream_index, uint32 pFrameNo, off_t
*keyframe = IsKeyFrame(stream_index, pFrameNo);
}
return ((*start > 0) && (*size > 0));
// return !(IsEndOfFile(*start) || *start < 0);
return ((*start > 0) && (*size > 0) && !(IsEndOfFile(*start)));
}
/* static */
@@ -93,6 +93,7 @@ public:
bool IsAudio(uint32 stream_index);
uint32 getNoFramesInChunk(uint32 stream_index, uint32 pFrameNo);
uint32 getFirstFrameInChunk(uint32 stream_index, uint32 pChunkID);
uint64 getOffsetForFrame(uint32 stream_index, uint32 pFrameNo);
uint32 getChunkSize(uint32 stream_index, uint32 pFrameNo);
@@ -528,6 +528,7 @@ SampleToChunk *aSampleToChunk;
}
theSampleToChunkArray[i] = aSampleToChunk;
// printf("%ld/%ld/%ld\n",aSampleToChunk->FirstChunk,aSampleToChunk->SamplesPerChunk,aSampleToChunk->TotalPrevFrames);
}
}
@@ -536,6 +537,30 @@ char *STSCAtom::OnGetAtomName()
return "Sample to Chunk Atom";
}
uint32 STSCAtom::getNoSamplesInChunk(uint32 pChunkID)
{
for (uint32 i=0;i<theHeader.NoEntries;i++) {
if (theSampleToChunkArray[i]->FirstChunk > pChunkID) {
return theSampleToChunkArray[i-1]->SamplesPerChunk;
}
}
return 0;
}
uint32 STSCAtom::getFirstSampleInChunk(uint32 pChunkID)
{
for (uint32 i=0;i<theHeader.NoEntries;i++) {
if (theSampleToChunkArray[i]->FirstChunk > pChunkID) {
uint32 Diff = pChunkID - theSampleToChunkArray[i-1]->FirstChunk;
uint32 pSampleNo = (Diff * theSampleToChunkArray[i-1]->SamplesPerChunk) + theSampleToChunkArray[i-1]->TotalPrevFrames;
return pSampleNo;
}
}
return 0;
}
uint32 STSCAtom::getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk)
{
uint32 ChunkID = 0;
@@ -186,6 +186,8 @@ public:
char *OnGetAtomName();
uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk);
uint32 getFirstSampleInChunk(uint32 pChunkID);
uint32 getNoSamplesInChunk(uint32 pChunkID);
private:
array_header theHeader;
SampleToChunkArray theSampleToChunkArray;
@@ -353,8 +355,10 @@ public:
uint32 getSampleForTime(uint32 pTime);
uint32 getSampleForFrame(uint32 pFrame);
uint32 getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk);
uint64 getOffsetForChunk(uint32 pChunk);
uint64 getOffsetForChunk(uint32 pChunkID);
uint32 getFirstSampleInChunk(uint32 pChunkID);
uint32 getSizeForSample(uint32 pSample);
uint32 getNoSamplesInChunk(uint32 pChunkID);
bool IsSyncSample(uint32 pSampleNo);
bool IsSingleSampleSize();
@@ -154,6 +154,17 @@ uint32 TRAKAtom::getChunkForSample(uint32 pSample, uint32 *pOffsetInChunk)
return 0;
}
uint32 TRAKAtom::getNoSamplesInChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0);
if (aAtomBase) {
return (dynamic_cast<STSCAtom *>(aAtomBase))->getNoSamplesInChunk(pChunkID);
}
return 0;
}
uint32 TRAKAtom::getSizeForSample(uint32 pSample)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsz'),0);
@@ -165,17 +176,29 @@ uint32 TRAKAtom::getSizeForSample(uint32 pSample)
return 0;
}
uint64 TRAKAtom::getOffsetForChunk(uint32 pChunk)
uint64 TRAKAtom::getOffsetForChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stco'),0);
if (aAtomBase) {
return (dynamic_cast<STCOAtom *>(aAtomBase))->getOffsetForChunk(pChunk);
return (dynamic_cast<STCOAtom *>(aAtomBase))->getOffsetForChunk(pChunkID);
}
return 0;
}
uint32 TRAKAtom::getFirstSampleInChunk(uint32 pChunkID)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stsc'),0);
if (aAtomBase) {
return (dynamic_cast<STSCAtom *>(aAtomBase))->getFirstSampleInChunk(pChunkID);
}
return 0;
}
bool TRAKAtom::IsSyncSample(uint32 pSampleNo)
{
AtomBase *aAtomBase = GetChildAtom(uint32('stss'),0);
@@ -54,16 +54,19 @@ struct mov_cookie
bool audio;
// audio only:
int64 byte_pos;
// audio only:
off_t byte_pos;
uint32 chunk_pos;
uint32 bytes_per_sec_rate;
uint32 bytes_per_sec_scale;
// video only:
uint32 line_count;
// Common
uint32 frame_pos;
uint32 frames_per_sec_rate;
uint32 frames_per_sec_scale;
uint32 line_count;
};
@@ -138,6 +141,7 @@ movReader::AllocateCookie(int32 streamNumber, void **_cookie)
cookie->stream = streamNumber;
cookie->buffer = 0;
cookie->buffer_size = 0;
cookie->frame_pos = 0;
BMediaFormats formats;
media_format *format = &cookie->format;
@@ -188,10 +192,10 @@ movReader::AllocateCookie(int32 streamNumber, void **_cookie)
*/
cookie->audio = true;
cookie->byte_pos = 0;
cookie->frame_pos = 0;
cookie->chunk_pos = 1;
if (stream_header->scale && stream_header->rate && stream_header->sample_size) {
cookie->bytes_per_sec_rate = stream_header->rate * stream_header->sample_size;
cookie->bytes_per_sec_rate = stream_header->rate * stream_header->sample_size * audio_format->NoOfChannels / 8;
cookie->bytes_per_sec_scale = stream_header->scale;
cookie->frames_per_sec_rate = stream_header->rate;
cookie->frames_per_sec_scale = stream_header->scale;
@@ -202,9 +206,11 @@ movReader::AllocateCookie(int32 streamNumber, void **_cookie)
cookie->frames_per_sec_rate = audio_format->SampleSize;
cookie->frames_per_sec_scale = 1;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using avg_bytes_per_sec)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
} else if (stream_header->rate) {
cookie->bytes_per_sec_rate = stream_header->rate;
} else if (stream_header->rate && stream_header->sample_size) {
cookie->bytes_per_sec_rate = stream_header->rate * stream_header->sample_size * audio_format->NoOfChannels / 8;
cookie->bytes_per_sec_scale = 1;
cookie->frames_per_sec_rate = stream_header->sample_size;
cookie->frames_per_sec_scale = 1;
TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
} else {
cookie->frames_per_sec_rate = 16000;
@@ -324,7 +330,6 @@ movReader::AllocateCookie(int32 streamNumber, void **_cookie)
}
cookie->audio = false;
cookie->frame_pos = 0;
cookie->line_count = theFileReader->MovMainHeader()->height;
if (stream_header->scale && stream_header->rate) {
@@ -458,8 +463,14 @@ movReader::GetNextChunk(void *_cookie,
mov_cookie *cookie = (mov_cookie *)_cookie;
int64 start; uint32 size; bool keyframe;
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe))
return B_LAST_BUFFER_ERROR;
if (cookie->audio) {
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->chunk_pos, &start, &size, &keyframe))
return B_LAST_BUFFER_ERROR;
} else {
if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe))
return B_LAST_BUFFER_ERROR;
}
if (cookie->buffer_size < size) {
delete [] cookie->buffer;
@@ -468,20 +479,22 @@ movReader::GetNextChunk(void *_cookie,
}
if (cookie->audio) {
TRACE("Audio stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
mediaHeader->start_time = (cookie->byte_pos * 1000000LL * (int64)cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
TRACE("Audio stream %d: chunk %ld expected start %lld Size %ld key %d\n",cookie->stream, cookie->chunk_pos, start, size, keyframe);
mediaHeader->type = B_MEDIA_ENCODED_AUDIO;
mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
// This will only work with raw audio I think.
mediaHeader->start_time = (cookie->byte_pos * 1000000L * cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using byte_pos\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->chunk_pos),mediaHeader->start_time);
// Should be frame_pos += frames per chunk
// We should find the current frame position (ie first frame in chunk) then compute using fps
cookie->frame_pos = theFileReader->getFirstFrameInChunk(cookie->stream,cookie->chunk_pos);
mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using frame_no %ld\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->chunk_pos),mediaHeader->start_time, cookie->frame_pos);
cookie->byte_pos += size;
if ((int64)cookie->frames_per_sec_scale > 0) {
cookie->frame_pos = ((mediaHeader->start_time * cookie->frames_per_sec_rate) / (int64)cookie->frames_per_sec_scale) / 1000000LL;
} else {
cookie->frame_pos = 0;
}
// cookie->frame_pos += theFileReader->getNoFramesInChunk(cookie->stream,cookie->frame_pos);
// cookie->frame_pos += 2205;
// frame_pos is chunk No for audio
cookie->chunk_pos++;
} else {
TRACE("Video stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
@@ -490,7 +503,7 @@ movReader::GetNextChunk(void *_cookie,
mediaHeader->u.encoded_video.first_active_line = 0;
mediaHeader->u.encoded_video.line_count = cookie->line_count;
cookie->frame_pos += 1;
cookie->frame_pos++;
}
TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);