seeking in ogg

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6626 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
shatty
2004-02-18 09:46:13 +00:00
parent b95c3b6860
commit 3f71285861
6 changed files with 173 additions and 65 deletions
+155 -32
View File
@@ -51,6 +51,7 @@ OggSeekable::OggSeekable(long serialno)
ogg_stream_init(&fStreamState,serialno);
fPosition = 0;
fLastPagePosition = 0;
fFrameRate = 0;
}
@@ -142,6 +143,12 @@ status_t
OggSeekable::ReadPage(ogg_page * page, int read_size)
{
// TRACE("OggSeekable::ReadPage (%llu)\n", fPosition);
int result;
while ((result = ogg_sync_pageout(&fSync, page)) == 1) {
if (ogg_page_serialno(page) == GetSerial()) {
return B_OK;
}
}
BAutolock autolock(fPositionLock);
// align to page boundary
int offset;
@@ -257,38 +264,164 @@ OggSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
return B_OK;
}
status_t
OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
{
if (seekTo == B_MEDIA_SEEK_TO_FRAME) {
if (*frame != 0) {
int64 granulepos;
if (seekTo & B_MEDIA_SEEK_TO_TIME) {
TRACE("OggSeekable::Seek: seek to time: %lld\n", *time);
if ((fFrameRate == 0) && (*time != 0)) {
TRACE("OggSeekable::Seek: fFrameRate unset, seek to non-zero time unsupported\n");
return B_UNSUPPORTED;
}
granulepos = fFirstGranulepos + (int64) (*time * (long long)fFrameRate) / 1000000LL;
} else if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
granulepos = fFirstGranulepos + *frame;
} else {
TRACE("OggSeekable::Seek: bad seekTo");
return B_BAD_VALUE;
}
if (seekTo == B_MEDIA_SEEK_TO_TIME) {
if (*time != 0) {
return B_UNSUPPORTED;
}
}
off_t result = Seek(0, SEEK_SET);
if (result < 0) {
return result;
}
TRACE("OggSeekable::Seek: seek to granulepos: %lld\n", granulepos);
for(uint i = 0 ; i < GetHeaderPackets().size() ; i++) {
ogg_packet packet;
status_t status = GetPacket(&packet);
// find the first packet with data in it
ogg_page page;
off_t left = 0;
Seek(0, SEEK_SET);
uint header_packets_left = GetHeaderPackets().size();
while (header_packets_left > 0) {
status_t status = ReadPage(&page, B_PAGE_SIZE);
if (status != B_OK) {
TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status));
return status;
}
header_packets_left -= ogg_page_packets(&page);
left += page.header_len + page.body_len;
}
if (ogg_page_continued(&page)) {
TRACE("OggSeekable::Seek: warning: data packet began in header page!\n");
// how to handle this?
}
// binary search to find our place
int64 left_granulepos = 0;
off_t right = GetLastPagePosition();
int64 right_granulepos = 0;
bool done = false;
while (!done) {
TRACE(" Seek: [%llu,%llu]: ", left, right);
ogg_sync_reset(&fSync);
ogg_stream_reset(&fStreamState);
if (right - left > B_PAGE_SIZE) {
fPosition = (right + left) / 2;
} else {
fPosition = left;
done = true;
}
do {
status_t status = ReadPage(&page, B_PAGE_SIZE);
if (status != B_OK) {
TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status));
return status;
}
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
TRACE("OggSeekable::Seek: ogg_stream_pagein: failed??\n");
return B_ERROR;
}
} while (ogg_page_granulepos(&page) == -1);
TRACE("granulepos = %lld, ", ogg_page_granulepos(&page));
TRACE("fPosition = %llu\n", fPosition);
// check the granulepos of the page against the requested frame
if (ogg_page_granulepos(&page) < granulepos) {
// The packet that the frame is in is someplace after
// the last complete packet in this page. (It may yet
// be in this page, but in a packet completed on the
// next page.)
left = (right + left) / 2;
left_granulepos = ogg_page_granulepos(&page);
continue;
} else {
right = (right + left) / 2;
right_granulepos = ogg_page_granulepos(&page);
continue;
}
}
*frame = 0;
*time = 0;
// if not zero, look for a granulepos in a packet
if (*frame != 0) {
ogg_packet packet;
do {
status_t status = GetPacket(&packet);
if (status != B_OK) {
TRACE("OggSeekable::Seek: GetPacket = %s\n", strerror(status));
return status;
}
} while (packet.granulepos == -1);
granulepos = packet.granulepos;
fCurrentFrame = granulepos - fFirstGranulepos;
fCurrentTime = (1000000LL * fCurrentFrame) / (long long)fFrameRate;
} else {
fCurrentFrame = 0;
fCurrentTime = 0;
}
TRACE("OggSeekable::Seek done: ");
TRACE("[%lld,%lld] => ", left_granulepos, right_granulepos);
TRACE("found frame %lld at time %llu\n", fCurrentFrame, fCurrentTime);
*frame = fCurrentFrame;
*time = fCurrentTime;
return B_OK;
}
// The packet that the frame is in is someplace before or
// in the last complete packet in this page. (It may be in
// a prior page)
/* ogg_packet packet;
do {
switch (ogg_stream_packetpeek(&fStreamState, &packet)) {
case 1: // a packet found
break;
case 0: { // no packets found
status_t status = ReadPage(&page, B_PAGE_SIZE);
if (status != B_OK) {
TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status));
return status;
}
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
TRACE("OggSeekable::Seek: ogg_stream_pagein: failed??\n");
return B_ERROR;
}
continue;
default: // error condition
TRACE("OggSeekable::Seek: error peeking for packet\n");
continue;
}
if (packet.granulepos == -1) {
// useless packet with no seek info, eat it
ogg_stream_packetout(&fStreamState, &packet);
}
} while (packet.granulepos == -1);
if (packet.granulepos
*/
/* if (ogg_page_granulepos(&page) == *frame) {
// The frame is in the last packet to end on this page.
// If the last packet on this page was continued from
// the prior page, we might need to go back farther to
// get the whole packet.
if ((ogg_page_packets(&page) == 1) &&
(ogg_page_continued(&page)) &&
(ogg_stream_packetpeek(&fStreamState, NULL) != 1)) {
right = (right + left) / 2;
continue;
}
// The frame is the last frame in the last packet on
// this page. If there are more than 1 packet, discard
// all but the last and return.
for (int i = 0 ; i < packets - 1 ; i++) {
ogg_packet packet;
GetPacket(&packet);
}
return B_OK;
}*/
// the default chunk is an ogg packet
status_t
@@ -313,22 +446,12 @@ OggSeekable::GetPacket(ogg_packet * packet)
// at the end, pull the packet
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
ogg_page page;
do {
int result = ogg_sync_pageout(&fSync, &page);
if (result == 0) {
status_t status = ReadPage(&page);
if (status != B_OK) {
TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(status));
return status;
}
}
if (result == -1) {
TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n");
return B_ERROR;
}
} while (ogg_page_serialno(&page) != GetSerial());
status_t status = ReadPage(&page);
if (status != B_OK) {
TRACE("OggSeekable::GetPacket: ReadPage = %s\n", strerror(status));
return status;
}
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
debugger("huh?");
TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n");
return B_ERROR;
}
@@ -46,6 +46,9 @@ protected:
int64 fCurrentFrame;
bigtime_t fCurrentTime;
int64 fFirstGranulepos;
float fFrameRate;
private:
off_t fPosition;
std::vector<off_t> fPagePositions;
@@ -216,6 +216,7 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
fMediaFormat = *format;
fMicrosecPerFrame = header->time_unit / 10.0;
fFrameRate = 1000000.0 / fMicrosecPerFrame;
// TODO: count the frames in the first page.. somehow.. :-/
int64 frames = 0;
@@ -226,13 +227,8 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
if (result != B_OK) {
return result;
}
int64 first_granulepos = ogg_page_granulepos(&page);
if (first_granulepos < 0) {
// negative start granulepos indicates that we discard that many frames
frames -= first_granulepos;
first_granulepos = 0;
}
int64 fFirstGranulepos = ogg_page_granulepos(&page);
TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos);
// read our last page
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
if (last < 0) {
@@ -253,25 +249,14 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
}
// compute frame count and duration from sample count
frames += last_granulepos - first_granulepos;
frames = last_granulepos - fFirstGranulepos;
*frameCount = frames;
*duration = (bigtime_t)(*frameCount * fMicrosecPerFrame);
*duration = (1000000LL * frames) / (long long)fFrameRate;
return B_OK;
}
status_t
OggTobiasSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
{
status_t status = inherited::Seek(seekTo, frame, time);
if (status == B_OK) {
fCurrentFrame = *frame;
fCurrentTime = *time;
}
return status;
}
status_t
OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader)
@@ -16,7 +16,6 @@ public:
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
media_format *format);
virtual status_t Seek(uint32 seekTo, int64 *frame, bigtime_t *time);
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
media_header *mediaHeader);
@@ -165,7 +165,7 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
} else {
format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
}
format->u.encoded_audio.output.frame_rate = (float)info.rate;
fFrameRate = format->u.encoded_audio.output.frame_rate = (float)info.rate;
format->u.encoded_audio.output.channel_count = info.channels;
format->u.encoded_audio.output.buffer_size
= AudioBufferSize(&format->u.encoded_audio.output);
@@ -199,15 +199,10 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
if (result != B_OK) {
return result;
}
int64 first_granulepos = ogg_page_granulepos(&page);
if (first_granulepos < 0) {
// negative start granulepos indicates that we discard that many frames
frames -= first_granulepos;
first_granulepos = 0;
}
int64 fFirstGranulepos = ogg_page_granulepos(&page);
TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos);
// read our last page
off_t last = Seek(GetLastPagePosition(), SEEK_SET);
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
if (last < 0) {
return last;
}
@@ -226,9 +221,10 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
}
// compute frame count and duration from sample count
frames += last_granulepos - first_granulepos;
frames = last_granulepos - fFirstGranulepos;
*frameCount = frames;
*duration = (1000000LL * frames) / (long long)format->u.encoded_audio.output.frame_rate;
*duration = (1000000LL * frames) / (long long)fFrameRate;
return B_OK;
}
@@ -6,6 +6,8 @@
namespace BPrivate { namespace media {
class OggVorbisSeekable : public OggSeekable {
private:
typedef OggSeekable inherited;
public:
static bool IsValidHeader(const ogg_packet & packet);
public: