ogg pseudo-seeking
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6266 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -5,6 +5,7 @@ UsePrivateHeaders media ;
|
|||||||
SubDirHdrs [ FDirName $(SUBDIR) libogg ] ;
|
SubDirHdrs [ FDirName $(SUBDIR) libogg ] ;
|
||||||
|
|
||||||
Addon ogg : media plugins :
|
Addon ogg : media plugins :
|
||||||
|
OggFrameInfo.cpp
|
||||||
OggReaderPlugin.cpp
|
OggReaderPlugin.cpp
|
||||||
OggStream.cpp
|
OggStream.cpp
|
||||||
OggSpeexStream.cpp
|
OggSpeexStream.cpp
|
||||||
|
|||||||
@@ -130,6 +130,10 @@ retry:
|
|||||||
} while (ogg_page_serialno(&next_page) != serialno);
|
} while (ogg_page_serialno(&next_page) != serialno);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
virtual status_t GetPageAt(off_t position, ogg_stream_state * stream,
|
||||||
|
int read_size = 4*B_PAGE_SIZE) {
|
||||||
|
return reader->GetPageAt(position,stream,read_size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
fStreams[serialno] = OggStream::makeOggStream(new Interface(this, serialno), serialno, packet);
|
fStreams[serialno] = OggStream::makeOggStream(new Interface(this, serialno), serialno, packet);
|
||||||
fCookies.push_back(fStreams[serialno]);
|
fCookies.push_back(fStreams[serialno]);
|
||||||
@@ -138,6 +142,51 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
OggReader::GetPageAt(off_t position, ogg_stream_state * stream, int read_size)
|
||||||
|
{
|
||||||
|
if (!fSeekable) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
ogg_sync_state sync;
|
||||||
|
ogg_sync_init(&sync);
|
||||||
|
ogg_page page;
|
||||||
|
int result;
|
||||||
|
while ((result = ogg_sync_pageout(&sync,&page)) == 0) {
|
||||||
|
char * buffer = ogg_sync_buffer(&sync,read_size);
|
||||||
|
ssize_t bytes = fSeekable->ReadAt(position,buffer,read_size);
|
||||||
|
position += read_size;
|
||||||
|
if (bytes == 0) {
|
||||||
|
TRACE("OggReader::GetPage: Read: no data\n");
|
||||||
|
return B_LAST_BUFFER_ERROR;
|
||||||
|
}
|
||||||
|
if (bytes < 0) {
|
||||||
|
TRACE("OggReader::GetPage: Read: error\n");
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
if (ogg_sync_wrote(&sync,bytes) != 0) {
|
||||||
|
TRACE("OggReader::GetPage: ogg_sync_wrote failed?: error\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == -1) {
|
||||||
|
TRACE("OggReader::GetPageAt: ogg_sync_pageout: not synced??\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
#ifdef STRICT_OGG
|
||||||
|
if (ogg_page_version(page) != 0) {
|
||||||
|
TRACE("OggReader::GetPageAt: ogg_page_version: error in page encoding??\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (ogg_stream_pagein(stream, &page) != 0) {
|
||||||
|
TRACE("oggReader::GetPageAt: ogg_stream_pagein: failed??\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
ogg_sync_clear(&sync);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static BPositionIO *
|
static BPositionIO *
|
||||||
get_seekable(BDataIO * data)
|
get_seekable(BDataIO * data)
|
||||||
{
|
{
|
||||||
@@ -283,7 +332,7 @@ OggReader::GetNextChunk(void *cookie,
|
|||||||
void **chunkBuffer, int32 *chunkSize,
|
void **chunkBuffer, int32 *chunkSize,
|
||||||
media_header *mediaHeader)
|
media_header *mediaHeader)
|
||||||
{
|
{
|
||||||
TRACE("OggReader::GetNextChunk\n");
|
// TRACE("OggReader::GetNextChunk\n");
|
||||||
OggStream * stream = static_cast<OggStream*>(cookie);
|
OggStream * stream = static_cast<OggStream*>(cookie);
|
||||||
return stream->GetNextChunk(chunkBuffer,chunkSize,mediaHeader);
|
return stream->GetNextChunk(chunkBuffer,chunkSize,mediaHeader);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,11 @@ public:
|
|||||||
media_header *mediaHeader);
|
media_header *mediaHeader);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// called by OggStream through GetPageInterface
|
||||||
status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE,
|
status_t GetPage(ogg_page * page, int read_size = 4*B_PAGE_SIZE,
|
||||||
bool short_page = false);
|
bool short_page = false);
|
||||||
|
status_t GetPageAt(off_t position, ogg_stream_state * stream,
|
||||||
|
int read_size = 4*B_PAGE_SIZE);
|
||||||
|
|
||||||
ogg_sync_state fSync;
|
ogg_sync_state fSync;
|
||||||
serialno_OggStream_map fStreams;
|
serialno_OggStream_map fStreams;
|
||||||
@@ -51,6 +54,8 @@ private:
|
|||||||
class GetPageInterface {
|
class GetPageInterface {
|
||||||
public:
|
public:
|
||||||
virtual status_t GetNextPage() = 0;
|
virtual status_t GetNextPage() = 0;
|
||||||
|
virtual status_t GetPageAt(off_t position, ogg_stream_state * stream,
|
||||||
|
int read_size = 4*B_PAGE_SIZE) = 0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#ifndef _OGG_READER_STREAM_INTERFACE_H_
|
|
||||||
#define _OGG_READER_STREAM_INTERFACE_H_
|
|
||||||
|
|
||||||
#include <ogg/ogg.h>
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _OGG_READER_STREAM_INTERFACE_H_
|
|
||||||
@@ -57,13 +57,19 @@ OggStream::OggStream(long serialno)
|
|||||||
{
|
{
|
||||||
TRACE("OggStream::OggStream\n");
|
TRACE("OggStream::OggStream\n");
|
||||||
this->fSerialno = serialno;
|
this->fSerialno = serialno;
|
||||||
|
fCurrentFrame = 0;
|
||||||
|
fCurrentTime = 0;
|
||||||
ogg_stream_init(&fStreamState,serialno);
|
ogg_stream_init(&fStreamState,serialno);
|
||||||
|
fCurrentPage = 0;
|
||||||
|
fCurrentPacket = 0;
|
||||||
|
ogg_stream_init(&fSeekStreamState,serialno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OggStream::~OggStream()
|
OggStream::~OggStream()
|
||||||
{
|
{
|
||||||
|
ogg_stream_clear(&fStreamState);
|
||||||
|
ogg_stream_clear(&fSeekStreamState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +148,40 @@ OggStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
status_t
|
status_t
|
||||||
OggStream::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
|
OggStream::Seek(uint32 seekTo, int64 *frame, bigtime_t *time)
|
||||||
{
|
{
|
||||||
debugger("OggStream::Seek");
|
TRACE("OggStream::Seek to %lld : %lld\n",*frame,*time);
|
||||||
|
if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
|
||||||
|
*frame = max_c(0, *frame); // clip to zero
|
||||||
|
*frame = min_c(*frame, fOggFrameInfos.size()-1); // clip to max
|
||||||
|
// input the page into a temporary seek stream
|
||||||
|
ogg_stream_state seekStreamState;
|
||||||
|
uint pageno = fOggFrameInfos[*frame].GetPage();
|
||||||
|
off_t position = fPagePositions[pageno];
|
||||||
|
ogg_stream_init(&seekStreamState, fSerialno);
|
||||||
|
status_t result = fReaderInterface->GetPageAt(position, &seekStreamState);
|
||||||
|
if (result != B_OK) {
|
||||||
|
return result; // pageno/fPagePosition corrupted?
|
||||||
|
}
|
||||||
|
// discard earlier packets from this page
|
||||||
|
uint packetno = fOggFrameInfos[*frame].GetPacket();
|
||||||
|
while (packetno-- > 0) {
|
||||||
|
ogg_packet packet;
|
||||||
|
if (ogg_stream_packetout(&fSeekStreamState, &packet) != 1) {
|
||||||
|
return B_ERROR; // packetno corrupted?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clear out the former seek stream state
|
||||||
|
// this will delete its internal storage
|
||||||
|
ogg_stream_clear(&fSeekStreamState);
|
||||||
|
// initialize it with the temporary seek stream
|
||||||
|
// this will transfer the internal storage to it
|
||||||
|
fSeekStreamState = seekStreamState;
|
||||||
|
// we notably do not clear our temporary stream
|
||||||
|
// instead we just let it go out of scope
|
||||||
|
fCurrentFrame = *frame;
|
||||||
|
} else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
|
||||||
|
*frame = *time/50000;
|
||||||
|
return Seek(B_MEDIA_SEEK_TO_FRAME,frame,time);
|
||||||
|
}
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +205,36 @@ OggStream::GetNextChunk(void **chunkBuffer, int32 *chunkSize,
|
|||||||
status_t
|
status_t
|
||||||
OggStream::GetPacket(ogg_packet * packet)
|
OggStream::GetPacket(ogg_packet * packet)
|
||||||
{
|
{
|
||||||
|
if (fCurrentFrame == fOggFrameInfos.size()) {
|
||||||
|
// at the end, pull the packet
|
||||||
|
uint old_page = fCurrentPage;
|
||||||
|
uint old_packet = fCurrentPacket;
|
||||||
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
while (ogg_stream_packetpeek(&fStreamState, NULL) != 1) {
|
||||||
fReaderInterface->GetNextPage();
|
fReaderInterface->GetNextPage();
|
||||||
|
fCurrentPage++;
|
||||||
}
|
}
|
||||||
if (ogg_stream_packetout(&fStreamState, packet) != 1) {
|
if (ogg_stream_packetout(&fStreamState, packet) != 1) {
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
OggFrameInfo info(old_page, old_packet);
|
||||||
|
fOggFrameInfos.push_back(info);
|
||||||
|
if (fCurrentPage != old_page) {
|
||||||
|
fCurrentPacket = 0;
|
||||||
|
} else {
|
||||||
|
fCurrentPacket++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// in the middle, get packet at position
|
||||||
|
uint pageno = fOggFrameInfos[fCurrentFrame].GetPage();
|
||||||
|
while (ogg_stream_packetpeek(&fSeekStreamState, NULL) != 1) {
|
||||||
|
off_t position = fPagePositions[pageno++];
|
||||||
|
fReaderInterface->GetPageAt(position, &fSeekStreamState);
|
||||||
|
}
|
||||||
|
if (ogg_stream_packetout(&fSeekStreamState, packet) != 1) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fCurrentFrame++; // ever moving forward!
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <MediaDefs.h>
|
#include <MediaDefs.h>
|
||||||
#include "ogg/ogg.h"
|
#include "ogg/ogg.h"
|
||||||
#include "OggReaderPlugin.h"
|
#include "OggReaderPlugin.h"
|
||||||
|
#include "OggFrameInfo.h"
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace BPrivate { namespace media {
|
namespace BPrivate { namespace media {
|
||||||
@@ -47,8 +49,14 @@ protected:
|
|||||||
virtual off_t TimeToPosition(bigtime_t time);
|
virtual off_t TimeToPosition(bigtime_t time);
|
||||||
private:
|
private:
|
||||||
long fSerialno;
|
long fSerialno;
|
||||||
|
int64 fCurrentFrame;
|
||||||
|
bigtime_t fCurrentTime;
|
||||||
std::vector<off_t> fPagePositions;
|
std::vector<off_t> fPagePositions;
|
||||||
|
std::vector<OggFrameInfo> fOggFrameInfos;
|
||||||
ogg_stream_state fStreamState;
|
ogg_stream_state fStreamState;
|
||||||
|
uint fCurrentPage;
|
||||||
|
uint fCurrentPacket;
|
||||||
|
ogg_stream_state fSeekStreamState;
|
||||||
OggReader::GetPageInterface * fReaderInterface;
|
OggReader::GetPageInterface * fReaderInterface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ OggVorbisStream::IsValidHeader(const ogg_packet & packet)
|
|||||||
OggVorbisStream::OggVorbisStream(long serialno)
|
OggVorbisStream::OggVorbisStream(long serialno)
|
||||||
: OggStream(serialno)
|
: OggStream(serialno)
|
||||||
{
|
{
|
||||||
|
TRACE("OggVorbisStream::OggVorbisStream\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
OggVorbisStream::~OggVorbisStream()
|
OggVorbisStream::~OggVorbisStream()
|
||||||
@@ -151,9 +151,6 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fill out format from header packet
|
// fill out format from header packet
|
||||||
format->type = B_MEDIA_ENCODED_AUDIO;
|
|
||||||
format->user_data_type = B_CODEC_TYPE_INFO;
|
|
||||||
strncpy((char*)format->user_data, "vorb", 4);
|
|
||||||
if (info.bitrate_nominal > 0) {
|
if (info.bitrate_nominal > 0) {
|
||||||
format->u.encoded_audio.bit_rate = info.bitrate_nominal;
|
format->u.encoded_audio.bit_rate = info.bitrate_nominal;
|
||||||
} else if (info.bitrate_upper > 0) {
|
} else if (info.bitrate_upper > 0) {
|
||||||
@@ -166,11 +163,8 @@ OggVorbisStream::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.frame_size = sizeof(ogg_packet);
|
|
||||||
format->u.encoded_audio.output.frame_rate = (float)info.rate;
|
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.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
|
||||||
format->u.encoded_audio.output.byte_order = B_MEDIA_HOST_ENDIAN;
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -192,7 +186,7 @@ OggVorbisStream::GetStreamInfo(int64 *frameCount, bigtime_t *duration,
|
|||||||
SaveHeaderPacket(packet);
|
SaveHeaderPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
format->SetMetaData((void*)&fHeaderPackets,sizeof(&fHeaderPackets));
|
format->SetMetaData((void*)&fHeaderPackets,sizeof(fHeaderPackets));
|
||||||
*duration = 80000000;
|
*duration = 80000000;
|
||||||
*frameCount = 60000;
|
*frameCount = 60000;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ speexDecoder::Setup(media_format *inputFormat,
|
|||||||
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &enabled);
|
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &enabled);
|
||||||
}
|
}
|
||||||
speex_decoder_ctl(fDecoderState, SPEEX_GET_FRAME_SIZE, &fHeader->frame_size);
|
speex_decoder_ctl(fDecoderState, SPEEX_GET_FRAME_SIZE, &fHeader->frame_size);
|
||||||
fSpeexOutputLength = fHeader->frame_size * sizeof(float) * fHeader->nb_channels;
|
|
||||||
if (fHeader->nb_channels == 2) {
|
if (fHeader->nb_channels == 2) {
|
||||||
SpeexCallback callback;
|
SpeexCallback callback;
|
||||||
SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
|
SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
|
||||||
@@ -197,13 +196,17 @@ void speexDecoder::CopyInfoToDecodedFormat(media_raw_audio_format * raf) {
|
|||||||
raf->channel_count = fHeader->nb_channels;
|
raf->channel_count = fHeader->nb_channels;
|
||||||
raf->format = media_raw_audio_format::B_AUDIO_FLOAT; // XXX verify: support others?
|
raf->format = media_raw_audio_format::B_AUDIO_FLOAT; // XXX verify: support others?
|
||||||
raf->byte_order = B_MEDIA_HOST_ENDIAN; // XXX should support other endain, too
|
raf->byte_order = B_MEDIA_HOST_ENDIAN; // XXX should support other endain, too
|
||||||
if (raf->buffer_size < 512 || raf->buffer_size > 65536) {
|
int buffer_size = raf->buffer_size;
|
||||||
raf->buffer_size = AudioBufferSize(raf);
|
if (buffer_size < 512 || buffer_size > 65536) {
|
||||||
|
buffer_size = AudioBufferSize(raf);
|
||||||
}
|
}
|
||||||
raf->buffer_size = ((raf->buffer_size - 1) / fSpeexOutputLength + 1) * fSpeexOutputLength;
|
int output_length = fHeader->frame_size * fHeader->nb_channels * (raf->format & 0xf);
|
||||||
|
buffer_size = ((buffer_size - 1) / output_length + 1) * output_length;
|
||||||
|
raf->buffer_size = buffer_size;
|
||||||
// setup output variables
|
// setup output variables
|
||||||
fFrameSize = (raf->format & 0xf) * raf->channel_count;
|
fFrameSize = (raf->format & 0xf) * raf->channel_count;
|
||||||
fOutputBufferSize = raf->buffer_size;
|
fOutputBufferSize = buffer_size;
|
||||||
|
fSpeexOutputLength = output_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#define DECODE_BUFFER_SIZE (32 * 1024)
|
#define DECODE_BUFFER_SIZE (32 * 1024)
|
||||||
|
|
||||||
|
|
||||||
inline size_t
|
inline size_t
|
||||||
AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */)
|
AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */)
|
||||||
{
|
{
|
||||||
@@ -23,9 +24,62 @@ AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000
|
|||||||
* (size_t)((raf->frame_rate * buffer_duration) / 1000000.0);
|
* (size_t)((raf->frame_rate * buffer_duration) / 1000000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
vorbisDecoder::vorbisDecoder()
|
|
||||||
|
/*
|
||||||
|
* vorbis descriptions/formats
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static media_format_description
|
||||||
|
vorbis_description()
|
||||||
{
|
{
|
||||||
TRACE("vorbisDecoder::vorbisDecoder\n");
|
media_format_description description;
|
||||||
|
description.family = B_MISC_FORMAT_FAMILY;
|
||||||
|
description.u.misc.file_format = 'OggS';
|
||||||
|
description.u.misc.codec = 'vorb';
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_vorbis_media_raw_audio_format(media_raw_audio_format * output)
|
||||||
|
{
|
||||||
|
output->format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||||
|
output->byte_order = B_MEDIA_HOST_ENDIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static media_format
|
||||||
|
vorbis_encoded_media_format()
|
||||||
|
{
|
||||||
|
media_format format;
|
||||||
|
format.type = B_MEDIA_ENCODED_AUDIO;
|
||||||
|
format.user_data_type = B_CODEC_TYPE_INFO;
|
||||||
|
strncpy((char*)format.user_data, "vorb", 4);
|
||||||
|
format.u.encoded_audio.frame_size = sizeof(ogg_packet);
|
||||||
|
init_vorbis_media_raw_audio_format(&format.u.encoded_audio.output);
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static media_format
|
||||||
|
vorbis_decoded_media_format()
|
||||||
|
{
|
||||||
|
media_format format;
|
||||||
|
format.type = B_MEDIA_RAW_AUDIO;
|
||||||
|
init_vorbis_media_raw_audio_format(&format.u.raw_audio);
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VorbisDecoder
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
VorbisDecoder::VorbisDecoder()
|
||||||
|
{
|
||||||
|
TRACE("VorbisDecoder::VorbisDecoder\n");
|
||||||
vorbis_info_init(&fInfo);
|
vorbis_info_init(&fInfo);
|
||||||
vorbis_comment_init(&fComment);
|
vorbis_comment_init(&fComment);
|
||||||
|
|
||||||
@@ -35,44 +89,41 @@ vorbisDecoder::vorbisDecoder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vorbisDecoder::~vorbisDecoder()
|
VorbisDecoder::~VorbisDecoder()
|
||||||
{
|
{
|
||||||
TRACE("vorbisDecoder::~vorbisDecoder\n");
|
TRACE("VorbisDecoder::~VorbisDecoder\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
vorbisDecoder::GetCodecInfo(media_codec_info &info)
|
VorbisDecoder::GetCodecInfo(media_codec_info &info)
|
||||||
{
|
{
|
||||||
strcpy(info.short_name, "vorbis");
|
strncpy(info.short_name, "vorbis", sizeof(info.short_name));
|
||||||
strcpy(info.pretty_name, "vorbis decoder, based on libvorbis");
|
strncpy(info.pretty_name, "vorbis decoder, by Andrew Bachmann, based on libvorbis", sizeof(info.pretty_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vorbisDecoder::Setup(media_format *inputFormat,
|
VorbisDecoder::Setup(media_format *inputFormat,
|
||||||
const void *infoBuffer, int32 infoSize)
|
const void *infoBuffer, int32 infoSize)
|
||||||
{
|
{
|
||||||
if (inputFormat->type != B_MEDIA_ENCODED_AUDIO) {
|
TRACE("VorbisDecoder::Setup\n");
|
||||||
TRACE("vorbisDecoder::Setup not called with audio stream: not vorbis\n");
|
if (!format_is_compatible(vorbis_encoded_media_format(),*inputFormat)) {
|
||||||
return B_ERROR;
|
return B_MEDIA_BAD_FORMAT;
|
||||||
}
|
}
|
||||||
if (inputFormat->u.encoded_audio.encoding != 'vorb') {
|
// grab header packets from meta data
|
||||||
TRACE("vorbisDecoder::Setup not called with 'vorb' stream: not vorbis\n");
|
if (inputFormat->MetaDataSize() != sizeof(std::vector<ogg_packet>)) {
|
||||||
return B_ERROR;
|
TRACE("VorbisDecoder::Setup not called with ogg_packet<vector> meta data: not vorbis\n");
|
||||||
}
|
|
||||||
if (inputFormat->MetaDataSize() != sizeof(std::vector<ogg_packet> *)) {
|
|
||||||
TRACE("vorbisDecoder::Setup not called with ogg_packet<vector> meta data: not vorbis\n");
|
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
std::vector<ogg_packet> * packets = (std::vector<ogg_packet> *)inputFormat->MetaData();
|
std::vector<ogg_packet> * packets = (std::vector<ogg_packet> *)inputFormat->MetaData();
|
||||||
if (packets->size() != 3) {
|
if (packets->size() != 3) {
|
||||||
TRACE("vorbisDecoder::Setup not called with three ogg_packets: not vorbis\n");
|
TRACE("VorbisDecoder::Setup not called with three ogg_packets: not vorbis\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
// parse header packet
|
// parse header packet
|
||||||
if (vorbis_synthesis_headerin(&fInfo,&fComment,&(*packets)[0]) != 0) {
|
if (vorbis_synthesis_headerin(&fInfo,&fComment,&(*packets)[0]) != 0) {
|
||||||
TRACE("vorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header\n");
|
TRACE("VorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
// parse comment packet
|
// parse comment packet
|
||||||
@@ -88,68 +139,58 @@ vorbisDecoder::Setup(media_format *inputFormat,
|
|||||||
// initialize decoder
|
// initialize decoder
|
||||||
vorbis_synthesis_init(&fDspState,&fInfo);
|
vorbis_synthesis_init(&fDspState,&fInfo);
|
||||||
vorbis_block_init(&fDspState,&fBlock);
|
vorbis_block_init(&fDspState,&fBlock);
|
||||||
// fill out the encoding format
|
// setup default output
|
||||||
CopyInfoToEncodedFormat(inputFormat);
|
media_format requested_format = vorbis_decoded_media_format();
|
||||||
return B_OK;
|
((media_raw_audio_format)requested_format.u.raw_audio) = inputFormat->u.encoded_audio.output;
|
||||||
|
return NegotiateOutputFormat(&requested_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vorbisDecoder::CopyInfoToEncodedFormat(media_format * format) {
|
|
||||||
format->type = B_MEDIA_ENCODED_AUDIO;
|
|
||||||
// ToDo: this won't work any longer
|
|
||||||
// format->u.encoded_audio.encoding
|
|
||||||
// = (media_encoded_audio_format::audio_encoding)'vorb';
|
|
||||||
if (fInfo.bitrate_nominal > 0) {
|
|
||||||
format->u.encoded_audio.bit_rate = fInfo.bitrate_nominal;
|
|
||||||
} else if (fInfo.bitrate_upper > 0) {
|
|
||||||
format->u.encoded_audio.bit_rate = fInfo.bitrate_upper;
|
|
||||||
} else if (fInfo.bitrate_lower > 0) {
|
|
||||||
format->u.encoded_audio.bit_rate = fInfo.bitrate_lower;
|
|
||||||
}
|
|
||||||
CopyInfoToDecodedFormat(&format->u.encoded_audio.output);
|
|
||||||
format->u.encoded_audio.frame_size = sizeof(ogg_packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vorbisDecoder::CopyInfoToDecodedFormat(media_raw_audio_format * raf) {
|
|
||||||
raf->frame_rate = (float)fInfo.rate; // XXX long->float ??
|
|
||||||
raf->channel_count = fInfo.channels;
|
|
||||||
raf->format = media_raw_audio_format::B_AUDIO_FLOAT; // XXX verify: support others?
|
|
||||||
raf->byte_order = B_MEDIA_HOST_ENDIAN; // XXX should support other endain, too
|
|
||||||
|
|
||||||
if (raf->buffer_size < 512 || raf->buffer_size > 65536) {
|
|
||||||
raf->buffer_size = AudioBufferSize(raf);
|
|
||||||
}
|
|
||||||
// setup output variables
|
|
||||||
fFrameSize = (raf->format & 0xf) * raf->channel_count;
|
|
||||||
fOutputBufferSize = raf->buffer_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vorbisDecoder::NegotiateOutputFormat(media_format *ioDecodedFormat)
|
VorbisDecoder::NegotiateOutputFormat(media_format *ioDecodedFormat)
|
||||||
{
|
{
|
||||||
TRACE("vorbisDecoder::NegotiateOutputFormat\n");
|
TRACE("VorbisDecoder::NegotiateOutputFormat\n");
|
||||||
// BeBook says: The codec will find and return in ioFormat its best matching format
|
// BMediaTrack::DecodedFormat
|
||||||
// => This means, we never return an error, and always change the format values
|
// Pass in ioFormat the format that you want (with wildcards
|
||||||
// that we don't support to something more applicable
|
// as applicable). The codec will find and return in ioFormat
|
||||||
ioDecodedFormat->type = B_MEDIA_RAW_AUDIO;
|
// its best matching format.
|
||||||
CopyInfoToDecodedFormat(&ioDecodedFormat->u.raw_audio);
|
//
|
||||||
// add the media_mult_audio_format fields
|
// BMediaDecoder::SetOutputFormat
|
||||||
if (ioDecodedFormat->u.raw_audio.channel_mask == 0) {
|
// sets the format the decoder should output. On return,
|
||||||
if (fInfo.channels == 1) {
|
// the outputFormat is changed to match the actual format
|
||||||
ioDecodedFormat->u.raw_audio.channel_mask = B_CHANNEL_LEFT;
|
// that will be output; this can be different if you
|
||||||
} else {
|
// specified any wildcards.
|
||||||
ioDecodedFormat->u.raw_audio.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
|
//
|
||||||
|
media_format format = vorbis_decoded_media_format();
|
||||||
|
format.u.raw_audio.frame_rate = (float)fInfo.rate;
|
||||||
|
format.u.raw_audio.channel_count = fInfo.channels;
|
||||||
|
format.u.raw_audio.channel_mask = B_CHANNEL_LEFT | (fInfo.channels != 1 ? B_CHANNEL_RIGHT : 0);
|
||||||
|
if (ioDecodedFormat->u.raw_audio.buffer_size < 512) {
|
||||||
|
format.u.raw_audio.buffer_size = AudioBufferSize(&format.u.raw_audio);
|
||||||
}
|
}
|
||||||
|
if (!format_is_compatible(format,*ioDecodedFormat)) {
|
||||||
|
#ifdef NegotiateOutputFormat_AS_BEBOOK_SPEC
|
||||||
|
return B_ERROR;
|
||||||
|
#else
|
||||||
|
// Be R5 behavior: never fail (?) => nuke the input format
|
||||||
|
*ioDecodedFormat = format;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
ioDecodedFormat->SpecializeTo(&format);
|
||||||
|
// setup output variables
|
||||||
|
fFrameSize = (ioDecodedFormat->u.raw_audio.format & 0xf) *
|
||||||
|
(ioDecodedFormat->u.raw_audio.channel_count);
|
||||||
|
fOutputBufferSize = ioDecodedFormat->u.raw_audio.buffer_size;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vorbisDecoder::Seek(uint32 seekTo,
|
VorbisDecoder::Seek(uint32 seekTo,
|
||||||
int64 seekFrame, int64 *frame,
|
int64 seekFrame, int64 *frame,
|
||||||
bigtime_t seekTime, bigtime_t *time)
|
bigtime_t seekTime, bigtime_t *time)
|
||||||
{
|
{
|
||||||
TRACE("vorbisDecoder::Seek\n");
|
TRACE("VorbisDecoder::Seek\n");
|
||||||
float **pcm;
|
float **pcm;
|
||||||
// throw the old samples away!
|
// throw the old samples away!
|
||||||
int samples = vorbis_synthesis_pcmout(&fDspState,&pcm);
|
int samples = vorbis_synthesis_pcmout(&fDspState,&pcm);
|
||||||
@@ -159,15 +200,15 @@ vorbisDecoder::Seek(uint32 seekTo,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vorbisDecoder::Decode(void *buffer, int64 *frameCount,
|
VorbisDecoder::Decode(void *buffer, int64 *frameCount,
|
||||||
media_header *mediaHeader, media_decode_info *info /* = 0 */)
|
media_header *mediaHeader, media_decode_info *info /* = 0 */)
|
||||||
{
|
{
|
||||||
// TRACE("vorbisDecoder::Decode\n");
|
// TRACE("VorbisDecoder::Decode\n");
|
||||||
uint8 * out_buffer = static_cast<uint8 *>(buffer);
|
uint8 * out_buffer = static_cast<uint8 *>(buffer);
|
||||||
int32 out_bytes_needed = fOutputBufferSize;
|
int32 out_bytes_needed = fOutputBufferSize;
|
||||||
|
|
||||||
mediaHeader->start_time = fStartTime;
|
mediaHeader->start_time = fStartTime;
|
||||||
//TRACE("vorbisDecoder: Decoding start time %.6f\n", fStartTime / 1000000.0);
|
//TRACE("VorbisDecoder: Decoding start time %.6f\n", fStartTime / 1000000.0);
|
||||||
|
|
||||||
while (out_bytes_needed > 0) {
|
while (out_bytes_needed > 0) {
|
||||||
int samples;
|
int samples;
|
||||||
@@ -182,11 +223,11 @@ vorbisDecoder::Decode(void *buffer, int64 *frameCount,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
TRACE("vorbisDecoder::Decode: GetNextChunk failed\n");
|
TRACE("VorbisDecoder::Decode: GetNextChunk failed\n");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
if (chunkSize != sizeof(ogg_packet)) {
|
if (chunkSize != sizeof(ogg_packet)) {
|
||||||
TRACE("vorbisDecoder::Decode: chunk not ogg_packet-sized\n");
|
TRACE("VorbisDecoder::Decode: chunk not ogg_packet-sized\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
ogg_packet * packet = static_cast<ogg_packet*>(chunkBuffer);
|
ogg_packet * packet = static_cast<ogg_packet*>(chunkBuffer);
|
||||||
@@ -207,7 +248,7 @@ vorbisDecoder::Decode(void *buffer, int64 *frameCount,
|
|||||||
vorbis_synthesis_read(&fDspState,samples);
|
vorbis_synthesis_read(&fDspState,samples);
|
||||||
|
|
||||||
fStartTime += (1000000LL * samples) / fInfo.rate;
|
fStartTime += (1000000LL * samples) / fInfo.rate;
|
||||||
//TRACE("vorbisDecoder: fStartTime inc'd to %.6f\n", fStartTime / 1000000.0);
|
//TRACE("VorbisDecoder: fStartTime inc'd to %.6f\n", fStartTime / 1000000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@@ -219,8 +260,14 @@ done:
|
|||||||
return B_LAST_BUFFER_ERROR;
|
return B_LAST_BUFFER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VorbisDecoderPlugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
Decoder *
|
Decoder *
|
||||||
vorbisDecoderPlugin::NewDecoder()
|
VorbisDecoderPlugin::NewDecoder()
|
||||||
{
|
{
|
||||||
static BLocker locker;
|
static BLocker locker;
|
||||||
static bool initdone = false;
|
static bool initdone = false;
|
||||||
@@ -228,28 +275,30 @@ vorbisDecoderPlugin::NewDecoder()
|
|||||||
if (!initdone) {
|
if (!initdone) {
|
||||||
initdone = true;
|
initdone = true;
|
||||||
}
|
}
|
||||||
return new vorbisDecoder;
|
return new VorbisDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
vorbisDecoderPlugin::RegisterDecoder()
|
VorbisDecoderPlugin::RegisterDecoder()
|
||||||
{
|
{
|
||||||
media_format_description description;
|
media_format_description description = vorbis_description();
|
||||||
// ToDo: what about B_OGG_FORMAT_FAMILY?
|
media_format format = vorbis_encoded_media_format();
|
||||||
description.family = B_MISC_FORMAT_FAMILY;
|
|
||||||
description.u.misc.file_format = 'ogg ';
|
|
||||||
description.u.misc.codec = 'vorb';
|
|
||||||
|
|
||||||
media_format format;
|
BMediaFormats formats;
|
||||||
format.type = B_MEDIA_ENCODED_AUDIO;
|
status_t result = formats.InitCheck();
|
||||||
format.u.encoded_audio = media_encoded_audio_format::wildcard;
|
if (result != B_OK) {
|
||||||
|
return result;
|
||||||
return BMediaFormats().MakeFormatFor(&description, 1, &format);
|
|
||||||
}
|
}
|
||||||
|
return formats.MakeFormatFor(&description, 1, &format);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* instantiate_plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
MediaPlugin *instantiate_plugin()
|
MediaPlugin *instantiate_plugin()
|
||||||
{
|
{
|
||||||
return new vorbisDecoderPlugin;
|
return new VorbisDecoderPlugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
#ifndef _VORBIS_CODEC_PLUGIN_H_
|
||||||
|
#define _VORBIS_CODEC_PLUGIN_H_
|
||||||
|
|
||||||
#include "DecoderPlugin.h"
|
#include "DecoderPlugin.h"
|
||||||
|
|
||||||
#include "libvorbis/vorbis/codec.h"
|
#include "libvorbis/vorbis/codec.h"
|
||||||
|
|
||||||
class vorbisDecoder : public Decoder
|
class VorbisDecoder : public Decoder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
vorbisDecoder();
|
VorbisDecoder();
|
||||||
~vorbisDecoder();
|
~VorbisDecoder();
|
||||||
|
|
||||||
void GetCodecInfo(media_codec_info &info);
|
void GetCodecInfo(media_codec_info &info);
|
||||||
status_t Setup(media_format *inputFormat,
|
status_t Setup(media_format *inputFormat,
|
||||||
@@ -23,9 +26,6 @@ public:
|
|||||||
media_header *mediaHeader, media_decode_info *info);
|
media_header *mediaHeader, media_decode_info *info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CopyInfoToEncodedFormat(media_format * format);
|
|
||||||
void CopyInfoToDecodedFormat(media_raw_audio_format * raf);
|
|
||||||
|
|
||||||
vorbis_info fInfo;
|
vorbis_info fInfo;
|
||||||
vorbis_comment fComment;
|
vorbis_comment fComment;
|
||||||
vorbis_dsp_state fDspState;
|
vorbis_dsp_state fDspState;
|
||||||
@@ -36,9 +36,11 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class vorbisDecoderPlugin : public DecoderPlugin
|
class VorbisDecoderPlugin : public DecoderPlugin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Decoder * NewDecoder();
|
Decoder * NewDecoder();
|
||||||
status_t RegisterDecoder();
|
status_t RegisterDecoder();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif _VORBIS_CODEC_PLUGIN_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user