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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user