seek and sync, it doesn't get much better than this

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6632 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
shatty
2004-02-18 13:10:28 +00:00
parent b797c2778b
commit dc51127a5d
6 changed files with 69 additions and 27 deletions
+31 -6
View File
@@ -51,6 +51,10 @@ OggSeekable::OggSeekable(long serialno)
ogg_stream_init(&fStreamState,serialno); ogg_stream_init(&fStreamState,serialno);
fPosition = 0; fPosition = 0;
fLastPagePosition = 0; fLastPagePosition = 0;
fCurrentFrame = 0;
fCurrentTime = 0;
fFirstGranulepos = 0;
fFrameRate = 0; fFrameRate = 0;
} }
@@ -267,6 +271,7 @@ OggSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
status_t status_t
OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time) OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
{ {
status_t status;
int64 granulepos; int64 granulepos;
if (seekTo & B_MEDIA_SEEK_TO_TIME) { if (seekTo & B_MEDIA_SEEK_TO_TIME) {
TRACE("OggSeekable::Seek: seek to time: %lld\n", *time); TRACE("OggSeekable::Seek: seek to time: %lld\n", *time);
@@ -286,7 +291,12 @@ OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
// find the first packet with data in it // find the first packet with data in it
ogg_page page; ogg_page page;
off_t left = 0; off_t left = 0;
Seek(0, SEEK_SET); BAutolock autolock(fPositionLock);
off_t pos = Seek(0, SEEK_SET);
if (pos < 0) {
TRACE("OggSeekable::Seek: Seek = %s\n", strerror(status));
return status;
}
uint header_packets_left = GetHeaderPackets().size(); uint header_packets_left = GetHeaderPackets().size();
while (header_packets_left > 0) { while (header_packets_left > 0) {
status_t status = ReadPage(&page, B_PAGE_SIZE); status_t status = ReadPage(&page, B_PAGE_SIZE);
@@ -297,6 +307,17 @@ OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
header_packets_left -= ogg_page_packets(&page); header_packets_left -= ogg_page_packets(&page);
left += page.header_len + page.body_len; left += page.header_len + page.body_len;
} }
TRACE("OggSeekable::Seek: header packets end after = %llu\n", left);
pos = Seek(left, SEEK_SET);
if (pos < 0) {
TRACE("OggSeekable::Seek: Seek2 = %s\n", strerror(status));
return status;
}
status = ReadPage(&page, B_PAGE_SIZE);
if (status != B_OK) {
TRACE("OggSeekable::Seek: ReadPage2 = %s\n", strerror(status));
return status;
}
if (ogg_page_continued(&page)) { if (ogg_page_continued(&page)) {
TRACE("OggSeekable::Seek: warning: data packet began in header page!\n"); TRACE("OggSeekable::Seek: warning: data packet began in header page!\n");
// how to handle this? // how to handle this?
@@ -318,7 +339,7 @@ OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
done = true; done = true;
} }
do { do {
status_t status = ReadPage(&page, B_PAGE_SIZE); status = ReadPage(&page, B_PAGE_SIZE);
if (status != B_OK) { if (status != B_OK) {
TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status)); TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status));
return status; return status;
@@ -350,7 +371,7 @@ OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
if (*frame != 0) { if (*frame != 0) {
ogg_packet packet; ogg_packet packet;
do { do {
status_t status = GetPacket(&packet); status = GetPacket(&packet);
if (status != B_OK) { if (status != B_OK) {
TRACE("OggSeekable::Seek: GetPacket = %s\n", strerror(status)); TRACE("OggSeekable::Seek: GetPacket = %s\n", strerror(status));
return status; return status;
@@ -423,11 +444,11 @@ OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
}*/ }*/
// the default chunk is an ogg packet
status_t status_t
OggSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, OggSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader) media_header *mediaHeader)
{ {
BAutolock autolock(fPositionLock);
mediaHeader->start_time = fCurrentTime; mediaHeader->start_time = fCurrentTime;
mediaHeader->file_pos = fPosition; mediaHeader->file_pos = fPosition;
status_t result = GetPacket(&fChunkPacket); status_t result = GetPacket(&fChunkPacket);
@@ -437,7 +458,11 @@ OggSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
} }
*chunkBuffer = &fChunkPacket; *chunkBuffer = &fChunkPacket;
*chunkSize = sizeof(fChunkPacket); *chunkSize = sizeof(fChunkPacket);
fCurrentFrame++; if (fChunkPacket.granulepos > 0) {
fCurrentFrame = fChunkPacket.granulepos - fFirstGranulepos;
} else {
fCurrentFrame++;
}
fCurrentTime = (bigtime_t)((fCurrentFrame * 1000000LL) / fFrameRate); fCurrentTime = (bigtime_t)((fCurrentFrame * 1000000LL) / fFrameRate);
return B_OK; return B_OK;
} }
@@ -185,12 +185,12 @@ status_t
OggSpeexSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, OggSpeexSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader) media_header *mediaHeader)
{ {
status_t result = GetPacket(&fChunkPacket); status_t result = inherited::GetNextChunk(chunkBuffer, chunkSize, mediaHeader);
if (result != B_OK) { if (result != B_OK) {
TRACE("OggSpeexSeekable::GetNextChunk failed: GetPacket = %s\n", strerror(result)); TRACE("OggSpeexSeekable::GetNextChunk failed: GetNextChunk = %s\n", strerror(result));
return result; return result;
} }
*chunkBuffer = fChunkPacket.packet; *chunkSize = ((ogg_packet*)*chunkBuffer)->bytes;
*chunkSize = fChunkPacket.bytes; *chunkBuffer = ((ogg_packet*)*chunkBuffer)->packet;
return B_OK; return B_OK;
} }
@@ -261,24 +261,21 @@ status_t
OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize, OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader) media_header *mediaHeader)
{ {
status_t result = GetPacket(&fChunkPacket); status_t result = inherited::GetNextChunk(chunkBuffer, chunkSize, mediaHeader);
if (result != B_OK) { if (result != B_OK) {
TRACE("OggTobiasSeekable::GetNextChunk failed: GetPacket = %s\n", strerror(result)); TRACE("OggTobiasSeekable::GetNextChunk failed: GetNextChunk = %s\n", strerror(result));
return result; return result;
} }
*chunkBuffer = fChunkPacket.packet; *chunkSize = ((ogg_packet*)*chunkBuffer)->bytes;
*chunkSize = fChunkPacket.bytes; *chunkBuffer = ((ogg_packet*)*chunkBuffer)->packet;
bool keyframe = fChunkPacket.packet[0] & (1 << 3); // ?? bool keyframe = ((uint*)chunkBuffer)[0] & (1 << 3); // ??
if (fMediaFormat.type == B_MEDIA_ENCODED_VIDEO) { if (fMediaFormat.type == B_MEDIA_ENCODED_VIDEO) {
mediaHeader->type = fMediaFormat.type; mediaHeader->type = fMediaFormat.type;
mediaHeader->start_time = fCurrentTime;
mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0); mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0);
mediaHeader->u.encoded_video.first_active_line mediaHeader->u.encoded_video.first_active_line
= fMediaFormat.u.encoded_video.output.first_active; = fMediaFormat.u.encoded_video.output.first_active;
mediaHeader->u.encoded_video.line_count mediaHeader->u.encoded_video.line_count
= fMediaFormat.u.encoded_video.output.display.line_count; = fMediaFormat.u.encoded_video.output.display.line_count;
} }
fCurrentFrame++;
fCurrentTime = (bigtime_t)(fCurrentFrame * fMicrosecPerFrame);
return B_OK; return B_OK;
} }
@@ -51,6 +51,7 @@ SpeexDecoder::SpeexDecoder()
fHeader = 0; fHeader = 0;
fStereoState = 0; fStereoState = 0;
fSpeexOutputLength = 0; fSpeexOutputLength = 0;
fStartTime = 0;
fFrameSize = 0; fFrameSize = 0;
fOutputBufferSize = 0; fOutputBufferSize = 0;
} }
@@ -249,8 +250,9 @@ SpeexDecoder::Decode(void *buffer, int64 *frameCount,
float * out_buffer = static_cast<float *>(buffer); float * out_buffer = static_cast<float *>(buffer);
int32 out_bytes_needed = fOutputBufferSize; int32 out_bytes_needed = fOutputBufferSize;
bool start = false; bool synced = false;
int total_samples = 0;
while (out_bytes_needed >= fSpeexOutputLength) { while (out_bytes_needed >= fSpeexOutputLength) {
// get a new packet // get a new packet
void *chunkBuffer; void *chunkBuffer;
@@ -264,9 +266,11 @@ SpeexDecoder::Decode(void *buffer, int64 *frameCount,
TRACE("SpeexDecoder::Decode: GetNextChunk failed\n"); TRACE("SpeexDecoder::Decode: GetNextChunk failed\n");
return status; return status;
} }
if (!start) { if (!synced) {
mediaHeader->start_time = mh.start_time; if (mh.start_time > 0) {
start = true; mediaHeader->start_time = mh.start_time - total_samples / fHeader->rate;
synced = true;
}
} }
speex_bits_read_from(&fBits, (char*)chunkBuffer, chunkSize); speex_bits_read_from(&fBits, (char*)chunkBuffer, chunkSize);
for (int frame = 0 ; frame < fHeader->frames_per_packet ; frame++) { for (int frame = 0 ; frame < fHeader->frames_per_packet ; frame++) {
@@ -290,10 +294,16 @@ SpeexDecoder::Decode(void *buffer, int64 *frameCount,
} }
out_buffer += fHeader->frame_size * fHeader->nb_channels; out_buffer += fHeader->frame_size * fHeader->nb_channels;
out_bytes_needed -= fSpeexOutputLength; out_bytes_needed -= fSpeexOutputLength;
total_samples += fHeader->frame_size;
} }
} }
done: done:
if (!synced) {
mediaHeader->start_time = fStartTime;
}
fStartTime = mediaHeader->start_time + total_samples / fHeader->rate;
*frameCount = (fOutputBufferSize - out_bytes_needed) / fFrameSize; *frameCount = (fOutputBufferSize - out_bytes_needed) / fFrameSize;
if (out_buffer != buffer) { if (out_buffer != buffer) {
@@ -46,7 +46,7 @@ VorbisDecoder::VorbisDecoder()
TRACE("VorbisDecoder::VorbisDecoder\n"); TRACE("VorbisDecoder::VorbisDecoder\n");
vorbis_info_init(&fInfo); vorbis_info_init(&fInfo);
vorbis_comment_init(&fComment); vorbis_comment_init(&fComment);
fStartTime = 0;
fFrameSize = 0; fFrameSize = 0;
fOutputBufferSize = 0; fOutputBufferSize = 0;
} }
@@ -170,8 +170,9 @@ VorbisDecoder::Decode(void *buffer, int64 *frameCount,
uint8 * out_buffer = static_cast<uint8 *>(buffer); uint8 * out_buffer = static_cast<uint8 *>(buffer);
int32 out_bytes_needed = fOutputBufferSize; int32 out_bytes_needed = fOutputBufferSize;
bool start = false; bool synced = false;
int total_samples = 0;
while (out_bytes_needed > 0) { while (out_bytes_needed > 0) {
int samples; int samples;
float **pcm; float **pcm;
@@ -192,9 +193,11 @@ VorbisDecoder::Decode(void *buffer, int64 *frameCount,
TRACE("VorbisDecoder::Decode: chunk not ogg_packet-sized\n"); TRACE("VorbisDecoder::Decode: chunk not ogg_packet-sized\n");
return B_ERROR; return B_ERROR;
} }
if (!start) { if (!synced) {
mediaHeader->start_time = mh.start_time; if (mh.start_time > 0) {
start = true; mediaHeader->start_time = mh.start_time - total_samples / fInfo.rate;
synced = true;
}
} }
ogg_packet * packet = static_cast<ogg_packet*>(chunkBuffer); ogg_packet * packet = static_cast<ogg_packet*>(chunkBuffer);
if (vorbis_synthesis(&fBlock,packet)==0) { if (vorbis_synthesis(&fBlock,packet)==0) {
@@ -203,6 +206,7 @@ VorbisDecoder::Decode(void *buffer, int64 *frameCount,
} }
// reduce samples to the amount of samples we will actually consume // reduce samples to the amount of samples we will actually consume
samples = min_c(samples,out_bytes_needed/fFrameSize); samples = min_c(samples,out_bytes_needed/fFrameSize);
total_samples += samples;
#if DECODE_AS_INT16 #if DECODE_AS_INT16
for (int sample = 0; sample < samples ; sample++) { for (int sample = 0; sample < samples ; sample++) {
for (int channel = 0; channel < fInfo.channels; channel++) { for (int channel = 0; channel < fInfo.channels; channel++) {
@@ -231,6 +235,11 @@ VorbisDecoder::Decode(void *buffer, int64 *frameCount,
} }
done: done:
if (!synced) {
mediaHeader->start_time = fStartTime;
}
fStartTime = mediaHeader->start_time + total_samples / fInfo.rate;
*frameCount = (fOutputBufferSize - out_bytes_needed) / fFrameSize; *frameCount = (fOutputBufferSize - out_bytes_needed) / fFrameSize;
if (out_buffer != buffer) { if (out_buffer != buffer) {
@@ -30,6 +30,7 @@ private:
vorbis_comment fComment; vorbis_comment fComment;
vorbis_dsp_state fDspState; vorbis_dsp_state fDspState;
vorbis_block fBlock; vorbis_block fBlock;
bigtime_t fStartTime;
int fFrameSize; int fFrameSize;
int fOutputBufferSize; int fOutputBufferSize;
}; };