fix brain-dead branching
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6750 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,238 +0,0 @@
|
||||
#include "OggCodecs.h"
|
||||
#include <MediaFormats.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE_THIS 0
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...) ((void)0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Useful ogg_packet functions
|
||||
*/
|
||||
|
||||
bool
|
||||
BPrivate::media::findIdentifier(const ogg_packet & packet, const char * id, uint pos)
|
||||
{
|
||||
TRACE("findIdentifier\n");
|
||||
uint length = strlen(id);
|
||||
if ((unsigned)packet.bytes < pos+length) {
|
||||
return false;
|
||||
}
|
||||
return !memcmp(&packet.packet[pos], id, length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OggCodec implementations
|
||||
*/
|
||||
|
||||
|
||||
OggCodec::OggCodec()
|
||||
: fCurrentFrame(-1),
|
||||
fCurrentTime(0),
|
||||
fOldFrame(-1),
|
||||
fOldGranulePos(-1),
|
||||
fInitCheck(B_NO_INIT),
|
||||
fPacketData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* virtual */
|
||||
OggCodec::~OggCodec()
|
||||
{
|
||||
delete fPacketData;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
OggCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const
|
||||
{
|
||||
return (packetno == 0);
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggCodec::HandlePacket(const ogg_packet & packet)
|
||||
{
|
||||
TRACE("OggCodec::HandlePacket\n");
|
||||
if (!IsHeaderPacket(packet, (fInitCheck == B_OK ? 1 : 0))) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
if (!packet.b_o_s) {
|
||||
TRACE("OggCodec::HandlePacket failed : not beginning of stream\n");
|
||||
return B_ERROR; // first packet was not beginning of stream
|
||||
}
|
||||
|
||||
// parse header packet
|
||||
uint32 four_bytes = *(uint32*)(&packet);
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description;
|
||||
description.family = B_MISC_FORMAT_FAMILY;
|
||||
description.u.misc.file_format = 'OggS';
|
||||
description.u.misc.codec = four_bytes;
|
||||
BMediaFormats formats;
|
||||
if (formats.InitCheck() == B_OK) {
|
||||
formats.GetFormatFor(description, &fMediaFormat);
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
fMediaFormat.user_data_type = B_CODEC_TYPE_INFO;
|
||||
strncpy((char*)fMediaFormat.user_data, (char*)(&packet), 4);
|
||||
|
||||
fPacketData = new unsigned char[packet.bytes];
|
||||
if (fPacketData == NULL) {
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
memcpy(fPacketData, packet.packet, packet.bytes);
|
||||
ogg_packet copy = packet;
|
||||
copy.packet = fPacketData;
|
||||
|
||||
fMediaFormat.SetMetaData(©,sizeof(copy));
|
||||
fInitCheck = B_OK;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggCodec::GetFormat(media_format * format) const
|
||||
{
|
||||
if (format == 0) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
if (fInitCheck != B_OK) {
|
||||
return fInitCheck;
|
||||
}
|
||||
*format = fMediaFormat;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ int64
|
||||
OggCodec::GranulesToFrames(int64 granules) const
|
||||
{
|
||||
return granules;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ int64
|
||||
OggCodec::FramesToGranules(int64 frames) const
|
||||
{
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
int result;
|
||||
while ((result = ogg_stream_packetout(stream, &fChunkPacket)) < 0) {
|
||||
// ignore previous packet fragments
|
||||
}
|
||||
if (result == 0) {
|
||||
return B_BUFFER_NOT_AVAILABLE;
|
||||
}
|
||||
*chunkBuffer = &fChunkPacket;
|
||||
*chunkSize = sizeof(fChunkPacket);
|
||||
mediaHeader->start_time = fCurrentTime;
|
||||
ogg_int64_t granulepos = fChunkPacket.granulepos;
|
||||
if (streaming) {
|
||||
if (granulepos < 0) {
|
||||
fCurrentFrame = -1;
|
||||
} else {
|
||||
fCurrentFrame = fOldFrame + GranulesToFrames(granulepos)
|
||||
- GranulesToFrames(fOldGranulePos);
|
||||
fOldFrame = fCurrentFrame;
|
||||
fOldGranulePos = granulepos;
|
||||
}
|
||||
} else {
|
||||
debugger("explode");
|
||||
}
|
||||
// default (silly) 1 frame/sec
|
||||
fCurrentTime = 1000000LL * fCurrentFrame;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OggCodecTest implementations
|
||||
*/
|
||||
|
||||
|
||||
OggCodecTest::OggCodecTest()
|
||||
{
|
||||
TRACE("OggCodecTest::OggCodecTest\n");
|
||||
OggCodecRoster * roster = OggCodecRoster::Roster();
|
||||
if (roster != NULL) {
|
||||
roster->AddCodecTest(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* virtual */
|
||||
OggCodecTest::~OggCodecTest()
|
||||
{
|
||||
TRACE("OggCodecTest::~OggCodecTest\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OggCodecRoster implementations
|
||||
*/
|
||||
|
||||
|
||||
/* static */ OggCodecRoster *
|
||||
OggCodecRoster::Roster(status_t * outError)
|
||||
{
|
||||
TRACE("OggCodecRoster::Roster\n");
|
||||
static OggCodecRoster * ogg_codec_roster = NULL;
|
||||
if (ogg_codec_roster == NULL) {
|
||||
ogg_codec_roster = new OggCodecRoster();
|
||||
if ((ogg_codec_roster == NULL) && (outError != NULL)) {
|
||||
*outError = B_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
return ogg_codec_roster;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
OggCodecRoster::AddCodecTest(const OggCodecTest * test)
|
||||
{
|
||||
TRACE("OggCodecRoster::AddCodecTest\n");
|
||||
fCodecs.push_back(test);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
OggCodec *
|
||||
OggCodecRoster::CodecFor(const ogg_packet & packet) const
|
||||
{
|
||||
TRACE("OggCodecRoster::CodecFor\n");
|
||||
std::vector<const OggCodecTest *>::const_iterator iter;
|
||||
for(iter = fCodecs.begin() ; iter != fCodecs.end() ; iter++) {
|
||||
const OggCodecTest * test = *iter;
|
||||
if (test->RecognizesInitialPacket(packet)) {
|
||||
return test->InstantiateCodec();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
OggCodecRoster::OggCodecRoster()
|
||||
{
|
||||
TRACE("OggCodecRoster::OggCodecRoster\n");
|
||||
}
|
||||
|
||||
|
||||
OggCodecRoster::~OggCodecRoster()
|
||||
{
|
||||
TRACE("OggCodecRoster::~OggCodecRoster\n");
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#ifndef _OGG_CODECS_H
|
||||
#define _OGG_CODECS_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <MediaDefs.h>
|
||||
#include "ogg/ogg.h"
|
||||
#include <vector>
|
||||
|
||||
namespace BPrivate { namespace media {
|
||||
|
||||
|
||||
/**
|
||||
* Useful ogg_packet functions
|
||||
*/
|
||||
|
||||
bool findIdentifier(const ogg_packet & packet, const char * id, uint pos);
|
||||
|
||||
/**
|
||||
* OggCodec
|
||||
*
|
||||
* An OggCodec is instantiated to assist an OggTrack with interacting with the codecs.
|
||||
* It helps with seeking, timing, and transforming the ogg_packets into chunks.
|
||||
*/
|
||||
class OggCodec {
|
||||
public:
|
||||
OggCodec();
|
||||
virtual ~OggCodec();
|
||||
|
||||
virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const;
|
||||
virtual status_t HandlePacket(const ogg_packet & packet);
|
||||
virtual status_t GetFormat(media_format * format) const;
|
||||
virtual int64 GranulesToFrames(int64 granules) const;
|
||||
virtual int64 FramesToGranules(int64 frames) const;
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
|
||||
protected:
|
||||
ogg_packet fChunkPacket;
|
||||
int64 fCurrentFrame;
|
||||
bigtime_t fCurrentTime;
|
||||
int64 fOldFrame;
|
||||
ogg_int64_t fOldGranulePos;
|
||||
media_format fMediaFormat;
|
||||
status_t fInitCheck;
|
||||
|
||||
private:
|
||||
unsigned char * fPacketData;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* OggCodecTest
|
||||
*
|
||||
* An OggCodecTest is capable of determining if an initial packet belongs to a
|
||||
* particular codec. It can instantiate that particular codec.
|
||||
*/
|
||||
class OggCodecTest {
|
||||
protected:
|
||||
OggCodecTest();
|
||||
public:
|
||||
virtual ~OggCodecTest();
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const = 0;
|
||||
virtual OggCodec * InstantiateCodec() const = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* OggCodecRoster
|
||||
*
|
||||
* An OggCodecRoster provides a single location to register OggCodecTests
|
||||
* and retrieve an appropriate OggCodec for an ogg_packet.
|
||||
*/
|
||||
class OggCodecRoster {
|
||||
public:
|
||||
static OggCodecRoster * Roster(status_t * outError = NULL);
|
||||
|
||||
status_t AddCodecTest(const OggCodecTest * wrapper);
|
||||
OggCodec * CodecFor(const ogg_packet & packet) const;
|
||||
|
||||
private:
|
||||
OggCodecRoster();
|
||||
~OggCodecRoster();
|
||||
|
||||
std::vector<const OggCodecTest *> fCodecs;
|
||||
};
|
||||
|
||||
|
||||
} } // BPrivate::media
|
||||
|
||||
using namespace BPrivate::media;
|
||||
|
||||
#endif // _OGG_CODECS_H
|
||||
@@ -1,228 +0,0 @@
|
||||
#include "OggSpeexFormats.h"
|
||||
#include "OggCodecs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
inline size_t
|
||||
AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */)
|
||||
{
|
||||
return (raf->format & 0xf) * (raf->channel_count)
|
||||
* (size_t)((raf->frame_rate * buffer_duration) / 1000000.0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* speex header from libspeex/speex_header.h
|
||||
* also documented at http://www.speex.org/manual/node7.html#SECTION00073000000000000000
|
||||
*/
|
||||
|
||||
typedef struct SpeexHeader {
|
||||
char speex_string[8]; /**< Identifies a Speex bit-stream, always set to "Speex " */
|
||||
char speex_version[20]; /**< Speex version */
|
||||
int speex_version_id; /**< Version for Speex (for checking compatibility) */
|
||||
int header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
|
||||
int rate; /**< Sampling rate used */
|
||||
int mode; /**< Mode used (0 for narrowband, 1 for wideband) */
|
||||
int mode_bitstream_version; /**< Version ID of the bit-stream */
|
||||
int nb_channels; /**< Number of channels encoded */
|
||||
int bitrate; /**< Bit-rate used */
|
||||
int frame_size; /**< Size of frames */
|
||||
int vbr; /**< 1 for a VBR encoding, 0 otherwise */
|
||||
int frames_per_packet; /**< Number of frames stored per Ogg packet */
|
||||
int extra_headers; /**< Number of additional headers after the comments */
|
||||
int reserved1; /**< Reserved for future use, must be zero */
|
||||
int reserved2; /**< Reserved for future use, must be zero */
|
||||
} SpeexHeader;
|
||||
|
||||
|
||||
/*
|
||||
* OggSpeexCodec implementations
|
||||
*/
|
||||
|
||||
class OggSpeexCodec : public OggCodec {
|
||||
public:
|
||||
OggSpeexCodec();
|
||||
virtual ~OggSpeexCodec();
|
||||
|
||||
virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const;
|
||||
virtual status_t HandlePacket(const ogg_packet & packet);
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
private:
|
||||
std::vector<ogg_packet> fMetaDataPackets;
|
||||
unsigned char * fHeaderPacketData;
|
||||
unsigned char * fCommentPacketData;
|
||||
std::vector<uchar *> fExtraPacketData;
|
||||
unsigned int fPacketCount;
|
||||
unsigned int fExtraHeadersField;
|
||||
};
|
||||
|
||||
|
||||
OggSpeexCodec::OggSpeexCodec()
|
||||
{
|
||||
TRACE("OggSpeexCodec::OggSpeexCodec\n");
|
||||
fHeaderPacketData = NULL;
|
||||
fCommentPacketData = NULL;
|
||||
fPacketCount = 0;
|
||||
}
|
||||
|
||||
|
||||
OggSpeexCodec::~OggSpeexCodec()
|
||||
{
|
||||
TRACE("OggSpeexCodec::~OggSpeexCodec\n");
|
||||
delete fHeaderPacketData;
|
||||
delete fCommentPacketData;
|
||||
std::vector<uchar *>::iterator iter;
|
||||
for (iter = fExtraPacketData.begin() ; iter != fExtraPacketData.end() ; iter++) {
|
||||
uchar * data = *iter;
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
OggSpeexCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const
|
||||
{
|
||||
if (packetno < 2) {
|
||||
return true;
|
||||
}
|
||||
if (fPacketCount == 0) {
|
||||
debugger("IsHeaderPacket called for packetno >= 2, before receiving the header packet");
|
||||
return false;
|
||||
}
|
||||
return (packetno < 2 + fExtraHeadersField);
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggSpeexCodec::HandlePacket(const ogg_packet & packet)
|
||||
{
|
||||
TRACE("OggSpeexCodec::HandlePacket\n");
|
||||
if (!IsHeaderPacket(packet, fPacketCount)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
switch (fPacketCount) {
|
||||
case 0: {
|
||||
// header packet
|
||||
if (!packet.b_o_s) {
|
||||
return B_ERROR; // first packet was not beginning of stream
|
||||
}
|
||||
|
||||
// parse header packet, check size against struct minus optional fields
|
||||
if (packet.bytes < 1 + (signed)sizeof(SpeexHeader) - 2*(signed)sizeof(int)) {
|
||||
return B_ERROR;
|
||||
}
|
||||
void * data = &(packet.packet[0]);
|
||||
SpeexHeader * header = (SpeexHeader *)data;
|
||||
|
||||
// store how many extra header packets
|
||||
fExtraHeadersField = header->extra_headers;
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description = speex_description();
|
||||
BMediaFormats formats;
|
||||
if ((formats.InitCheck() != B_OK) ||
|
||||
(formats.GetFormatFor(description, &fMediaFormat) != B_OK)) {
|
||||
fMediaFormat = speex_encoded_media_format();
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
if (header->bitrate > 0) {
|
||||
fMediaFormat.u.encoded_audio.bit_rate = header->bitrate;
|
||||
} else {
|
||||
// TODO: manually compute it where possible
|
||||
}
|
||||
if (header->nb_channels == 1) {
|
||||
fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT;
|
||||
} else {
|
||||
fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
|
||||
}
|
||||
fMediaFormat.u.encoded_audio.output.frame_rate = header->rate;
|
||||
fMediaFormat.u.encoded_audio.output.channel_count = header->nb_channels;
|
||||
// allocate buffer, round up to nearest speex output_length size
|
||||
int buffer_size = AudioBufferSize(&fMediaFormat.u.encoded_audio.output);
|
||||
int output_length = header->frame_size * header->nb_channels *
|
||||
(fMediaFormat.u.encoded_audio.output.format & 0xf);
|
||||
buffer_size = ((buffer_size - 1) / output_length + 1) * output_length;
|
||||
fMediaFormat.u.encoded_audio.output.buffer_size = buffer_size;
|
||||
|
||||
fHeaderPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fHeaderPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fHeaderPacketData;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// comment packet
|
||||
fCommentPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCommentPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCommentPacketData;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// extra headers
|
||||
uchar * extraHeaderData = new unsigned char[packet.bytes];
|
||||
memcpy(extraHeaderData, packet.packet, packet.bytes);
|
||||
fExtraPacketData.push_back(extraHeaderData);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = extraHeaderData;
|
||||
break;
|
||||
}
|
||||
if (fPacketCount == 1 + fExtraHeadersField) {
|
||||
fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets));
|
||||
fInitCheck = B_OK;
|
||||
}
|
||||
fPacketCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggSpeexCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
if (fCurrentFrame == -1) {
|
||||
fCurrentTime = -1;
|
||||
} else {
|
||||
fCurrentTime = (1000000LL * fCurrentFrame)
|
||||
/ (long long)fMediaFormat.u.encoded_audio.output.frame_rate;
|
||||
}
|
||||
status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
*chunkBuffer = fChunkPacket.packet;
|
||||
*chunkSize = fChunkPacket.bytes;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OggSpeexCodecTest
|
||||
*/
|
||||
|
||||
static class OggSpeexCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggSpeexCodecTest() {}
|
||||
virtual ~OggSpeexCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet, "Speex ", 0);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggSpeexCodec();
|
||||
}
|
||||
} ogg_speex_codec_test;
|
||||
@@ -1,327 +0,0 @@
|
||||
#include "OggTheoraFormats.h"
|
||||
#include "OggCodecs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* theora header parsing code from theora/theara.h
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
OC_CS_UNSPECIFIED,
|
||||
OC_CS_ITU_REC_470M,
|
||||
OC_CS_ITU_REC_470BG,
|
||||
} theora_colorspace;
|
||||
|
||||
typedef struct {
|
||||
ogg_uint32_t width;
|
||||
ogg_uint32_t height;
|
||||
ogg_uint32_t frame_width;
|
||||
ogg_uint32_t frame_height;
|
||||
ogg_uint32_t offset_x;
|
||||
ogg_uint32_t offset_y;
|
||||
ogg_uint32_t fps_numerator;
|
||||
ogg_uint32_t fps_denominator;
|
||||
ogg_uint32_t aspect_numerator;
|
||||
ogg_uint32_t aspect_denominator;
|
||||
theora_colorspace colorspace;
|
||||
int target_bitrate;
|
||||
int quality;
|
||||
int quick_p; /* quick encode/decode */
|
||||
|
||||
/* decode only */
|
||||
unsigned char version_major;
|
||||
unsigned char version_minor;
|
||||
unsigned char version_subminor;
|
||||
|
||||
void *codec_setup;
|
||||
|
||||
/* encode only */
|
||||
int dropframes_p;
|
||||
int keyframe_auto_p;
|
||||
ogg_uint32_t keyframe_frequency;
|
||||
ogg_uint32_t keyframe_frequency_force; /* also used for decode init to
|
||||
get granpos shift correct */
|
||||
ogg_uint32_t keyframe_data_target_bitrate;
|
||||
ogg_int32_t keyframe_auto_threshold;
|
||||
ogg_uint32_t keyframe_mindistance;
|
||||
ogg_int32_t noise_sensitivity;
|
||||
ogg_int32_t sharpness;
|
||||
|
||||
} theora_info;
|
||||
|
||||
typedef struct theora_comment{
|
||||
char **user_comments;
|
||||
int *comment_lengths;
|
||||
int comments;
|
||||
char *vendor;
|
||||
|
||||
} theora_comment;
|
||||
|
||||
// based on theora/lib/toplevel.c _theora_unpack_info
|
||||
|
||||
#define theora_read(x,y,z) ( *z = oggpackB_read(x,y) )
|
||||
|
||||
#define OC_BADHEADER -1
|
||||
|
||||
static int _theora_unpack_info(theora_info *ci, oggpack_buffer *opb){
|
||||
long ret;
|
||||
|
||||
theora_read(opb,8,&ret);
|
||||
ci->version_major=(unsigned char)ret;
|
||||
theora_read(opb,8,&ret);
|
||||
ci->version_minor=(unsigned char)ret;
|
||||
theora_read(opb,8,&ret);
|
||||
ci->version_subminor=(unsigned char)ret;
|
||||
|
||||
// if(ci->version_major!=VERSION_MAJOR)return(OC_VERSION);
|
||||
// if(ci->version_minor>VERSION_MINOR)return(OC_VERSION);
|
||||
|
||||
theora_read(opb,16,&ret);
|
||||
ci->width=ret<<4;
|
||||
theora_read(opb,16,&ret);
|
||||
ci->height=ret<<4;
|
||||
theora_read(opb,24,&ret);
|
||||
ci->frame_width=ret;
|
||||
theora_read(opb,24,&ret);
|
||||
ci->frame_height=ret;
|
||||
theora_read(opb,8,&ret);
|
||||
ci->offset_x=ret;
|
||||
theora_read(opb,8,&ret);
|
||||
ci->offset_y=ret;
|
||||
|
||||
theora_read(opb,32,&ret);
|
||||
ci->fps_numerator=ret;
|
||||
theora_read(opb,32,&ret);
|
||||
ci->fps_denominator=ret;
|
||||
theora_read(opb,24,&ret);
|
||||
ci->aspect_numerator=ret;
|
||||
theora_read(opb,24,&ret);
|
||||
ci->aspect_denominator=ret;
|
||||
|
||||
theora_read(opb,8,&ret);
|
||||
ci->colorspace=(theora_colorspace)ret;
|
||||
theora_read(opb,24,&ret);
|
||||
ci->target_bitrate=ret;
|
||||
theora_read(opb,6,&ret);
|
||||
ci->quality=ret=ret;
|
||||
|
||||
theora_read(opb,5,&ret);
|
||||
ci->keyframe_frequency_force=1<<ret;
|
||||
|
||||
/* spare configuration bits */
|
||||
if ( theora_read(opb,5,&ret) == -1 )
|
||||
return (OC_BADHEADER);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* OggTheoraCodec
|
||||
*/
|
||||
|
||||
|
||||
class OggTheoraCodec : public OggCodec {
|
||||
public:
|
||||
OggTheoraCodec();
|
||||
virtual ~OggTheoraCodec();
|
||||
|
||||
virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const;
|
||||
virtual status_t HandlePacket(const ogg_packet & packet);
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
private:
|
||||
std::vector<ogg_packet> fMetaDataPackets;
|
||||
unsigned char * fHeaderPacketData;
|
||||
unsigned char * fCommentPacketData;
|
||||
unsigned char * fCodecPacketData;
|
||||
unsigned int fPacketCount;
|
||||
};
|
||||
|
||||
|
||||
OggTheoraCodec::OggTheoraCodec()
|
||||
{
|
||||
TRACE("OggTheoraCodec::OggTheoraCodec\n");
|
||||
fHeaderPacketData = NULL;
|
||||
fCommentPacketData = NULL;
|
||||
fCodecPacketData = NULL;
|
||||
fPacketCount = 0;
|
||||
}
|
||||
|
||||
|
||||
OggTheoraCodec::~OggTheoraCodec()
|
||||
{
|
||||
delete fHeaderPacketData;
|
||||
delete fCommentPacketData;
|
||||
delete fCodecPacketData;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
OggTheoraCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const
|
||||
{
|
||||
oggpack_buffer opb;
|
||||
oggpackB_readinit(&opb, packet.packet, packet.bytes);
|
||||
uint packtype = oggpackB_read(&opb, 8);
|
||||
return (packetno == 0 && packtype == 0x80)
|
||||
|| (packetno == 1 && packtype == 0x81)
|
||||
|| (packetno == 2 && packtype == 0x82);
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTheoraCodec::HandlePacket(const ogg_packet & packet)
|
||||
{
|
||||
TRACE("OggTheoraCodec::HandlePacket\n");
|
||||
if (!IsHeaderPacket(packet, fPacketCount)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
switch (fPacketCount) {
|
||||
case 0: {
|
||||
// header packet
|
||||
if (!packet.b_o_s) {
|
||||
return B_ERROR; // first packet was not beginning of stream
|
||||
}
|
||||
|
||||
// parse header packet
|
||||
// based on libvorbis/info.c vorbis_synthesis_headerin(...)
|
||||
oggpack_buffer opb;
|
||||
oggpackB_readinit(&opb, packet.packet, packet.bytes);
|
||||
// discard packet type (already validated in IsHeaderPacket)
|
||||
oggpackB_read(&opb, 8);
|
||||
// discard theora string
|
||||
for (uint i = 0 ; i < sizeof("theora") - 1 ; i++) {
|
||||
oggpackB_read(&opb, 8);
|
||||
}
|
||||
theora_info info;
|
||||
if (_theora_unpack_info(&info, &opb) != 0) {
|
||||
return B_ERROR; // couldn't unpack info
|
||||
}
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description = theora_description();
|
||||
BMediaFormats formats;
|
||||
if ((formats.InitCheck() != B_OK) ||
|
||||
(formats.GetFormatFor(description, &fMediaFormat) != B_OK)) {
|
||||
fMediaFormat = theora_encoded_media_format();
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
// sanity check
|
||||
if (info.fps_denominator != 0) {
|
||||
fMediaFormat.u.encoded_video.output.field_rate =
|
||||
(double)info.fps_numerator / (double)info.fps_denominator;
|
||||
}
|
||||
fMediaFormat.u.encoded_video.output.first_active = info.offset_y;
|
||||
fMediaFormat.u.encoded_video.output.last_active = info.offset_y + info.height;
|
||||
fMediaFormat.u.encoded_video.output.pixel_width_aspect = info.aspect_numerator;
|
||||
fMediaFormat.u.encoded_video.output.pixel_height_aspect = info.aspect_denominator;
|
||||
fMediaFormat.u.encoded_video.output.display.line_width = info.frame_width;
|
||||
fMediaFormat.u.encoded_video.output.display.line_count = info.frame_height;
|
||||
fMediaFormat.u.encoded_video.avg_bit_rate = info.target_bitrate; // advisory only
|
||||
fMediaFormat.u.encoded_video.frame_size = info.width * info.height;
|
||||
// TODO: wring more info out of the headers
|
||||
|
||||
fHeaderPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fHeaderPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fHeaderPacketData;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// comment packet
|
||||
fCommentPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCommentPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCommentPacketData;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// codec setup packet
|
||||
fCodecPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCodecPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCodecPacketData;
|
||||
|
||||
fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets));
|
||||
fInitCheck = B_OK;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// huh?
|
||||
break;
|
||||
}
|
||||
fPacketCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTheoraCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
if (streaming) {
|
||||
if (fChunkPacket.granulepos == -1) {
|
||||
fCurrentTime = -1;
|
||||
} else {
|
||||
fCurrentTime = (bigtime_t) ((1000000LL * fCurrentFrame)
|
||||
/ (fMediaFormat.u.encoded_video.output.field_rate *
|
||||
fMediaFormat.u.encoded_video.output.interlace));
|
||||
}
|
||||
} else {
|
||||
debugger("kaboom");
|
||||
}
|
||||
status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
oggpack_buffer opb;
|
||||
oggpackB_readinit(&opb, fChunkPacket.packet, fChunkPacket.bytes);
|
||||
// Read 1 bit. This must be 0 for all data packets.
|
||||
uint packtype = oggpackB_read(&opb, 1);
|
||||
if (packtype != 0) {
|
||||
// packet was not a data packet
|
||||
return B_ERROR;
|
||||
}
|
||||
// read 1 bit (frametype) this is 0 for keyframes, 1 for P frames.
|
||||
uint keyframe = oggpackB_read(&opb, 1);
|
||||
// read 6 bits (quality mask) this is the quality index for this frame.
|
||||
// uint quality = oggpackB_read(&opb, 6);
|
||||
/*
|
||||
if the frametype is 0 (keyframe):
|
||||
read 1 bit (keyframetype) this indicates the coding variety of the keyframe.
|
||||
read 2 bits (spare) these bits are unused.
|
||||
*/
|
||||
mediaHeader->u.encoded_video.field_number = fCurrentFrame;
|
||||
mediaHeader->u.encoded_video.field_flags = (keyframe == 1 ? B_MEDIA_KEY_FRAME : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OggTheoraCodecWrapper
|
||||
*/
|
||||
|
||||
static class OggTheoraCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggTheoraCodecTest() {}
|
||||
virtual ~OggTheoraCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet, "theora", 1);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggTheoraCodec();
|
||||
}
|
||||
} ogg_theora_codec_test;
|
||||
@@ -1,465 +0,0 @@
|
||||
#include "OggTobiasFormats.h"
|
||||
#include "OggCodecs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* tobias header structs from http://tobias.everwicked.com/packfmt.htm
|
||||
*/
|
||||
|
||||
|
||||
typedef struct tobias_stream_header_video
|
||||
{
|
||||
ogg_int32_t width;
|
||||
ogg_int32_t height;
|
||||
} tobias_stream_header_video;
|
||||
|
||||
|
||||
typedef struct tobias_stream_header_audio
|
||||
{
|
||||
ogg_int16_t channels;
|
||||
ogg_int16_t blockalign;
|
||||
ogg_int32_t avgbytespersec;
|
||||
} tobias_stream_header_audio;
|
||||
|
||||
|
||||
typedef struct tobias_stream_header
|
||||
{
|
||||
char streamtype[8];
|
||||
char subtype[4];
|
||||
|
||||
ogg_int32_t size; // size of the structure
|
||||
|
||||
ogg_int64_t time_unit; // in reference time (100 ns units)
|
||||
ogg_int64_t samples_per_unit;
|
||||
ogg_int32_t default_len; // in media time
|
||||
|
||||
ogg_int32_t buffersize;
|
||||
ogg_int16_t bits_per_sample;
|
||||
|
||||
union {
|
||||
// Video specific
|
||||
tobias_stream_header_video video;
|
||||
// Audio specific
|
||||
tobias_stream_header_audio audio;
|
||||
};
|
||||
} tobias_stream_header;
|
||||
|
||||
|
||||
/*
|
||||
static int64
|
||||
count_granules(const ogg_packet & packet)
|
||||
{
|
||||
// thanks to Marcus:
|
||||
int lenbytes = ((packet.packet[0] & 0xc0) >> 6) | ((packet.packet[0] & 0x02) << 1);
|
||||
if (lenbytes == 0) {
|
||||
return 1;
|
||||
}
|
||||
int64 granules = 0;
|
||||
int count = 0;
|
||||
// fprintf(stderr, "lenbytes = %d, ", lenbytes);
|
||||
while (lenbytes-- > 0) {
|
||||
granules += ((uint8*)packet.packet)[count+1] * (1LL << 8 * count);
|
||||
count++;
|
||||
}
|
||||
// fprintf(stderr, "granules = %lld\n", granules);
|
||||
return granules;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* OggTobiasCodec
|
||||
*/
|
||||
|
||||
|
||||
class OggTobiasCodec : public OggCodec {
|
||||
public:
|
||||
OggTobiasCodec() {}
|
||||
virtual ~OggTobiasCodec() {}
|
||||
|
||||
virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const;
|
||||
virtual status_t HandlePacket(const ogg_packet & packet);
|
||||
|
||||
protected:
|
||||
virtual status_t HeaderToFormat(const tobias_stream_header & header) = 0;
|
||||
|
||||
std::vector<ogg_packet> fMetaDataPackets;
|
||||
unsigned char * fHeaderPacketData;
|
||||
unsigned char * fCommentPacketData;
|
||||
unsigned int fPacketCount;
|
||||
};
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
OggTobiasCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const
|
||||
{
|
||||
oggpack_buffer opb;
|
||||
oggpack_readinit(&opb, packet.packet, packet.bytes);
|
||||
uint packtype = oggpack_read(&opb, 8);
|
||||
return (packetno == 0 && packtype == 0x01)
|
||||
|| (packetno == 1 && packtype == 0x03);
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasCodec::HandlePacket(const ogg_packet & packet)
|
||||
{
|
||||
TRACE("OggTobiasCodec::HandlePacket\n");
|
||||
if (!IsHeaderPacket(packet, fPacketCount)) {
|
||||
return B_ERROR;
|
||||
}
|
||||
switch (fPacketCount) {
|
||||
case 0: {
|
||||
if (!packet.b_o_s) {
|
||||
return B_ERROR; // first packet was not beginning of stream
|
||||
}
|
||||
|
||||
// parse header packet
|
||||
if (packet.bytes < 1+(signed)sizeof(tobias_stream_header)) {
|
||||
return B_ERROR;
|
||||
}
|
||||
void * data = &(packet.packet[1]);
|
||||
tobias_stream_header * header = (tobias_stream_header *)data;
|
||||
|
||||
status_t result = HeaderToFormat(*header);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// initialize codec info
|
||||
fMediaFormat.user_data_type = B_CODEC_TYPE_INFO;
|
||||
strncpy((char*)fMediaFormat.user_data, header->subtype, 4);
|
||||
|
||||
// save the header packet for use in meta data
|
||||
fHeaderPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fHeaderPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fHeaderPacketData;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// save the comment packet for use in meta data
|
||||
fCommentPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCommentPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCommentPacketData;
|
||||
|
||||
// ready for showtime
|
||||
fInitCheck = B_OK;
|
||||
fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// huh?
|
||||
break;
|
||||
}
|
||||
fPacketCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OggTobiasVideoCodec
|
||||
*/
|
||||
|
||||
|
||||
class OggTobiasVideoCodec : public OggTobiasCodec {
|
||||
public:
|
||||
OggTobiasVideoCodec();
|
||||
virtual ~OggTobiasVideoCodec();
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
|
||||
protected:
|
||||
virtual status_t HeaderToFormat(const tobias_stream_header & header);
|
||||
};
|
||||
|
||||
|
||||
OggTobiasVideoCodec::OggTobiasVideoCodec()
|
||||
{
|
||||
TRACE("OggTobiasVideoCodec::OggTobiasVideoCodec\n");
|
||||
}
|
||||
|
||||
|
||||
OggTobiasVideoCodec::~OggTobiasVideoCodec()
|
||||
{
|
||||
TRACE("OggTobiasVideoCodec::~OggTobiasVideoCodec\n");
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasVideoCodec::HeaderToFormat(const tobias_stream_header & header)
|
||||
{
|
||||
TRACE("OggTobiasVideoCodec::HeaderToFormat\n");
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description = tobias_video_description();
|
||||
description.u.avi.codec = header.subtype[3] << 24 | header.subtype[2] << 16
|
||||
| header.subtype[1] << 8 | header.subtype[0];
|
||||
BMediaFormats formats;
|
||||
if ((formats.InitCheck() != B_OK) ||
|
||||
(formats.GetFormatFor(description, &fMediaFormat) != B_OK)) {
|
||||
fMediaFormat = tobias_video_encoded_media_format();
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
fMediaFormat.u.encoded_video.frame_size
|
||||
= header.video.width * header.video.height;
|
||||
fMediaFormat.u.encoded_video.output.field_rate = 10000000.0 / header.time_unit;
|
||||
fMediaFormat.u.encoded_video.output.interlace = 1;
|
||||
fMediaFormat.u.encoded_video.output.first_active = 0;
|
||||
fMediaFormat.u.encoded_video.output.last_active = header.video.height - 1;
|
||||
fMediaFormat.u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT;
|
||||
fMediaFormat.u.encoded_video.output.pixel_width_aspect = 1;
|
||||
fMediaFormat.u.encoded_video.output.pixel_height_aspect = 1;
|
||||
fMediaFormat.u.encoded_video.output.display.line_width = header.video.width;
|
||||
fMediaFormat.u.encoded_video.output.display.line_count = header.video.height;
|
||||
fMediaFormat.u.encoded_video.output.display.bytes_per_row = 0;
|
||||
fMediaFormat.u.encoded_video.output.display.pixel_offset = 0;
|
||||
fMediaFormat.u.encoded_video.output.display.line_offset = 0;
|
||||
fMediaFormat.u.encoded_video.output.display.flags = 0;
|
||||
|
||||
// TODO: wring more info out of the headers?
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasVideoCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
if (streaming) {
|
||||
if (fChunkPacket.granulepos == -1) {
|
||||
int new_granulepos = fOldGranulePos + 1;
|
||||
fCurrentFrame = fOldFrame + GranulesToFrames(new_granulepos)
|
||||
- GranulesToFrames(fOldGranulePos);
|
||||
fOldFrame = fCurrentFrame;
|
||||
fOldGranulePos = new_granulepos;
|
||||
} else {
|
||||
// fprintf(stderr, "granulepos: %lld ", fChunkPacket.granulepos);
|
||||
// fprintf(stderr, "out: frame = %lld\n", fCurrentFrame);
|
||||
}
|
||||
} else {
|
||||
debugger("kaboom");
|
||||
}
|
||||
fCurrentTime = (bigtime_t) ((1000000LL * fCurrentFrame)
|
||||
/ (fMediaFormat.u.encoded_video.output.field_rate *
|
||||
fMediaFormat.u.encoded_video.output.interlace));
|
||||
// fprintf(stderr, "video in: frame = %lld time = %lld\n", fCurrentFrame, fCurrentTime);
|
||||
int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1);
|
||||
*chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]);
|
||||
*chunkSize = fChunkPacket.bytes-(lenbytes+1);
|
||||
bool keyframe = (fChunkPacket.packet[0] & (1 << 3));
|
||||
mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// OggTobiasVideoCodecTest
|
||||
|
||||
static class OggTobiasVideoCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggTobiasVideoCodecTest() {}
|
||||
virtual ~OggTobiasVideoCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet, "video", 1);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggTobiasVideoCodec();
|
||||
}
|
||||
} ogg_tobias_video_test;
|
||||
|
||||
|
||||
/*
|
||||
* OggTobiasAudioCodec
|
||||
*/
|
||||
|
||||
|
||||
class OggTobiasAudioCodec : public OggTobiasCodec {
|
||||
public:
|
||||
OggTobiasAudioCodec();
|
||||
virtual ~OggTobiasAudioCodec();
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
|
||||
protected:
|
||||
virtual status_t HeaderToFormat(const tobias_stream_header & header);
|
||||
};
|
||||
|
||||
|
||||
OggTobiasAudioCodec::OggTobiasAudioCodec()
|
||||
{
|
||||
TRACE("OggTobiasAudioCodec::OggTobiasAudioCodec\n");
|
||||
}
|
||||
|
||||
|
||||
OggTobiasAudioCodec::~OggTobiasAudioCodec()
|
||||
{
|
||||
TRACE("OggTobiasAudioCodec::~OggTobiasAudioCodec\n");
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasAudioCodec::HeaderToFormat(const tobias_stream_header & header)
|
||||
{
|
||||
TRACE("OggTobiasAudioCodec::HeaderToFormat\n");
|
||||
|
||||
debugger("get_audio_format");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasAudioCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
if (fCurrentFrame == -1) {
|
||||
fCurrentFrame = fOldFrame + 1;
|
||||
fOldFrame = fCurrentFrame;
|
||||
fOldGranulePos++;
|
||||
}
|
||||
fCurrentTime = (bigtime_t) (1000000LL * fCurrentFrame)
|
||||
/ (long long)fMediaFormat.u.encoded_audio.output.frame_rate;
|
||||
status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1);
|
||||
*chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]);
|
||||
*chunkSize = fChunkPacket.bytes-(lenbytes+1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// OggTobiasAudioCodecTest
|
||||
|
||||
static class OggTobiasAudioCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggTobiasAudioCodecTest() {}
|
||||
virtual ~OggTobiasAudioCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet, "audio", 1);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggTobiasAudioCodec();
|
||||
}
|
||||
} ogg_tobias_audio_test;
|
||||
|
||||
|
||||
/*
|
||||
* OggTobiasTextCodec
|
||||
*/
|
||||
|
||||
class OggTobiasTextCodec : public OggTobiasCodec {
|
||||
public:
|
||||
OggTobiasTextCodec();
|
||||
virtual ~OggTobiasTextCodec();
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
|
||||
protected:
|
||||
virtual status_t HeaderToFormat(const tobias_stream_header & header);
|
||||
};
|
||||
|
||||
|
||||
OggTobiasTextCodec::OggTobiasTextCodec()
|
||||
{
|
||||
TRACE("OggTobiasTextCodec::OggTobiasTextCodec\n");
|
||||
}
|
||||
|
||||
|
||||
OggTobiasTextCodec::~OggTobiasTextCodec()
|
||||
{
|
||||
TRACE("OggTobiasTextCodec::~OggTobiasTextCodec\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasTextCodec::HeaderToFormat(const tobias_stream_header & header)
|
||||
{
|
||||
TRACE("OggTobiasTextCodec::HeaderToFormat\n");
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description = tobias_text_description();
|
||||
BMediaFormats formats;
|
||||
if ((formats.InitCheck() != B_OK) ||
|
||||
(formats.GetFormatFor(description, &fMediaFormat) != B_OK)) {
|
||||
fMediaFormat = tobias_text_encoded_media_format();
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
(void)header;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggTobiasTextCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
if (fCurrentFrame == -1) {
|
||||
fCurrentFrame = fOldFrame + 1;
|
||||
fOldFrame = fCurrentFrame;
|
||||
fOldGranulePos++;
|
||||
}
|
||||
fCurrentTime = (bigtime_t) (1000000LL * fCurrentFrame)
|
||||
/ (long long)fMediaFormat.u.encoded_audio.output.frame_rate;
|
||||
status_t result = OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
if (result != B_OK) {
|
||||
return result;
|
||||
}
|
||||
// thanks to Marcus:
|
||||
int lenbytes = ((fChunkPacket.packet[0] & 0xc0) >> 6) | ((fChunkPacket.packet[0] & 0x02) << 1);
|
||||
*chunkBuffer = (void*)&(fChunkPacket.packet[lenbytes+1]);
|
||||
*chunkSize = fChunkPacket.bytes-(lenbytes+1);
|
||||
bool keyframe = (fChunkPacket.packet[0] & (1 << 3));
|
||||
mediaHeader->u.encoded_video.field_flags = (keyframe ? B_MEDIA_KEY_FRAME : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// OggTobiasTextCodecTest
|
||||
|
||||
static class OggTobiasTextCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggTobiasTextCodecTest() {}
|
||||
virtual ~OggTobiasTextCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet,"text",1);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggTobiasTextCodec();
|
||||
}
|
||||
} ogg_tobias_text_test;
|
||||
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
#include "OggVorbisFormats.h"
|
||||
#include "OggCodecs.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE_THIS 1
|
||||
#if TRACE_THIS
|
||||
#define TRACE printf
|
||||
#else
|
||||
#define TRACE(a...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
inline size_t
|
||||
AudioBufferSize(media_raw_audio_format * raf, bigtime_t buffer_duration = 50000 /* 50 ms */)
|
||||
{
|
||||
return (raf->format & 0xf) * (raf->channel_count)
|
||||
* (size_t)((raf->frame_rate * buffer_duration) / 1000000.0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* vorbis header parsing code from libvorbis/info.c
|
||||
*/
|
||||
|
||||
|
||||
typedef struct vorbis_info{
|
||||
int version;
|
||||
int channels;
|
||||
long rate;
|
||||
|
||||
/* The below bitrate declarations are *hints*.
|
||||
Combinations of the three values carry the following implications:
|
||||
|
||||
all three set to the same value:
|
||||
implies a fixed rate bitstream
|
||||
only nominal set:
|
||||
implies a VBR stream that averages the nominal bitrate. No hard
|
||||
upper/lower limit
|
||||
upper and or lower set:
|
||||
implies a VBR bitstream that obeys the bitrate limits. nominal
|
||||
may also be set to give a nominal rate.
|
||||
none set:
|
||||
the coder does not care to speculate.
|
||||
*/
|
||||
|
||||
long bitrate_upper;
|
||||
long bitrate_nominal;
|
||||
long bitrate_lower;
|
||||
long bitrate_window;
|
||||
|
||||
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);
|
||||
if (vi->version != 0) {
|
||||
return -1;
|
||||
}
|
||||
vi->channels = oggpack_read(opb, 8);
|
||||
vi->rate = oggpack_read(opb, 32);
|
||||
vi->bitrate_upper = oggpack_read(opb, 32);
|
||||
vi->bitrate_nominal = oggpack_read(opb, 32);
|
||||
vi->bitrate_lower = oggpack_read(opb, 32);
|
||||
long blocksizes0 = oggpack_read(opb, 4);
|
||||
long blocksizes1 = oggpack_read(opb, 4);
|
||||
if (vi->rate < 1) {
|
||||
return -1;
|
||||
}
|
||||
if (vi->channels < 1) {
|
||||
return -1;
|
||||
}
|
||||
if (blocksizes0 < 8) {
|
||||
return -1;
|
||||
}
|
||||
if (blocksizes1 < blocksizes0) {
|
||||
return -1;
|
||||
}
|
||||
if (oggpack_read(opb, 1) != 1) {
|
||||
return -1;
|
||||
} /* EOP check */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OggVorbisCodec
|
||||
*/
|
||||
|
||||
|
||||
class OggVorbisCodec : public OggCodec {
|
||||
public:
|
||||
OggVorbisCodec();
|
||||
virtual ~OggVorbisCodec();
|
||||
|
||||
virtual bool IsHeaderPacket(const ogg_packet & packet, uint packetno) const;
|
||||
virtual status_t HandlePacket(const ogg_packet & packet);
|
||||
|
||||
virtual status_t GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader);
|
||||
private:
|
||||
std::vector<ogg_packet> fMetaDataPackets;
|
||||
unsigned char * fHeaderPacketData;
|
||||
unsigned char * fCommentPacketData;
|
||||
unsigned char * fCodebookPacketData;
|
||||
unsigned int fPacketCount;
|
||||
};
|
||||
|
||||
|
||||
OggVorbisCodec::OggVorbisCodec()
|
||||
{
|
||||
TRACE("OggVorbisCodec::OggVorbisCodec\n");
|
||||
fHeaderPacketData = NULL;
|
||||
fCommentPacketData = NULL;
|
||||
fCodebookPacketData = NULL;
|
||||
fPacketCount = 0;
|
||||
}
|
||||
|
||||
|
||||
OggVorbisCodec::~OggVorbisCodec()
|
||||
{
|
||||
delete fHeaderPacketData;
|
||||
delete fCommentPacketData;
|
||||
delete fCodebookPacketData;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ bool
|
||||
OggVorbisCodec::IsHeaderPacket(const ogg_packet & packet, uint packetno) const
|
||||
{
|
||||
oggpack_buffer opb;
|
||||
oggpack_readinit(&opb, packet.packet, packet.bytes);
|
||||
uint packtype = oggpack_read(&opb, 8);
|
||||
return (packetno == 0 && packtype == 0x01)
|
||||
|| (packetno == 1 && packtype == 0x03)
|
||||
|| (packetno == 2 && packtype == 0x05);
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggVorbisCodec::HandlePacket(const ogg_packet & packet)
|
||||
{
|
||||
TRACE("OggVorbisCodec::HandlePacket\n");
|
||||
if (!IsHeaderPacket(packet, fPacketCount)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
switch (fPacketCount) {
|
||||
case 0: {
|
||||
// header packet
|
||||
if (!packet.b_o_s) {
|
||||
return B_ERROR; // first packet was not beginning of stream
|
||||
}
|
||||
|
||||
// parse header packet
|
||||
// based on libvorbis/info.c vorbis_synthesis_headerin(...)
|
||||
oggpack_buffer opb;
|
||||
oggpack_readinit(&opb, packet.packet, packet.bytes);
|
||||
// discard packet type (already validated in IsHeaderPacket)
|
||||
oggpack_read(&opb, 8);
|
||||
// discard vorbis string
|
||||
for (uint i = 0 ; i < sizeof("vorbis") - 1 ; i++) {
|
||||
oggpack_read(&opb, 8);
|
||||
}
|
||||
vorbis_info info;
|
||||
if (_vorbis_unpack_info(&info, &opb) != 0) {
|
||||
return B_ERROR; // couldn't unpack info
|
||||
}
|
||||
|
||||
// get the format for the description
|
||||
media_format_description description = vorbis_description();
|
||||
BMediaFormats formats;
|
||||
if ((formats.InitCheck() != B_OK) ||
|
||||
(formats.GetFormatFor(description, &fMediaFormat) != B_OK)) {
|
||||
fMediaFormat = vorbis_encoded_media_format();
|
||||
}
|
||||
|
||||
// fill out format from header packet
|
||||
if (info.bitrate_nominal > 0) {
|
||||
fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_nominal;
|
||||
} else if (info.bitrate_upper > 0) {
|
||||
fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_upper;
|
||||
} else if (info.bitrate_lower > 0) {
|
||||
fMediaFormat.u.encoded_audio.bit_rate = info.bitrate_lower;
|
||||
}
|
||||
if (info.channels == 1) {
|
||||
fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT;
|
||||
} else {
|
||||
fMediaFormat.u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
|
||||
}
|
||||
fMediaFormat.u.encoded_audio.output.frame_rate = (float)info.rate;
|
||||
fMediaFormat.u.encoded_audio.output.channel_count = info.channels;
|
||||
fMediaFormat.u.encoded_audio.output.buffer_size
|
||||
= AudioBufferSize(&fMediaFormat.u.encoded_audio.output);
|
||||
|
||||
fHeaderPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fHeaderPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fHeaderPacketData;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// comment packet
|
||||
fCommentPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCommentPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCommentPacketData;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// codebook packet
|
||||
fCodebookPacketData = new unsigned char[packet.bytes];
|
||||
memcpy(fCodebookPacketData, packet.packet, packet.bytes);
|
||||
fMetaDataPackets.push_back(packet);
|
||||
fMetaDataPackets[fPacketCount].packet = fCodebookPacketData;
|
||||
|
||||
fInitCheck = B_OK;
|
||||
fMediaFormat.SetMetaData(&fMetaDataPackets,sizeof(fMetaDataPackets));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// huh?
|
||||
break;
|
||||
}
|
||||
fPacketCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* virtual */ status_t
|
||||
OggVorbisCodec::GetChunk(ogg_stream_state * stream, bool streaming,
|
||||
void **chunkBuffer, int32 *chunkSize,
|
||||
media_header *mediaHeader)
|
||||
{
|
||||
if (fCurrentFrame == -1) {
|
||||
fCurrentTime = -1;
|
||||
} else {
|
||||
fCurrentTime = (1000000LL * fCurrentFrame)
|
||||
/ (long long)fMediaFormat.u.encoded_audio.output.frame_rate;
|
||||
}
|
||||
return OggCodec::GetChunk(stream, streaming, chunkBuffer, chunkSize, mediaHeader);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OggVorbisCodecTest
|
||||
*/
|
||||
|
||||
static class OggVorbisCodecTest : public OggCodecTest {
|
||||
public:
|
||||
OggVorbisCodecTest() {}
|
||||
virtual ~OggVorbisCodecTest() {}
|
||||
virtual bool RecognizesInitialPacket(const ogg_packet & packet) const
|
||||
{
|
||||
return findIdentifier(packet, "vorbis", 1);
|
||||
}
|
||||
virtual OggCodec * InstantiateCodec() const
|
||||
{
|
||||
return new OggVorbisCodec();
|
||||
}
|
||||
} ogg_vorbis_codec_test;
|
||||
Reference in New Issue
Block a user