seeking in ogg
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6626 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -51,6 +51,7 @@ OggSeekable::OggSeekable(long serialno)
|
|||||||
ogg_stream_init(&fStreamState,serialno);
|
ogg_stream_init(&fStreamState,serialno);
|
||||||
fPosition = 0;
|
fPosition = 0;
|
||||||
fLastPagePosition = 0;
|
fLastPagePosition = 0;
|
||||||
|
fFrameRate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -142,6 +143,12 @@ status_t
|
|||||||
OggSeekable::ReadPage(ogg_page * page, int read_size)
|
OggSeekable::ReadPage(ogg_page * page, int read_size)
|
||||||
{
|
{
|
||||||
// TRACE("OggSeekable::ReadPage (%llu)\n", fPosition);
|
// 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);
|
BAutolock autolock(fPositionLock);
|
||||||
// align to page boundary
|
// align to page boundary
|
||||||
int offset;
|
int offset;
|
||||||
@@ -257,38 +264,164 @@ OggSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
|
OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
|
||||||
{
|
{
|
||||||
if (seekTo == B_MEDIA_SEEK_TO_FRAME) {
|
int64 granulepos;
|
||||||
if (*frame != 0) {
|
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;
|
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) {
|
TRACE("OggSeekable::Seek: seek to granulepos: %lld\n", granulepos);
|
||||||
if (*time != 0) {
|
|
||||||
return B_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off_t result = Seek(0, SEEK_SET);
|
|
||||||
if (result < 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint i = 0 ; i < GetHeaderPackets().size() ; i++) {
|
// find the first packet with data in it
|
||||||
ogg_packet packet;
|
ogg_page page;
|
||||||
status_t status = GetPacket(&packet);
|
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) {
|
if (status != B_OK) {
|
||||||
|
TRACE("OggSeekable::Seek: ReadPage = %s\n", strerror(status));
|
||||||
return 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?
|
||||||
}
|
}
|
||||||
|
|
||||||
*frame = 0;
|
// binary search to find our place
|
||||||
*time = 0;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
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
|
// the default chunk is an ogg packet
|
||||||
status_t
|
status_t
|
||||||
@@ -313,22 +446,12 @@ OggSeekable::GetPacket(ogg_packet * packet)
|
|||||||
// at the end, pull the packet
|
// at the end, pull the packet
|
||||||
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
||||||
ogg_page page;
|
ogg_page page;
|
||||||
do {
|
|
||||||
int result = ogg_sync_pageout(&fSync, &page);
|
|
||||||
if (result == 0) {
|
|
||||||
status_t status = ReadPage(&page);
|
status_t status = ReadPage(&page);
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
TRACE("OggSeekable::GetPacket: GetNextPage = %s\n", strerror(status));
|
TRACE("OggSeekable::GetPacket: ReadPage = %s\n", strerror(status));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (result == -1) {
|
|
||||||
TRACE("OggSeekable::GetPacket: ogg_sync_pageout: not synced??\n");
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
} while (ogg_page_serialno(&page) != GetSerial());
|
|
||||||
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
|
if (ogg_stream_pagein(&fStreamState, &page) != 0) {
|
||||||
debugger("huh?");
|
|
||||||
TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n");
|
TRACE("OggSeekable::GetPacket: ogg_stream_pagein: failed??\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ protected:
|
|||||||
int64 fCurrentFrame;
|
int64 fCurrentFrame;
|
||||||
bigtime_t fCurrentTime;
|
bigtime_t fCurrentTime;
|
||||||
|
|
||||||
|
int64 fFirstGranulepos;
|
||||||
|
float fFrameRate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
off_t fPosition;
|
off_t fPosition;
|
||||||
std::vector<off_t> fPagePositions;
|
std::vector<off_t> fPagePositions;
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
|
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
|
||||||
fMediaFormat = *format;
|
fMediaFormat = *format;
|
||||||
fMicrosecPerFrame = header->time_unit / 10.0;
|
fMicrosecPerFrame = header->time_unit / 10.0;
|
||||||
|
fFrameRate = 1000000.0 / fMicrosecPerFrame;
|
||||||
|
|
||||||
// TODO: count the frames in the first page.. somehow.. :-/
|
// TODO: count the frames in the first page.. somehow.. :-/
|
||||||
int64 frames = 0;
|
int64 frames = 0;
|
||||||
@@ -226,13 +227,8 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int64 first_granulepos = ogg_page_granulepos(&page);
|
int64 fFirstGranulepos = ogg_page_granulepos(&page);
|
||||||
if (first_granulepos < 0) {
|
TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos);
|
||||||
// negative start granulepos indicates that we discard that many frames
|
|
||||||
frames -= first_granulepos;
|
|
||||||
first_granulepos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read our last page
|
// read our last page
|
||||||
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
|
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
|
||||||
if (last < 0) {
|
if (last < 0) {
|
||||||
@@ -253,25 +249,14 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compute frame count and duration from sample count
|
// compute frame count and duration from sample count
|
||||||
frames += last_granulepos - first_granulepos;
|
frames = last_granulepos - fFirstGranulepos;
|
||||||
|
|
||||||
*frameCount = frames;
|
*frameCount = frames;
|
||||||
*duration = (bigtime_t)(*frameCount * fMicrosecPerFrame);
|
*duration = (1000000LL * frames) / (long long)fFrameRate;
|
||||||
return B_OK;
|
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
|
status_t
|
||||||
OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
OggTobiasSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
||||||
media_header *mediaHeader)
|
media_header *mediaHeader)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public:
|
|||||||
|
|
||||||
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
virtual status_t GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
||||||
media_format *format);
|
media_format *format);
|
||||||
virtual status_t Seek(uint32 seekTo, int64 *frame, bigtime_t *time);
|
|
||||||
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
virtual status_t GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
||||||
media_header *mediaHeader);
|
media_header *mediaHeader);
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
} else {
|
} else {
|
||||||
format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
|
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.channel_count = info.channels;
|
||||||
format->u.encoded_audio.output.buffer_size
|
format->u.encoded_audio.output.buffer_size
|
||||||
= AudioBufferSize(&format->u.encoded_audio.output);
|
= AudioBufferSize(&format->u.encoded_audio.output);
|
||||||
@@ -199,15 +199,10 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
int64 first_granulepos = ogg_page_granulepos(&page);
|
int64 fFirstGranulepos = ogg_page_granulepos(&page);
|
||||||
if (first_granulepos < 0) {
|
TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos);
|
||||||
// negative start granulepos indicates that we discard that many frames
|
|
||||||
frames -= first_granulepos;
|
|
||||||
first_granulepos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read our last page
|
// read our last page
|
||||||
off_t last = Seek(GetLastPagePosition(), SEEK_SET);
|
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
|
||||||
if (last < 0) {
|
if (last < 0) {
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
@@ -226,9 +221,10 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compute frame count and duration from sample count
|
// compute frame count and duration from sample count
|
||||||
frames += last_granulepos - first_granulepos;
|
frames = last_granulepos - fFirstGranulepos;
|
||||||
|
|
||||||
*frameCount = frames;
|
*frameCount = frames;
|
||||||
*duration = (1000000LL * frames) / (long long)format->u.encoded_audio.output.frame_rate;
|
*duration = (1000000LL * frames) / (long long)fFrameRate;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
namespace BPrivate { namespace media {
|
namespace BPrivate { namespace media {
|
||||||
|
|
||||||
class OggVorbisSeekable : public OggSeekable {
|
class OggVorbisSeekable : public OggSeekable {
|
||||||
|
private:
|
||||||
|
typedef OggSeekable inherited;
|
||||||
public:
|
public:
|
||||||
static bool IsValidHeader(const ogg_packet & packet);
|
static bool IsValidHeader(const ogg_packet & packet);
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user