speedy duration computation, seek to zero, still missing the samples from the initial packet though?

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6551 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
shatty
2004-02-10 11:01:47 +00:00
parent a0bf290624
commit e23d6a15a6
8 changed files with 336 additions and 21 deletions
@@ -89,7 +89,8 @@ retry:
return B_ERROR;
}
long serialno = ogg_page_serialno(&page);
if (fTracks.find(serialno) == fTracks.end()) {
bool new_serialno = fTracks.find(serialno) == fTracks.end();
if (new_serialno) {
// this is an unknown serialno
if (ogg_page_bos(&page) == 0) {
TRACE("OggReader::GetPage: non-bos page with unknown serialno\n");
@@ -140,11 +141,14 @@ retry:
}
fCookies.push_back(serialno);
}
status_t status = fTracks[serialno]->AddPage(fPosition, page);
if (status != B_OK) {
return status;
// this check ensures that we only push the initial pages into OggSeekables.
if (!fSeekable || new_serialno) {
status_t status = fTracks[serialno]->AddPage(fPosition, page);
if (status != B_OK) {
return status;
}
fPosition += page.header_len + page.body_len;
}
fPosition += page.header_len + page.body_len;
return page.header_len + page.body_len;
}
@@ -183,6 +187,7 @@ get_seekable(BDataIO * data)
return seekable;
}
static BFile *
get_file(BDataIO * data)
{
@@ -198,6 +203,7 @@ get_file(BDataIO * data)
return file;
}
status_t
OggReader::Sniff(int32 *streamCount)
{
@@ -221,6 +227,12 @@ OggReader::Sniff(int32 *streamCount)
return bytes;
}
}
if (fSeekable) {
status_t status = FindLastPages();
if (status != B_OK) {
return status;
}
}
*streamCount = fCookies.size();
return B_OK;
}
@@ -301,6 +313,81 @@ OggReader::GetNextChunk(void *cookie,
}
status_t
OggReader::FindLastPages()
{
TRACE("OggReader::FindLastPages\n");
status_t result = B_ERROR;
const int read_size = 256*256;
ogg_page page;
ogg_sync_state sync;
ogg_sync_init(&sync);
off_t right = fSeekable->Seek(0, SEEK_END);
off_t left = right;
// we assume the common case is that the last pages are near the end
uint serial_count = 0;
while (serial_count < fCookies.size()) {
int offset;
while ((offset = ogg_sync_pageseek(&sync, &page)) <= 0) {
left += -offset;
if (offset == 0) {
if (fSeekable->Position() >= right) {
if (left == 0) {
TRACE("OggReader::FindLastPages: couldn't find some stream's page!!!\n");
goto done;
}
left = max_c(0, left - read_size);
result = fSeekable->Seek(left, SEEK_SET);
if (result < 0) {
goto done;
}
ogg_sync_reset(&sync);
continue;
}
char * buffer = ogg_sync_buffer(&sync, read_size);
ssize_t bytes = fSeekable->Read(buffer, read_size);
if (bytes < 0) {
TRACE("OggReader::FindLastPages: Read: error\n");
result = bytes;
goto done;
}
if (ogg_sync_wrote(&sync, bytes) != 0) {
TRACE("OggReader::FindLastPages: ogg_sync_wrote failed?: error\n");
goto done;
}
}
}
off_t current = left;
do {
// found a page at "current"
long serialno = ogg_page_serialno(&page);
OggSeekable * track = dynamic_cast<OggSeekable*>(fTracks[serialno]);
if (track == 0) {
TRACE("OggReader::FindLastPages: unknown serialno == TODO: chaining?\n");
} else {
if (track->GetLastPagePosition() == 0) {
serial_count++;
}
track->SetLastPagePosition(current);
}
current += page.header_len + page.body_len;
} while ((current < right) && (ogg_sync_pageout(&sync, &page) == 1));
right = left;
ogg_sync_reset(&sync);
}
result = B_OK;
done:
ogg_sync_clear(&sync);
return result;
}
/*
* OggReaderPlugin
*/
Reader *
OggReaderPlugin::NewReader()
{
@@ -36,6 +36,8 @@ public:
media_header *mediaHeader);
private:
status_t FindLastPages();
ogg_sync_state fSync;
BLocker fSyncLock;
serialno_OggTrack_map fTracks;
+92 -8
View File
@@ -49,6 +49,8 @@ OggSeekable::OggSeekable(long serialno)
TRACE("OggSeekable::OggSeekable\n");
ogg_sync_init(&fSync);
ogg_stream_init(&fStreamState,serialno);
fPosition = 0;
fLastPagePosition = 0;
}
@@ -86,6 +88,56 @@ OggSeekable::AddPage(off_t position, const ogg_page & page)
}
void
OggSeekable::SetLastPagePosition(off_t position)
{
fLastPagePosition = max_c(fLastPagePosition, position);
}
off_t
OggSeekable::GetLastPagePosition()
{
return fLastPagePosition;
}
off_t
OggSeekable::Seek(off_t position, int32 mode)
{
BAutolock autolock(fPositionLock);
off_t result = fPositionIO->Seek(position, mode);
if (result >= 0) {
fPosition = result;
ogg_sync_reset(&fSync);
ogg_stream_reset(&fStreamState);
}
return result;
}
off_t
OggSeekable::Position(void) const
{
return fPosition;
}
status_t
OggSeekable::GetSize(off_t * size)
{
BAutolock autolock(fPositionLock);
off_t prior = fPositionIO->Position();
off_t result = fPositionIO->Seek(0, SEEK_END);
fPositionIO->Seek(prior, SEEK_SET);
if (result >= 0) {
*size = result;
result = B_OK;
}
return result;
}
status_t
OggSeekable::ReadPage(ogg_page * page, int read_size)
{
@@ -98,16 +150,16 @@ OggSeekable::ReadPage(ogg_page * page, int read_size)
char * buffer = ogg_sync_buffer(&fSync, read_size);
ssize_t bytes = fPositionIO->ReadAt(fPosition, buffer, read_size);
if (bytes == 0) {
TRACE("OggReader::ReadPageAt: ReadAt: no data\n");
TRACE("OggSeekable::ReadPage: ReadAt: no data\n");
return B_LAST_BUFFER_ERROR;
}
if (bytes < 0) {
TRACE("OggReader::ReadPageAt: ReadAt: error\n");
TRACE("OggSeekable::ReadPage: ReadAt: error\n");
return bytes;
}
fPosition += bytes;
if (ogg_sync_wrote(&fSync, bytes) != 0) {
TRACE("OggReader::ReadPageAt: ogg_sync_wrote failed?: error\n");
TRACE("OggSeekable::ReadPage: ogg_sync_wrote failed?: error\n");
return B_ERROR;
}
}
@@ -120,25 +172,25 @@ OggSeekable::ReadPage(ogg_page * page, int read_size)
char * buffer = ogg_sync_buffer(&fSync, read_size);
ssize_t bytes = fPositionIO->ReadAt(fPosition, buffer, read_size);
if (bytes == 0) {
TRACE("OggReader::ReadPageAt: ReadAt 2: no data\n");
TRACE("OggSeekable::ReadPage: ReadAt 2: no data\n");
return B_LAST_BUFFER_ERROR;
}
if (bytes < 0) {
TRACE("OggReader::ReadPageAt: ReadAt 2: error\n");
TRACE("OggSeekable::ReadPage: ReadAt 2: error\n");
return bytes;
}
fPosition += bytes;
if (ogg_sync_wrote(&fSync, bytes) != 0) {
TRACE("OggReader::ReadPageAt: ogg_sync_wrote 2 failed?: error\n");
TRACE("OggSeekable::ReadPage: ogg_sync_wrote 2 failed?: error\n");
return B_ERROR;
}
}
if (result == -1) {
TRACE("OggReader::ReadPageAt: ogg_sync_pageout: not synced??\n");
TRACE("OggSeekable::ReadPage: ogg_sync_pageout: not synced??\n");
return B_ERROR;
}
if (ogg_page_version(page) != 0) {
TRACE("OggReader::GetPageAt: ogg_page_version: error in page encoding??\n");
TRACE("OggSeekable::ReadPage: ogg_page_version: error in page encoding??\n");
#ifdef STRICT_OGG
return B_ERROR;
#endif
@@ -197,6 +249,38 @@ OggSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
}
status_t
OggSeekable::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
{
if (seekTo == B_MEDIA_SEEK_TO_FRAME) {
if (*frame != 0) {
return B_UNSUPPORTED;
}
}
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;
}
for(uint i = 0 ; i < GetHeaderPackets().size() ; i++) {
ogg_packet packet;
status_t status = GetPacket(&packet);
if (status != B_OK) {
return status;
}
}
*frame = 0;
*time = 0;
return B_OK;
}
// the default chunk is an ogg packet
status_t
OggSeekable::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
@@ -15,6 +15,7 @@ public:
// interface for OggReader
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);
@@ -26,7 +27,15 @@ public:
// reader push input function
status_t AddPage(off_t position, const ogg_page & page);
void SetLastPagePosition(off_t position);
off_t GetLastPagePosition();
private:
off_t fLastPagePosition;
protected:
off_t Seek(off_t position, int32 mode);
off_t Position(void) const;
status_t GetSize(off_t * size);
status_t ReadPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE);
// subclass pull input function
@@ -140,8 +140,53 @@ OggSpeexSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
}
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
*duration = 100000000;
*frameCount = 60000;
// TODO: count the frames in the first page.. somehow.. :-/
int64 frames = 0;
// seek back to the start
int64 frame = 0;
bigtime_t time = 0;
result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time);
if (result != B_OK) {
return result;
}
ogg_page page;
// read the first page
result = ReadPage(&page);
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;
}
// read our last page
off_t last = Seek(GetLastPagePosition(), SEEK_SET);
if (last < 0) {
return last;
}
result = ReadPage(&page);
if (result != B_OK) {
return result;
}
int64 last_granulepos = ogg_page_granulepos(&page);
// seek back to the start
result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time);
if (result != B_OK) {
return result;
}
// compute frame count and duration from sample count
frames += last_granulepos - first_granulepos;
*frameCount = frames;
*duration = (1000000LL * frames) / (long long)format->u.encoded_audio.output.frame_rate;
return B_OK;
}
@@ -188,19 +188,16 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
if (result != B_OK) {
return result;
}
*frameCount = (bigtime_t)(3 * 3600 * format->u.encoded_video.output.field_rate);
} else if (strcmp(header->streamtype, "audio") == 0) {
result = get_audio_format(header, format);
if (result != B_OK) {
return result;
}
*frameCount = 2000000;
} else if (strcmp(header->streamtype, "text") == 0) {
result = get_text_format(header, format);
if (result != B_OK) {
return result;
}
*frameCount = 2000000;
} else {
*frameCount = 0;
// unknown streamtype
@@ -219,11 +216,62 @@ OggTobiasSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
fMediaFormat = *format;
fMicrosecPerFrame = header->time_unit / 10.0;
// TODO: count the frames in the first page.. somehow.. :-/
int64 frames = 0;
ogg_page page;
// read the first page
result = ReadPage(&page);
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;
}
// read our last page
off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET);
if (last < 0) {
return last;
}
result = ReadPage(&page);
if (result != B_OK) {
return result;
}
int64 last_granulepos = ogg_page_granulepos(&page);
// seek back to the start
int64 frame = 0;
bigtime_t time = 0;
result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time);
if (result != B_OK) {
return result;
}
// compute frame count and duration from sample count
frames += last_granulepos - first_granulepos;
*frameCount = frames;
*duration = (bigtime_t)(*frameCount * fMicrosecPerFrame);
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)
@@ -6,6 +6,8 @@
namespace BPrivate { namespace media {
class OggTobiasSeekable : public OggSeekable {
private:
typedef OggSeekable inherited;
public:
static bool IsValidHeader(const ogg_packet & packet);
public:
@@ -14,6 +16,7 @@ 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);
@@ -48,6 +48,7 @@ typedef struct vorbis_info{
void *codec_setup;
} vorbis_info;
// based on libvorbis/info.c _vorbis_unpack_info
static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){
vi->version = oggpack_read(opb, 32);
@@ -79,6 +80,7 @@ static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){
return 0;
}
/*
* OggVorbisSeekable implementations
*/
@@ -188,10 +190,45 @@ OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets()));
// TODO: count the frames in the first page.. somehow.. :-/
int64 frames = 0;
ogg_page page;
// read the first page
result = ReadPage(&page);
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;
}
// read our last page
off_t last = Seek(GetLastPagePosition(), SEEK_SET);
if (last < 0) {
return last;
}
result = ReadPage(&page);
if (result != B_OK) {
return result;
}
int64 last_granulepos = ogg_page_granulepos(&page);
// seek back to the start
int64 frame = 0;
bigtime_t time = 0;
result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time);
if (result != B_OK) {
return result;
}
// compute frame count and duration from sample count
int64 samples = 1000000;
*frameCount = samples;
*duration = (1000000LL * samples) / (long long)format->u.encoded_audio.output.frame_rate;
frames += last_granulepos - first_granulepos;
*frameCount = frames;
*duration = (1000000LL * frames) / (long long)format->u.encoded_audio.output.frame_rate;
return B_OK;
}