From 1c828415cfbe92acf9a14ba78833703ea418aef4 Mon Sep 17 00:00:00 2001 From: shatty Date: Sat, 13 Dec 2003 20:12:32 +0000 Subject: [PATCH] ogg reader plugin based on mp3 git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5663 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/ogg/Jamfile | 14 + .../media/plugins/ogg/OggReaderPlugin.cpp | 874 +++++++++ .../media/plugins/ogg/OggReaderPlugin.h | 82 + src/add-ons/media/plugins/ogg/libogg/COPYING | 28 + src/add-ons/media/plugins/ogg/libogg/Jamfile | 6 + .../media/plugins/ogg/libogg/bitwise.c | 452 +++++ .../media/plugins/ogg/libogg/config_types.h | 10 + .../media/plugins/ogg/libogg/framing.c | 1746 +++++++++++++++++ .../media/plugins/ogg/libogg/ogg/ogg.h | 184 ++ .../media/plugins/ogg/libogg/ogg/os_types.h | 84 + 10 files changed, 3480 insertions(+) create mode 100644 src/add-ons/media/plugins/ogg/Jamfile create mode 100644 src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp create mode 100644 src/add-ons/media/plugins/ogg/OggReaderPlugin.h create mode 100644 src/add-ons/media/plugins/ogg/libogg/COPYING create mode 100644 src/add-ons/media/plugins/ogg/libogg/Jamfile create mode 100644 src/add-ons/media/plugins/ogg/libogg/bitwise.c create mode 100644 src/add-ons/media/plugins/ogg/libogg/config_types.h create mode 100644 src/add-ons/media/plugins/ogg/libogg/framing.c create mode 100644 src/add-ons/media/plugins/ogg/libogg/ogg/ogg.h create mode 100644 src/add-ons/media/plugins/ogg/libogg/ogg/os_types.h diff --git a/src/add-ons/media/plugins/ogg/Jamfile b/src/add-ons/media/plugins/ogg/Jamfile new file mode 100644 index 0000000000..6ce3aee2fe --- /dev/null +++ b/src/add-ons/media/plugins/ogg/Jamfile @@ -0,0 +1,14 @@ +SubDir OBOS_TOP src add-ons media plugins ogg ; + +UsePrivateHeaders media ; + +SubDirHdrs $(SUBDIR) libogg ogg ; + +Addon ogg : media plugins : + OggReaderPlugin.cpp + : false : libogg.a +; + +LinkSharedOSLibs ogg : be libmedia.so ; + +SubInclude OBOS_TOP src add-ons media plugins ogg libogg ; diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp new file mode 100644 index 0000000000..b84c1b34ba --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.cpp @@ -0,0 +1,874 @@ +#include +#include +#include +#include +#include +#include +#include +#include "OggReaderPlugin.h" + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE ((void)0) +#endif + +// bit_rate_table[mpeg_version_index][layer_index][bitrate_index] +static const int bit_rate_table[4][4][16] = +{ + { // mpeg version 2.5 + { }, // undefined layer + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 3 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // layer 1 + }, + { // undefined version + { }, + { }, + { }, + { } + }, + { // mpeg version 2 + { }, + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } + }, + { // mpeg version 1 + { }, + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0} + } +}; + +// b_mpeg_id_table[mpeg_version_index][layer_index] +static const int32 b_mpeg_id_table[4][4] = +{ + { 0, B_MPEG_2_5_AUDIO_LAYER_3, B_MPEG_2_5_AUDIO_LAYER_2, B_MPEG_2_5_AUDIO_LAYER_1 }, + { 0, 0, 0, 0 }, + { 0, B_MPEG_2_AUDIO_LAYER_3, B_MPEG_2_AUDIO_LAYER_2, B_MPEG_2_AUDIO_LAYER_1 }, + { 0, B_MPEG_1_AUDIO_LAYER_3, B_MPEG_1_AUDIO_LAYER_2, B_MPEG_1_AUDIO_LAYER_1 }, +}; + +// frame_rate_table[mpeg_version_index][sampling_rate_index] +static const int frame_rate_table[4][4] = +{ + { 11025, 12000, 8000, 0}, // mpeg version 2.5 + { 0, 0, 0, 0 }, + { 22050, 24000, 16000, 0}, // mpeg version 2 + { 44100, 48000, 32000, 0} // mpeg version 1 +}; + +// name_table[mpeg_version_index][layer_index] +static const char * name_table[4][4] = +{ + { 0, "MPEG 2.5 Audio Layer 3", "MPEG 2.5 Audio Layer 2", "MPEG 2.5 Audio Layer 1" }, + { 0, 0, 0, 0 }, + { 0, "MPEG 2 Audio Layer 3", "MPEG 2 Audio Layer 2", "MPEG 2 Audio Layer 1" }, + { 0, "MPEG 1 Audio Layer 3", "MPEG 1 Audio Layer 2", "MPEG 1 Audio Layer 1" }, +}; + +// frame_sample_count_table[layer_index] +static const int frame_sample_count_table[4] = { 0, 1152, 1152, 384 }; + +static const int MAX_CHUNK_SIZE = 5200; + +struct mp3data +{ + int64 position; + char * chunkBuffer; + + int64 duration; // usec + + int32 framesPerFrame; // PCM frames in each mpeg frame + int64 frameCount; // PCM frames + int32 frameRate; + int64 framePosition; + + media_format format; +}; + +struct oggReader::xing_vbr_info +{ + int32 frameRate; + int64 encodedFramesCount; + int64 byteCount; + int32 vbrScale; + bool hasSeekpoints; + uint8 seekpoints[100]; + + int64 duration; // usec + int64 frameCount; // PCM frames +}; + +struct oggReader::fhg_vbr_info +{ +}; + + +oggReader::oggReader() + : fXingVbrInfo(0), + fFhgVbrInfo(0) +{ + TRACE("oggReader::oggReader\n"); +} + + +oggReader::~oggReader() +{ + delete fXingVbrInfo; + delete fFhgVbrInfo; +} + + +const char * +oggReader::Copyright() +{ + return "mp3 reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen"; +} + + +status_t +oggReader::Sniff(int32 *streamCount) +{ + TRACE("oggReader::Sniff\n"); + + fSeekableSource = dynamic_cast(Reader::Source()); + if (!fSeekableSource) { + TRACE("oggReader::Sniff: non seekable sources not supported\n"); + return B_ERROR; + } + + fFileSize = Source()->Seek(0, SEEK_END); + + TRACE("oggReader::Sniff: file size is %Ld bytes\n", fFileSize); + + if (!IsMp3File()) { + TRACE("oggReader::Sniff: non recognized as mp3 file\n"); + return B_ERROR; + } + + TRACE("oggReader::Sniff: looks like an mp3 file\n"); + + if (!ParseFile()) { + TRACE("oggReader::Sniff: parsing file failed\n"); + return B_ERROR; + } + + *streamCount = 1; + return B_OK; +} + + +void +oggReader::GetFileFormatInfo(media_file_format *mff) +{ + mff->capabilities = media_file_format::B_READABLE + | media_file_format::B_KNOWS_ENCODED_AUDIO + | media_file_format::B_IMPERFECTLY_SEEKABLE; + mff->family = B_MPEG_FORMAT_FAMILY; + mff->version = 100; + strcpy(mff->mime_type, "audio/mpeg"); + strcpy(mff->file_extension, "mp3"); + + uint8 header[4]; + Source()->ReadAt(fDataStart, header, sizeof(header)); + int mpeg_version_index = (header[1] >> 3) & 0x03; + int layer_index = (header[1] >> 1) & 0x03; + strcpy(mff->short_name, name_table[mpeg_version_index][layer_index]); + strcpy(mff->pretty_name, name_table[mpeg_version_index][layer_index]); +} + + +status_t +oggReader::AllocateCookie(int32 streamNumber, void **cookie) +{ + TRACE("oggReader::AllocateCookie\n"); + + mp3data *data = new mp3data; + data->chunkBuffer = new char[MAX_CHUNK_SIZE]; + data->position = 0; + + uint8 header[4]; + Source()->ReadAt(fDataStart, header, sizeof(header)); + int mpeg_version_index = (header[1] >> 3) & 0x03; + int layer_index = (header[1] >> 1) & 0x03; + + int bit_rate; + int frame_size; + + if (fXingVbrInfo && fXingVbrInfo->frameCount != -1 && fXingVbrInfo->duration != -1) { + TRACE("oggReader::AllocateCookie: using timing info from VBR header\n"); + bit_rate = (fXingVbrInfo->byteCount * 8 * 1000000) / fXingVbrInfo->duration; // average bit rate + frame_size = fXingVbrInfo->byteCount / fXingVbrInfo->encodedFramesCount; // average frame size + data->duration = fXingVbrInfo->duration; + data->frameCount = fXingVbrInfo->frameCount; + data->frameRate = fXingVbrInfo->frameRate; + } else { + TRACE("oggReader::AllocateCookie: assuming CBR, calculating timing info from file\n"); + int sampling_rate_index = (header[2] >> 2) & 0x03; + int bitrate_index = (header[2] >> 4) & 0x0f; + int samples_per_chunk = frame_sample_count_table[layer_index]; + bit_rate = 1000 * bit_rate_table[mpeg_version_index][layer_index][bitrate_index]; + frame_size = GetFrameLength(header); + data->frameRate = frame_rate_table[mpeg_version_index][sampling_rate_index]; + data->frameCount = samples_per_chunk * (fDataSize / frame_size); + data->duration = (data->frameCount * 1000000) / data->frameRate; + } + + TRACE("oggReader::AllocateCookie: frameRate %ld, frameCount %Ld, duration %.6f\n", + data->frameRate, data->frameCount, data->duration / 1000000.0); + + data->framePosition = 0; + data->framesPerFrame = frame_sample_count_table[layer_index]; + +// BMediaFormats formats; + media_format_description description; + description.family = B_MPEG_FORMAT_FAMILY; + description.u.mpeg.id = b_mpeg_id_table[mpeg_version_index][layer_index]; +// formats.GetFormatFor(description, &data->format); + + _get_format_for_description(&data->format, description); + +// data->format.u.encoded_audio.encoding = media_encoded_audio_format::B_ANY; + data->format.u.encoded_audio.bit_rate = bit_rate; + data->format.u.encoded_audio.frame_size = frame_size; +// data->format.u.encoded_audio.output.frame_rate = data->frameRate; +// data->format.u.encoded_audio.output.channel_count = 2; + + // store the cookie + *cookie = data; + return B_OK; +} + + +status_t +oggReader::FreeCookie(void *cookie) +{ + TRACE("oggReader::FreeCookie\n"); + mp3data *data = reinterpret_cast(cookie); + delete [] data->chunkBuffer; + delete data; + + return B_OK; +} + + +status_t +oggReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, void **infoBuffer, int32 *infoSize) +{ + mp3data *data = reinterpret_cast(cookie); + + *frameCount = data->frameCount; + *duration = data->duration; + *format = data->format; + *infoBuffer = 0; + *infoSize = 0; + return B_OK; +} + + +status_t +oggReader::Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time) +{ + if (!fSeekableSource) + return B_ERROR; + + mp3data *data = reinterpret_cast(cookie); + int64 pos; + + // this isn't very accurate + + if (seekTo & B_MEDIA_SEEK_TO_FRAME) { + pos = fXingVbrInfo ? XingSeekPoint(100.0 * *frame / (float)data->frameCount) : -1; + if (pos < 0) + pos = (*frame * fDataSize) / data->frameCount; + TRACE("oggReader::Seek to frame %Ld, pos %Ld\n", *frame, pos); + *time = (*frame * data->duration) / data->frameCount; + TRACE("oggReader::Seek newtime %Ld\n", *time); + } else if (seekTo & B_MEDIA_SEEK_TO_TIME) { + pos = fXingVbrInfo ? XingSeekPoint(100.0 * *time / (float)data->duration) : -1; + if (pos < 0) + pos = (*time * fDataSize) / data->duration; + TRACE("oggReader::Seek to time %Ld, pos %Ld\n", *time, pos); + *frame = (*time * data->frameCount) / data->duration; + TRACE("oggReader::Seek newframe %Ld\n", *frame); + } else { + return B_ERROR; + } + + // We ignore B_MEDIA_SEEK_CLOSEST_FORWARD, B_MEDIA_SEEK_CLOSEST_BACKWARD + + uint8 buffer[16000]; + if (pos > fDataSize - 16000) + pos = fDataSize - 16000; + if (pos < 0) + pos = 0; + int64 size = fDataSize - pos; + if (size > 16000) + size = 16000; + if (size != Source()->ReadAt(fDataStart + pos, buffer, size)) { + TRACE("oggReader::Seek: unexpected read error\n"); + return B_ERROR; + } + int32 end = size - 4; + int32 ofs; + for (ofs = 0; ofs < end; ofs++) { + if (buffer[ofs] != 0xff) // quick check + continue; + if (IsValidStream(&buffer[ofs], size - ofs)) + break; + } + if (ofs == end) { + TRACE("oggReader::Seek: couldn't synchronize\n"); + return B_ERROR; + } + data->position = pos + ofs; + + data->framePosition = *frame; // this is not exact + + TRACE("oggReader::Seek: synchronized at position %Ld\n", data->position); + return B_OK; +} + + +status_t +oggReader::GetNextChunk(void *cookie, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + mp3data *data = reinterpret_cast(cookie); + + int64 maxbytes = fDataSize - data->position; + if (maxbytes < 4) + return B_ERROR; + + mediaHeader->start_time = (data->framePosition * 1000000) / data->frameRate; + mediaHeader->file_pos = data->position; + + if (4 != Source()->ReadAt(fDataStart + data->position, data->chunkBuffer, 4)) { + TRACE("oggReader::GetNextChunk: unexpected read error\n"); + return B_ERROR; + } + data->position += 4; + maxbytes -= 4; + + int size = GetFrameLength(data->chunkBuffer) - 4; + if (size <= 0) { + TRACE("oggReader::GetNextChunk: invalid frame encountered\n"); + // try to synchronize again here! + return B_ERROR; + } + + if (size > maxbytes) + size = maxbytes; + + if (size != Source()->ReadAt(fDataStart + data->position, data->chunkBuffer + 4, size)) { + TRACE("oggReader::GetNextChunk: unexpected read error\n"); + return B_ERROR; + } + data->position += size; + + data->framePosition += data->framesPerFrame; + + *chunkBuffer = data->chunkBuffer; + *chunkSize = size + 4; + + if (*chunkSize > MAX_CHUNK_SIZE) { + printf("oggReader: chunk buffer overrun, read %ld bytes into %d bytes buffer\n", *chunkSize, MAX_CHUNK_SIZE); + exit(1); + } + + return B_OK; +} + + +bool +oggReader::ParseFile() +{ + // Since we already know that this is an mp3 file, + // detect the real (mp3 audio) data start and end + // and find VBR or other headers and tags + + const int32 search_size = 16384; + const int32 padding_size = 128; // Get???Length() functions need some bytes to look into + int64 offset; + int32 size; + uint8 buf[search_size]; + + fDataStart = -1; + + for (offset = 0; offset < fFileSize; ) { + int64 maxsize = fFileSize - offset; + size = (search_size < maxsize) ? search_size : maxsize; + + if (size != Source()->ReadAt(offset, buf, size)) { + TRACE("oggReaderPlugin::ParseFile reading %ld bytes at offset %Ld failed\n", size, offset); + return false; + } + + int skip_bytes = 0; + + // since the various Get???Length() functions need to check a few bytes + // (10 for ID3V2, about 40 for VBR), we stop searching before buffer end + int32 end = size - padding_size; + for (int32 pos = 0; pos < end; ) { + int hdr_length; + + // A Xing or Fraunhofer VBR header is embedded into a valid + // mp3 frame that contains silence. We need to first check + // for these headers before we can search for the start of a stream. + + hdr_length = GetXingVbrLength(&buf[pos]); + if (hdr_length > 0) { + TRACE("oggReaderPlugin::ParseFile found a Xing VBR header of %d bytes at position %Ld\n", hdr_length, offset + pos); + ParseXingVbrHeader(offset + pos); + goto skip_header; + } + + hdr_length = GetInfoCbrLength(&buf[pos]); + if (hdr_length > 0) { + TRACE("oggReaderPlugin::ParseFile found a Info CBR header of %d bytes at position %Ld\n", hdr_length, offset + pos); + goto skip_header; + } + + hdr_length = GetFraunhoferVbrLength(&buf[pos]); + if (hdr_length > 0) { + TRACE("oggReaderPlugin::ParseFile found a Fraunhofer VBR header of %d bytes at position %Ld\n", hdr_length, offset + pos); + ParseFraunhoferVbrHeader(offset + pos); + goto skip_header; + } + + hdr_length = GetLameVbrLength(&buf[pos]); + if (hdr_length > 0) { + TRACE("oggReaderPlugin::ParseFile found a Lame VBR header of %d bytes at position %Ld\n", hdr_length, offset + pos); + goto skip_header; + } + + hdr_length = GetId3v2Length(&buf[pos]); + if (hdr_length > 0) { + TRACE("oggReaderPlugin::ParseFile found a ID3V2 header of %d bytes at position %Ld\n", hdr_length, offset + pos); + goto skip_header; + } + + if (IsValidStream(&buf[pos], size - pos)) { + fDataStart = offset + pos; + break; + } + + pos++; + continue; + + skip_header: + int skip_max = end - pos; + skip_bytes = (skip_max < hdr_length) ? skip_max : hdr_length; + pos += skip_bytes; + skip_bytes = hdr_length - skip_bytes; + } + if (fDataStart != -1) + break; + + if (skip_bytes) { + offset += skip_bytes; + skip_bytes = 0; + } else { + offset += (search_size - padding_size); + } + } + + fDataSize = fFileSize - fDataStart; + + TRACE("found mp3 audio data at file position %Ld, maximum data length is %Ld\n", fDataStart, fDataSize); + + // search for a ID3 V1 tag + offset = fFileSize - 128; + size = 128; + if (offset > 0) { + if (size != Source()->ReadAt(offset, buf, size)) { + TRACE("oggReaderPlugin::ParseFile reading %ld bytes at offset %Ld failed\n", size, offset); + return false; + } + if (buf[0] == 'T'&& buf[1] == 'A' && buf[2] == 'G') { + TRACE("oggReaderPlugin::ParseFile found a ID3V1 header of 128 bytes at position %Ld\n", offset); + fDataSize -= 128; + } + } + + // search for a lyrics tag + // maximum size is 5100 bytes, and a 128 byte ID3V1 tag is always appended + // starts with "LYRICSBEGIN", end with "LYRICS200" or "LYRICSEND" + offset = fFileSize - 5300; + size = 5300; + if (offset < 0) { + offset = 0; + size = fFileSize; + } + if (size != Source()->ReadAt(offset, buf, size)) { + TRACE("oggReaderPlugin::ParseFile reading %ld bytes at offset %Ld failed\n", size, offset); + return false; + } + for (int pos = 0; pos < size; pos++) { + if (buf[pos] != 'L') + continue; + if (0 == memcmp(&buf[pos], "LYRICSBEGIN", 11)) { + TRACE("oggReaderPlugin::ParseFile found a Lyrics header at position %Ld\n", offset + pos); + fDataSize = offset + pos + fDataStart; + } + } + + // might search for APE tags, too + + TRACE("found mp3 audio data at file position %Ld, data length is %Ld\n", fDataStart, fDataSize); + + return true; +} + +int +oggReader::GetXingVbrLength(uint8 *header) +{ + int h_id = (header[1] >> 3) & 1; + int h_mode = (header[3] >> 6) & 3; + uint8 *xing_header; + + // determine offset of header + if(h_id) // mpeg1 + xing_header = (h_mode != 3) ? (header + 36) : (header + 21); + else // mpeg2 + xing_header = (h_mode != 3) ? (header + 21) : (header + 13); + + if (xing_header[0] != 'X') return -1; + if (xing_header[1] != 'i') return -1; + if (xing_header[2] != 'n') return -1; + if (xing_header[3] != 'g') return -1; + + return GetFrameLength(header); +} + +int +oggReader::GetInfoCbrLength(uint8 *header) +{ + int h_id = (header[1] >> 3) & 1; + int h_mode = (header[3] >> 6) & 3; + uint8 *info_header; + + // determine offset of header + if(h_id) // mpeg1 + info_header = (h_mode != 3) ? (header + 36) : (header + 21); + else // mpeg2 + info_header = (h_mode != 3) ? (header + 21) : (header + 13); + + if (info_header[0] != 'I') return -1; + if (info_header[1] != 'n') return -1; + if (info_header[2] != 'f') return -1; + if (info_header[3] != 'o') return -1; + + return GetFrameLength(header); +} + +void +oggReader::ParseXingVbrHeader(int64 pos) +{ + static const int sr_table[2][4] = { { 22050, 24000, 16000, 0 }, { 44100, 48000, 32000, 0 } }; + static const int FRAMES_FLAG = 0x0001; + static const int BYTES_FLAG = 0x0002; + static const int TOC_FLAG = 0x0004; + static const int VBR_SCALE_FLAG = 0x0008; + uint8 header[200]; + uint8 *xing_header; + + Source()->ReadAt(pos, header, sizeof(header)); + + int layer_index = (header[1] >> 1) & 3; + int h_id = (header[1] >> 3) & 1; + int h_sr_index = (header[2] >> 2) & 3; + int h_mode = (header[3] >> 6) & 3; + + // determine offset of header + if(h_id) // mpeg1 + xing_header = (h_mode != 3) ? (header + 36) : (header + 21); + else // mpeg2 + xing_header = (h_mode != 3) ? (header + 21) : (header + 13); + + xing_header += 4; // skip ID + + int flags = B_BENDIAN_TO_HOST_INT32(*(uint32 *)xing_header); + xing_header += 4; + + if (fXingVbrInfo) { + TRACE("oggReader::ParseXingVbrHeader: Error, already found a header\n"); + return; + } + + fXingVbrInfo = new xing_vbr_info; + + fXingVbrInfo->frameRate = sr_table[h_id][h_sr_index]; + if (flags & FRAMES_FLAG) { + fXingVbrInfo->encodedFramesCount = (int64)(uint32)B_BENDIAN_TO_HOST_INT32(*(uint32 *)xing_header); + xing_header += 4; + } else { + fXingVbrInfo->encodedFramesCount = -1; + } + + if (flags & BYTES_FLAG) { + fXingVbrInfo->byteCount = (int64)(uint32)B_BENDIAN_TO_HOST_INT32(*(uint32 *)xing_header); + xing_header += 4; + } else { + fXingVbrInfo->byteCount = -1; + } + + if (flags & TOC_FLAG) { + fXingVbrInfo->hasSeekpoints = true; + memcpy(fXingVbrInfo->seekpoints, xing_header, 100); + xing_header += 100; + } else { + fXingVbrInfo->hasSeekpoints = false; + } + + if (flags & VBR_SCALE_FLAG) { + fXingVbrInfo->vbrScale = B_BENDIAN_TO_HOST_INT32(*(uint32 *)xing_header); + xing_header += 4; + } else { + fXingVbrInfo->vbrScale = -1; + } + + // mpeg frame (chunk) size is is constant and always 384 samples (frames) for + // Layer I and 1152 samples for Layer II and Layer III + if (fXingVbrInfo->encodedFramesCount != -1) { + fXingVbrInfo->frameCount = fXingVbrInfo->encodedFramesCount * frame_sample_count_table[layer_index]; + fXingVbrInfo->duration = (fXingVbrInfo->frameCount * 1000000) / fXingVbrInfo->frameRate; + } else { + fXingVbrInfo->duration = -1; + fXingVbrInfo->frameCount = -1; + } + + TRACE("oggReader::ParseXingVbrHeader: %Ld encoded frames, %Ld bytes, %s seekpoints, vbrscale %ld\n", + fXingVbrInfo->encodedFramesCount, fXingVbrInfo->byteCount, + fXingVbrInfo->hasSeekpoints ? "has" : "no", fXingVbrInfo->vbrScale); + TRACE("oggReader::ParseXingVbrHeader: frameRate %ld, frameCount %Ld, duration %.6f\n", + fXingVbrInfo->frameRate, fXingVbrInfo->frameCount, fXingVbrInfo->duration / 1000000.0); +} + +int64 +oggReader::XingSeekPoint(float percent) +{ + if (!fXingVbrInfo || !fXingVbrInfo->hasSeekpoints || fXingVbrInfo->byteCount == -1) + return -1; + + int a; + int64 point; + float fa, fb, fx; + + if (percent < 0.0f) + percent = 0.0f; + if (percent > 100.0f) + percent = 100.0f; + + a = (int)percent; + if (a > 99) + a = 99; + fa = fXingVbrInfo->seekpoints[a]; + if (a < 99) + fb = fXingVbrInfo->seekpoints[a + 1]; + else + fb = 256.0f; + + fx = fa + (fb - fa) * (percent - a); + + point = (int64)((1.0f / 256.0f) * fx * fXingVbrInfo->byteCount); + TRACE("oggReader::XingSeekPoint for %.8f%% is %Ld\n", percent, point); + return point; +} + +int +oggReader::GetLameVbrLength(uint8 *header) +{ + return -1; +} + +int +oggReader::GetFraunhoferVbrLength(uint8 *header) +{ + if (header[0] != 0xff) return -1; + if (header[36] != 'V') return -1; + if (header[37] != 'B') return -1; + if (header[38] != 'R') return -1; + if (header[39] != 'I') return -1; + + return GetFrameLength(header); +} + +void +oggReader::ParseFraunhoferVbrHeader(int64 pos) +{ +} + +int +oggReader::GetId3v2Length(uint8 *buffer) +{ + if ((buffer[0] == 'I') && /* magic */ + (buffer[1] == 'D') && + (buffer[2] == '3') && + (buffer[3] != 0xff) && (buffer[4] != 0xff) && /* version */ + /* flags */ + (!(buffer[6] & 0x80)) && (!(buffer[7] & 0x80)) && /* the MSB in each byte in size is 0, to avoid */ + (!(buffer[8] & 0x80)) && (!(buffer[9] & 0x80))) { /* making a buggy mpeg header */ + return ((buffer[6] << 21)|(buffer[7] << 14)|(buffer[8] << 7)|(buffer[9])) + 10; + } + return B_ENTRY_NOT_FOUND; +} + +bool +oggReader::IsMp3File() +{ + // avoid detecting mp3 in a container format like AVI or mov + + // To detect an mp3 file, we seek into the middle, + // and search for a valid sequence of 3 frame headers. + // A mp3 frame has a maximum length of 2881 bytes, we + // load a block of 16kB and use it to search. + + const int32 search_size = 16384; + int64 offset; + int32 size; + uint8 buf[search_size]; + + size = 8; + offset = 0; + if (size != Source()->ReadAt(offset, buf, size)) { + TRACE("oggReaderPlugin::IsMp3File reading %ld bytes at offset %Ld failed\n", size, offset); + return false; + } + + // avoid reading some common formats that might have an embedded mp3 stream + // RIFF, AVI or WAV + if (buf[0] == 'R' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == 'F') + return false; + // Ogg Vorbis + if (buf[0] == 'O' && buf[1] == 'g' && buf[2] == 'g' && buf[3] == 'S') + return false; + // Real Media + if (buf[0] == '.' && buf[1] == 'R' && buf[2] == 'M' && buf[3] == 'F') + return false; + // Quicktime + if (buf[4] == 'm' && buf[5] == 'o' && buf[6] == 'o' && buf[7] == 'v') + return false; + // ASF 1 (first few bytes of GUID) + if (buf[0] == 0x30 && buf[1] == 0x26 && buf[2] == 0xb2 && buf[3] == 0x75 + && buf[4] == 0x8e && buf[5] == 0x66 && buf[6] == 0xcf && buf[7] == 0x11) + return false; + // ASF 2.0 (first few bytes of GUID) + if (buf[0] == 0xd1 && buf[1] == 0x29 && buf[2] == 0xe2 && buf[3] == 0xd6 + && buf[4] == 0xda && buf[5] == 0x35 && buf[6] == 0xd1 && buf[7] == 0x11) + return false; + + // search for a valid mpeg audio frame header + // sequence in the middle of the file + size = search_size; + offset = fFileSize / 2 - search_size / 2; + if (size > fFileSize) { + size = fFileSize; + offset = 0; + } + + TRACE("searching for mp3 frame headers at %Ld in %ld bytes\n", offset, size); + + if (size != Source()->ReadAt(offset, buf, size)) { + TRACE("oggReaderPlugin::IsMp3File reading %ld bytes at offset %Ld failed\n", size, offset); + return false; + } + + int32 end = size - 4; + for (int32 pos = 0; pos < end; pos++) { + if (buf[pos] != 0xff) // quick check + continue; + if (IsValidStream(&buf[pos], size - pos)) + return true; + } + return false; +} + +bool +oggReader::IsValidStream(uint8 *buffer, int size) +{ + // check 3 consecutive frame headers to make sure + // that the length encoded in the header is correct, + // and also that mpeg version and layer do not change + int length1 = GetFrameLength(buffer); + if (length1 < 0 || (length1 + 4) > size) + return false; + int version_index1 = (buffer[1] >> 3) & 0x03; + int layer_index1 = (buffer[1] >> 1) & 0x03; + int length2 = GetFrameLength(buffer + length1); + if (length2 < 0 || (length1 + length2 + 4) > size) + return false; + int version_index2 = (buffer[length1 + 1] >> 3) & 0x03; + int layer_index2 = (buffer[length1 + 1] >> 1) & 0x03; + if (version_index1 != version_index2 || layer_index1 != layer_index1) + return false; + int length3 = GetFrameLength(buffer + length1 + length2); + if (length3 < 0) + return false; + int version_index3 = (buffer[length1 + length2 + 1] >> 3) & 0x03; + int layer_index3 = (buffer[length1 + length2 + 1] >> 1) & 0x03; + if (version_index2 != version_index3 || layer_index2 != layer_index3) + return false; + return true; +} + +int +oggReader::GetFrameLength(void *header) +{ + uint8 *h = (uint8 *)header; + + if (h[0] != 0xff) + return -1; + if ((h[1] & 0xe0) != 0xe0) + return -1; + + int mpeg_version_index = (h[1] >> 3) & 0x03; + int layer_index = (h[1] >> 1) & 0x03; + int bitrate_index = (h[2] >> 4) & 0x0f; + int sampling_rate_index = (h[2] >> 2) & 0x03; + int padding = (h[2] >> 1) & 0x01; + /* no interested in the other bits */ + + int bitrate = bit_rate_table[mpeg_version_index][layer_index][bitrate_index]; + int framerate = frame_rate_table[mpeg_version_index][sampling_rate_index]; + + if (!bitrate || !framerate) + return -1; + + int length; + if (layer_index == 3) // layer 1 + length = ((144 * 1000 * bitrate) / framerate) + (padding * 4); + else // layer 2 & 3 + length = ((144 * 1000 * bitrate) / framerate) + padding; + +#if 0 + TRACE("%s %s, %s crc, bit rate %d, frame rate %d, padding %d, frame length %d\n", + mpeg_version_index == 0 ? "mpeg 2.5" : (mpeg_version_index == 2 ? "mpeg 2" : "mpeg 1"), + layer_index == 3 ? "layer 1" : (layer_index == 2 ? "layer 2" : "layer 3"), + (h[1] & 0x01) ? "no" : "has", + bitrate, framerate, padding, length); +#endif + + return length; +} + +Reader * +oggReaderPlugin::NewReader() +{ + return new oggReader; +} + + +MediaPlugin * +instantiate_plugin() +{ + return new oggReaderPlugin; +} diff --git a/src/add-ons/media/plugins/ogg/OggReaderPlugin.h b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h new file mode 100644 index 0000000000..07eab603d9 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/OggReaderPlugin.h @@ -0,0 +1,82 @@ +#ifndef _OGG_READER_PLUGIN_H +#define _OGG_READER_PLUGIN_H + +#include "ReaderPlugin.h" + +namespace BPrivate { namespace media { + +class oggReader : public Reader +{ +public: + oggReader(); + ~oggReader(); + + const char *Copyright(); + + status_t Sniff(int32 *streamCount); + + void GetFileFormatInfo(media_file_format *mff); + + status_t AllocateCookie(int32 streamNumber, void **cookie); + status_t FreeCookie(void *cookie); + + status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, void **infoBuffer, int32 *infoSize); + + status_t Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time); + + status_t GetNextChunk(void *cookie, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); + + BPositionIO *Source() { return fSeekableSource; } + +private: + bool ParseFile(); + bool IsMp3File(); + + // checks if the buffer contains a valid ogg stream, length should be + bool IsValidStream(uint8 *buffer, int size); + + int GetFrameLength(void *header); + int GetXingVbrLength(uint8 *header); + int GetFraunhoferVbrLength(uint8 *header); + int GetLameVbrLength(uint8 *header); + int GetId3v2Length(uint8 *header); + int GetInfoCbrLength(uint8 *header); + + bool FindData(); + + void ParseXingVbrHeader(int64 pos); + void ParseFraunhoferVbrHeader(int64 pos); + + int64 XingSeekPoint(float percent); + +private: + BPositionIO * fSeekableSource; + int64 fFileSize; + + int64 fDataStart; + int64 fDataSize; + + struct xing_vbr_info; + struct fhg_vbr_info; + + xing_vbr_info * fXingVbrInfo; + fhg_vbr_info * fFhgVbrInfo; +}; + + +class oggReaderPlugin : public ReaderPlugin +{ +public: + Reader *NewReader(); +}; + +} } // namespace BPrivate::media + +using namespace BPrivate::media; + +#endif diff --git a/src/add-ons/media/plugins/ogg/libogg/COPYING b/src/add-ons/media/plugins/ogg/libogg/COPYING new file mode 100644 index 0000000000..bd21d03ec7 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/libogg/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2002, Xiph.Org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.Org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/add-ons/media/plugins/ogg/libogg/Jamfile b/src/add-ons/media/plugins/ogg/libogg/Jamfile new file mode 100644 index 0000000000..52efbaa448 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/libogg/Jamfile @@ -0,0 +1,6 @@ +SubDir OBOS_TOP src add-ons media plugins ogg libogg ; + +StaticLibrary ogg : + bitwise.c + framing.c +; diff --git a/src/add-ons/media/plugins/ogg/libogg/bitwise.c b/src/add-ons/media/plugins/ogg/libogg/bitwise.c new file mode 100644 index 0000000000..2d01ad011b --- /dev/null +++ b/src/add-ons/media/plugins/ogg/libogg/bitwise.c @@ -0,0 +1,452 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitwise.c,v 1.1 2003/12/13 20:12:32 shatty Exp $ + + ********************************************************************/ + +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +#include +#include +#include + +#define BUFFER_INCREMENT 256 + +static unsigned long mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr=b->buffer=_ogg_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpack_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr|=mask[bits]; +} + +void oggpack_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ + unsigned char *ptr=(unsigned char *)source; + + long bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + int i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer=_ogg_realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->buffer+=bytes; + *b->ptr=0; + + } + if(bits) + oggpack_write(b,(long)(ptr[bytes]),bits); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpack_writeclear(oggpack_buffer *b){ + _ogg_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=value>>(8-b->endbit); + if(bits>=16){ + b->ptr[2]=value>>(16-b->endbit); + if(bits>=24){ + b->ptr[3]=value>>(24-b->endbit); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=value>>(32-b->endbit); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +long oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +/* bits <= 32 */ +long oggpack_read(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1UL; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +long oggpack_read1(oggpack_buffer *b){ + unsigned long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1UL; + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +long oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_SELFTEST +#include + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer o; +oggpack_buffer r; + +void report(char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long bytes,i; + unsigned char *buffer; + + oggpack_reset(&o); + for(i=0;i +#include +#include + +/* A complete description of Ogg framing exists in docs/framing.html */ + +int ogg_page_version(ogg_page *og){ + return((int)(og->header[4])); +} + +int ogg_page_continued(ogg_page *og){ + return((int)(og->header[5]&0x01)); +} + +int ogg_page_bos(ogg_page *og){ + return((int)(og->header[5]&0x02)); +} + +int ogg_page_eos(ogg_page *og){ + return((int)(og->header[5]&0x04)); +} + +ogg_int64_t ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + ogg_int64_t granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +int ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +long ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int ogg_page_packets(ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static ogg_uint32_t _ogg_crc_entry(unsigned long index){ + int i; + unsigned long r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static ogg_uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +int ogg_stream_init(ogg_stream_state *os,int serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +int ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)_ogg_free(os->body_data); + if(os->lacing_vals)_ogg_free(os->lacing_vals); + if(os->granule_vals)_ogg_free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + _ogg_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data=_ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=crc_reg&0xff; + og->header[23]=(crc_reg>>8)&0xff; + og->header[24]=(crc_reg>>16)&0xff; + og->header[25]=(crc_reg>>24)&0xff; + } +} + +/* submit data to the internal buffer of the framing engine */ +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + int lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not gurantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + ogg_stream_page will flush the last page in a stream even if it's + undersized; you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you need to flush an undersized + page in the middle of a stream for some reason. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + int i; + int vals=0; + int maxvals=(os->lacing_fill>255?255:os->lacing_fill); + int bytes=0; + long acc=0; + ogg_int64_t granule_pos=os->granule_vals[0]; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + long serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + long pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=vals&0xff; + for(i=0;iheader[i+27]=(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +int ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. See the 'frame-prog.txt' docs for details and + example code. */ + +/* initialize the struct to a known state */ +int ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +int ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + _ogg_free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + long newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data=_ogg_realloc(oy->data,newsize); + else + oy->data=_ogg_malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + long bytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + long bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + while(1){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + long bodysize=og->body_len; + int segptr=0; + + int version=ogg_page_version(og); + int continued=ogg_page_continued(og); + int bos=ogg_page_bos(og); + int eos=ogg_page_eos(og); + ogg_int64_t granulepos=ogg_page_granulepos(og); + int serialno=ogg_page_serialno(og); + long pageno=ogg_page_pageno(og); + int segments=header[26]; + + /* clean up 'returned data' */ + { + long lr=os->lacing_returned; + long br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + + /* are we a 'continued packet' page? If so, we'll need to skip + some segments */ + if(continued){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + int bytes=size; + int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + int val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + _ogg_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#ifdef _V_SELFTEST +#include + +ogg_stream_state os_en, os_de; +ogg_sync_state oy; + +void checkpacket(ogg_packet *op,int len, int no, int pos){ + long j; + static int sequence=0; + static int lastno=0; + + if(op->bytes!=len){ + fprintf(stderr,"incorrect packet length!\n"); + exit(1); + } + if(op->granulepos!=pos){ + fprintf(stderr,"incorrect packet position!\n"); + exit(1); + } + + /* packet number just follows sequence/gap; adjust the input number + for that */ + if(no==0){ + sequence=0; + }else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(op->packetno!=sequence){ + fprintf(stderr,"incorrect packet sequence %ld != %d\n", + (long)(op->packetno),sequence); + exit(1); + } + + /* Test data */ + for(j=0;jbytes;j++) + if(op->packet[j]!=((j+no)&0xff)){ + fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", + j,op->packet[j],(j+no)&0xff); + exit(1); + } +} + +void check_page(unsigned char *data,const int *header,ogg_page *og){ + long j; + /* Test data */ + for(j=0;jbody_len;j++) + if(og->body[j]!=data[j]){ + fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", + j,data[j],og->body[j]); + exit(1); + } + + /* Test header */ + for(j=0;jheader_len;j++){ + if(og->header[j]!=header[j]){ + fprintf(stderr,"header content mismatch at pos %ld:\n",j); + for(j=0;jheader[j]); + fprintf(stderr,"\n"); + exit(1); + } + } + if(og->header_len!=header[26]+27){ + fprintf(stderr,"header length incorrect! (%ld!=%d)\n", + og->header_len,header[26]+27); + exit(1); + } +} + +void print_header(ogg_page *og){ + int j; + fprintf(stderr,"\nHEADER:\n"); + fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); + + fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); + + fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); + + for(j=27;jheader_len;j++) + fprintf(stderr,"%d ",(int)og->header[j]); + fprintf(stderr,")\n\n"); +} + +void copy_page(ogg_page *og){ + unsigned char *temp=_ogg_malloc(og->header_len); + memcpy(temp,og->header,og->header_len); + og->header=temp; + + temp=_ogg_malloc(og->body_len); + memcpy(temp,og->body,og->body_len); + og->body=temp; +} + +void error(void){ + fprintf(stderr,"error!\n"); + exit(1); +} + +/* 17 only */ +const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + +/* 17, 254, 255, 256, 500, 510, 600 byte, pad */ +const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; +const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + +/* nil packets; beginning,middle,end */ +const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; +const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + +/* large initial packet */ +const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + +const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + + +/* continuing packet test */ +const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x34,0x24,0xd5,0x29, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xc8,0xc3,0xcb,0xed, + 5, + 10,255,4,255,0}; + + +/* page with the 255 segment limit */ +const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + +const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + + +/* packet that overspans over an entire page */ +const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xbd,0xd5,0xb5,0x8b, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xef,0xdd,0x88,0xde, + 7, + 255,255,75,255,4,255,0}; + +/* packet that overspans over an entire page */ +const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1,0}; + +void test_pack(const int *pl, const int **headers){ + unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ + long inptr=0; + long outptr=0; + long deptr=0; + long depacket=0; + long granule_pos=7,pageno=0; + int i,j,packets,pageout=0; + int eosflag=0; + int bosflag=0; + + ogg_stream_reset(&os_en); + ogg_stream_reset(&os_de); + ogg_sync_reset(&oy); + + for(packets=0;;packets++)if(pl[packets]==-1)break; + + for(i=0;i0){ + /* got a page. Happy happy. Verify that it's good. */ + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(memcmp(&op_de,&op_de2,sizeof(op_de))){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } + } + } + } + _ogg_free(data); + if(headers[pageno]!=NULL){ + fprintf(stderr,"did not write last page!\n"); + exit(1); + } + if(headers[pageout]!=NULL){ + fprintf(stderr,"did not decode last page!\n"); + exit(1); + } + if(inptr!=outptr){ + fprintf(stderr,"encoded page data incomplete!\n"); + exit(1); + } + if(inptr!=deptr){ + fprintf(stderr,"decoded page data incomplete!\n"); + exit(1); + } + if(inptr!=depacket){ + fprintf(stderr,"decoded packet data incomplete!\n"); + exit(1); + } + if(!eosflag){ + fprintf(stderr,"Never got a packet with EOS set!\n"); + exit(1); + } + fprintf(stderr,"ok.\n"); +} + +int main(void){ + + ogg_stream_init(&os_en,0x04030201); + ogg_stream_init(&os_de,0x04030201); + ogg_sync_init(&oy); + + /* Exercise each code path in the framing code. Also verify that + the checksums are working. */ + + { + /* 17 only */ + const int packets[]={17, -1}; + const int *headret[]={head1_0,NULL}; + + fprintf(stderr,"testing single page encoding... "); + test_pack(packets,headret); + } + + { + /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ + const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; + const int *headret[]={head1_1,head2_1,NULL}; + + fprintf(stderr,"testing basic page encoding... "); + test_pack(packets,headret); + } + + { + /* nil packets; beginning,middle,end */ + const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + const int *headret[]={head1_2,head2_2,NULL}; + + fprintf(stderr,"testing basic nil packets... "); + test_pack(packets,headret); + } + + { + /* large initial packet */ + const int packets[]={4345,259,255,-1}; + const int *headret[]={head1_3,head2_3,NULL}; + + fprintf(stderr,"testing initial-packet lacing > 4k... "); + test_pack(packets,headret); + } + + { + /* continuing packet test */ + const int packets[]={0,4345,259,255,-1}; + const int *headret[]={head1_4,head2_4,head3_4,NULL}; + + fprintf(stderr,"testing single packet page span... "); + test_pack(packets,headret); + } + + /* page with the 255 segment limit */ + { + + const int packets[]={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + const int *headret[]={head1_5,head2_5,head3_5,NULL}; + + fprintf(stderr,"testing max packet segments... "); + test_pack(packets,headret); + } + + { + /* packet that overspans over an entire page */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing very large packets... "); + test_pack(packets,headret); + } + + { + /* term only page. why not? */ + const int packets[]={0,100,4080,-1}; + const int *headret[]={head1_7,head2_7,head3_7,NULL}; + + fprintf(stderr,"testing zero data page (1 nil packet)... "); + test_pack(packets,headret); + } + + + + { + /* build a bunch of pages for testing */ + unsigned char *data=_ogg_malloc(1024*1024); + int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int inptr=0,i,j; + ogg_page og[5]; + + ogg_stream_reset(&os_en); + + for(i=0;pl[i]!=-1;i++){ + ogg_packet op; + int len=pl[i]; + + op.packet=data+inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(j=0;j0)error(); + + /* Test fractional page inputs: incomplete fixed header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, + 5); + ogg_sync_wrote(&oy,5); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete body */ + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, + og[1].header_len-28); + ogg_sync_wrote(&oy,og[1].header_len-28); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); + ogg_sync_wrote(&oy,1000); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, + og[1].body_len-1000); + ogg_sync_wrote(&oy,og[1].body_len-1000); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test fractional page inputs: page + incomplete capture */ + { + ogg_page og_de; + fprintf(stderr,"Testing sync on 1+partial inputs... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, + og[1].header_len-20); + ogg_sync_wrote(&oy,og[1].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing search for capture... "); + ogg_sync_reset(&oy); + + /* 'garbage' */ + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, + og[2].header_len-20); + ogg_sync_wrote(&oy,og[2].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len); + ogg_sync_wrote(&oy,og[2].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: page + garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing recapture... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len-5); + ogg_sync_wrote(&oy,og[2].body_len-5); + + memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, + og[3].header_len); + ogg_sync_wrote(&oy,og[3].header_len); + + memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, + og[3].body_len); + ogg_sync_wrote(&oy,og[3].body_len); + + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + } + + return(0); +} + +#endif + + + + diff --git a/src/add-ons/media/plugins/ogg/libogg/ogg/ogg.h b/src/add-ons/media/plugins/ogg/libogg/ogg/ogg.h new file mode 100644 index 0000000000..db66b595b9 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/libogg/ogg/ogg.h @@ -0,0 +1,184 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id: ogg.h,v 1.1 2003/12/13 20:12:32 shatty Exp $ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern int ogg_page_serialno(ogg_page *og); +extern long ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ + + + + + + diff --git a/src/add-ons/media/plugins/ogg/libogg/ogg/os_types.h b/src/add-ons/media/plugins/ogg/libogg/ogg/os_types.h new file mode 100644 index 0000000000..e2e3b8a417 --- /dev/null +++ b/src/add-ons/media/plugins/ogg/libogg/ogg/os_types.h @@ -0,0 +1,84 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h,v 1.1 2003/12/13 20:12:32 shatty Exp $ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#ifdef _WIN32 + +# ifndef __GNUC__ + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; +# else + /* Cygwin */ + #include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#else + +# include +# include + +#endif + +#endif /* _OS_TYPES_H */