Files
haiku-beta6/src/add-ons/media/plugins/vorbis/vorbisCodecPlugin.cpp
T
shatty f339b6e0b5 clean vorbis seek requires discarding pending samples from the dsp
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5783 a95241bf-73f2-0310-859d-f6bbb57e9c96
2003-12-28 12:19:37 +00:00

237 lines
6.8 KiB
C++

#include <stdio.h>
#include <Autolock.h>
#include <DataIO.h>
#include <Locker.h>
#include <MediaFormats.h>
#include <MediaRoster.h>
#include "vorbisCodecPlugin.h"
#define TRACE_THIS 1
#if TRACE_THIS
#define TRACE printf
#else
#define TRACE(a...)
#endif
#define DECODE_BUFFER_SIZE (32 * 1024)
vorbisDecoder::vorbisDecoder()
{
TRACE("vorbisDecoder::vorbisDecoder\n");
vorbis_info_init(&fInfo);
vorbis_comment_init(&fComment);
fStartTime = 0;
fFrameSize = 0;
fOutputBufferSize = 0;
}
vorbisDecoder::~vorbisDecoder()
{
TRACE("vorbisDecoder::~vorbisDecoder\n");
}
status_t
vorbisDecoder::Setup(media_format *ioEncodedFormat,
const void *infoBuffer, int32 infoSize)
{
if ((ioEncodedFormat->type != B_MEDIA_UNKNOWN_TYPE)
&& (ioEncodedFormat->type != B_MEDIA_ENCODED_AUDIO)) {
TRACE("vorbisDecoder::Setup not called with audio/unknown stream: not vorbis");
return B_ERROR;
}
if (infoSize != sizeof(ogg_packet)) {
TRACE("vorbisDecoder::Setup not called with ogg_packet info: not vorbis");
return B_ERROR;
}
ogg_packet * packet = (ogg_packet*)infoBuffer;
// parse header packet
if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) {
TRACE("vorbisDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis header");
return B_ERROR;
}
// get comment packet
int32 size;
media_header mh;
if (GetNextChunk((void**)&packet, &size, &mh) != B_OK) {
TRACE("vorbisDecoder::Setup: GetNextChunk failed to get comment\n");
return B_ERROR;
}
// parse comment packet
if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) {
TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis comment");
return B_ERROR;
}
// get codebook packet
if (GetNextChunk((void**)&packet, &size, &mh) != B_OK) {
TRACE("vorbisDecoder::Setup: GetNextChunk failed to get codebook\n");
return B_ERROR;
}
// parse codebook packet
if (vorbis_synthesis_headerin(&fInfo,&fComment,packet) != 0) {
TRACE("vorbiseDecoder::Setup: vorbis_synthesis_headerin failed: not vorbis codebook");
return B_ERROR;
}
// initialize decoder
vorbis_synthesis_init(&fDspState,&fInfo);
vorbis_block_init(&fDspState,&fBlock);
// fill out the encoding format
CopyInfoToEncodedFormat(ioEncodedFormat);
return B_OK;
}
void vorbisDecoder::CopyInfoToEncodedFormat(media_format * format) {
format->type = B_MEDIA_ENCODED_AUDIO;
format->u.encoded_audio.encoding
= (media_encoded_audio_format::audio_encoding)'vorb';
if (fInfo.bitrate_nominal) {
format->u.encoded_audio.bit_rate = fInfo.bitrate_nominal;
} else if (fInfo.bitrate_upper) {
format->u.encoded_audio.bit_rate = fInfo.bitrate_upper;
} else if (fInfo.bitrate_lower) {
format->u.encoded_audio.bit_rate = fInfo.bitrate_lower;
}
CopyInfoToDecodedFormat(&format->u.encoded_audio.output);
}
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) {
BMediaRoster * roster = BMediaRoster::Roster();
raf->buffer_size
= roster->AudioBufferSizeFor(raf->channel_count,raf->format,raf->frame_rate);
}
}
status_t
vorbisDecoder::NegotiateOutputFormat(media_format *ioDecodedFormat)
{
TRACE("vorbisDecoder::NegotiateOutputFormat\n");
// BeBook says: The codec will find and return in ioFormat its best matching format
// => This means, we never return an error, and always change the format values
// that we don't support to something more applicable
ioDecodedFormat->type = B_MEDIA_RAW_AUDIO;
CopyInfoToDecodedFormat(&ioDecodedFormat->u.raw_audio);
if (ioDecodedFormat->u.raw_audio.channel_mask == 0) {
if (fInfo.channels == 1) {
ioDecodedFormat->u.raw_audio.channel_mask = B_CHANNEL_LEFT;
} else {
ioDecodedFormat->u.raw_audio.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
}
}
// setup rest of the needed variables
fFrameSize = (ioDecodedFormat->u.raw_audio.format & 0xf) * fInfo.channels;
fOutputBufferSize = ioDecodedFormat->u.raw_audio.buffer_size;
return B_OK;
}
status_t
vorbisDecoder::Seek(uint32 seekTo,
int64 seekFrame, int64 *frame,
bigtime_t seekTime, bigtime_t *time)
{
TRACE("vorbisDecoder::Seek\n");
float **pcm;
// throw the old samples away!
int samples = vorbis_synthesis_pcmout(&fDspState,&pcm);
vorbis_synthesis_read(&fDspState,samples);
return B_OK;
}
status_t
vorbisDecoder::Decode(void *buffer, int64 *frameCount,
media_header *mediaHeader, media_decode_info *info /* = 0 */)
{
TRACE("vorbisDecoder::Decode\n");
uint8 * out_buffer = static_cast<uint8 *>(buffer);
int32 out_bytes_needed = fOutputBufferSize;
mediaHeader->start_time = fStartTime;
//TRACE("vorbisDecoder: Decoding start time %.6f\n", fStartTime / 1000000.0);
while (out_bytes_needed > 0) {
int samples;
float **pcm;
while ((samples = vorbis_synthesis_pcmout(&fDspState,&pcm)) == 0) {
// get a new packet
void *chunkBuffer;
int32 chunkSize;
media_header mh;
status_t status = GetNextChunk(&chunkBuffer, &chunkSize, &mh);
if (status == B_LAST_BUFFER_ERROR) {
goto done;
}
if (status != B_OK) {
TRACE("vorbisDecoder::Decode: GetNextChunk failed\n");
return status;
}
if (chunkSize != sizeof(ogg_packet)) {
TRACE("vorbisDecoder::Decode: chunk not ogg_packet-sized\n");
return B_ERROR;
}
ogg_packet * packet = static_cast<ogg_packet*>(chunkBuffer);
if (vorbis_synthesis(&fBlock,packet)==0) {
vorbis_synthesis_blockin(&fDspState,&fBlock);
}
}
// reduce samples to the amount of samples we will actually consume
samples = min_c(samples,out_bytes_needed/fFrameSize);
for (int sample = 0; sample < samples ; sample++) {
for (int channel = 0; channel < fInfo.channels; channel++) {
*((float*)out_buffer) = pcm[channel][sample];
out_buffer += sizeof(float);
}
}
out_bytes_needed -= samples * fInfo.channels * sizeof(float);
// report back how many samples we consumed
vorbis_synthesis_read(&fDspState,samples);
fStartTime += (1000000LL * samples) / fInfo.rate;
//TRACE("vorbisDecoder: fStartTime inc'd to %.6f\n", fStartTime / 1000000.0);
}
done:
*frameCount = (fOutputBufferSize - out_bytes_needed) / fFrameSize;
if (out_buffer != buffer) {
return B_OK;
}
return B_LAST_BUFFER_ERROR;
}
Decoder *
vorbisDecoderPlugin::NewDecoder()
{
static BLocker locker;
static bool initdone = false;
BAutolock lock(locker);
if (!initdone) {
initdone = true;
}
return new vorbisDecoder;
}
status_t
vorbisDecoderPlugin::RegisterPlugin()
{
PublishDecoder("unknown", "vorbis", "vorbis decoder, based on libvorbis");
PublishDecoder("audiocodec/vorbis", "vorbis", "vorbis decoder, based on libvorbis");
return B_OK;
}
MediaPlugin *instantiate_plugin()
{
return new vorbisDecoderPlugin;
}