/***************************************************************************** * * This program is free software ; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: ogg.c 543 2006-01-07 22:06:24Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/ #include "../common/common.h" #include "vorbis.h" #include "tremor/ogg.h" #include "tremor/ivorbiscodec.h" // OGG and OGM file format #define PACKET_TYPE_HEADER 0x01 #define PACKET_TYPE_COMMENT 0x03 #define PACKET_TYPE_CODEBOOK 0x05 #define PACKET_TYPE_BITS 0x07 #define PACKET_IS_SYNCPOINT 0x08 #define PACKET_LEN_BITS01 0xc0 #define PACKET_LEN_BITS2 0x02 static const char MT_Video[] = "video"; static const char MT_Audio[] = "audio"; static const char MT_Text[] = "text"; typedef struct oggstream { format_stream Stream; bool_t Invalid; // not vorbis and not ogm bool_t Vorbis; // sending direct oggpackets (not the merged binary data) bool_t Native; // speex int PacketNo; int64_t MediaTime; // in units int64_t MediaRateNum; // in units/tick int64_t MediaRateDen; int DefaultLen; bool_t NeedMorePage; ogg_stream_state* OggStream; ogg_packet OggPacket; vorbis_info Info; vorbis_comment Comment; } oggstream; typedef struct ogg { format_base Format; ogg_sync_state* OggSync; ogg_page OggPage; } ogg; typedef struct ogm_header_video { int32_t width; int32_t height; } ogm_header_video; typedef struct ogm_header_audio { int16_t channels; int16_t blockalign; int32_t avgbytespersec; } ogm_header_audio; typedef struct ogm_header { char streamtype[8]; char subtype[4]; int32_t size; int64_t time_unit; int64_t samples_per_unit; int32_t default_len; int32_t buffersize; int16_t bits_per_sample; union { ogm_header_video video; ogm_header_audio audio; } format; } ogm_header; extern int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb,int eop); static void FreeStream(ogg* p,oggstream* Stream) { vorbis_comment_clear(&Stream->Comment); vorbis_info_clear(&Stream->Info); ogg_packet_release(&Stream->OggPacket); if (Stream->OggStream) { ogg_stream_destroy(Stream->OggStream); Stream->OggStream = NULL; } } static void Done(ogg* p) { ogg_page_release(&p->OggPage); if (p->OggSync) { ogg_sync_destroy(p->OggSync); p->OggSync = NULL; } } static int AddBuffer(ogg* p,format_buffer* Buffer) { if (!Buffer) return ERR_NEED_MORE_DATA; if (Buffer->Length) { ogg_buffer* Ptr = ogg_sync_bufferinext(p->OggSync); if (!Ptr) { Format_BufferRelease(&p->Format,Buffer); return ERR_OUT_OF_MEMORY; } Ptr->ext = &p->Format; Ptr->extdata = Buffer; Ptr->data = Buffer->Block.Ptr; Ptr->size = Buffer->Length; ogg_sync_wrote(p->OggSync,Buffer->Length); } else Format_BufferRelease(&p->Format,Buffer); return ERR_NONE; } static int Init(ogg* p) { format_reader* Reader = p->Format.Reader; format_buffer* Buffer; int i; memset(&p->OggPage,0,sizeof(ogg_page)); p->OggSync = ogg_sync_create(); p->Format.TimeStamps = 1; if (!p->OggSync) return ERR_OUT_OF_MEMORY; Format_ReadBuffer(Reader,0); Buffer = Format_BufferRemove(Reader); if (!Buffer) return ERR_INVALID_DATA; // search for 'OggS' pattern for (i=3;iLength;++i) if (Buffer->Block.Ptr[i-3]=='O' && Buffer->Block.Ptr[i-2]=='g' && Buffer->Block.Ptr[i-1]=='g' && Buffer->Block.Ptr[i-0]=='S') break; if (i==Buffer->Length) { Format_BufferRelease(&p->Format,Buffer); return ERR_INVALID_DATA; } return AddBuffer(p,Buffer); } static void AfterSeek(ogg* p) { ogg_sync_reset(p->OggSync); } static void ReleaseStream(ogg* p,oggstream* Stream) { ogg_stream_reset_serialno(Stream->OggStream,Stream->Stream.Id); Stream->NeedMorePage = 1; Stream->MediaTime = Stream->Stream.Reader->FilePos>0 && Stream->PacketNo>3 ? -1:0; } extern void oggext_release(void* ext,void* ext2) { if (ext && ext2) Format_BufferRelease((format_base*)ext,(format_buffer*)ext2); } static int ReadPacketTime( ogg* p, format_reader* Reader, format_packet* Packet ) { // only called by CalcDuration format_buffer* Buffer; int Bytes; ogg_buffer* Ptr; ogg_sync_state* Sync = ogg_sync_create(); ogg_page Page; if (!Sync) return ERR_OUT_OF_MEMORY; while ((Buffer = Format_BufferRemove(Reader))!=NULL) { Ptr = ogg_sync_bufferinext(Sync); if (Ptr) { Ptr->ext = &p->Format; Ptr->extdata = Buffer; Ptr->data = Buffer->Block.Ptr; Ptr->size = Buffer->Length; ogg_sync_wrote(Sync,Buffer->Length); } else Format_BufferRelease(&p->Format,Buffer); } memset(&Page,0,sizeof(Page)); while ((Bytes = ogg_sync_pageseek(Sync,&Page)) != 0) { if (Bytes > 0) { int64_t MediaTime = ogg_page_granulepos(&Page); int Id = ogg_page_serialno(&Page); int i; if (MediaTime != -1) for (i=0;iFormat.StreamCount;++i) if (p->Format.Streams[i]->Id == Id) { oggstream* s = (oggstream*) p->Format.Streams[i]; if (s->MediaRateNum) { tick_t RefTime = (tick_t)((MediaTime * s->MediaRateDen) / s->MediaRateNum); if (RefTime > Packet->RefTime) { Packet->Stream = &s->Stream; Packet->RefTime = RefTime; } } break; } } } ogg_page_release(&Page); ogg_sync_destroy(Sync); return ERR_NONE; } static void FrameRate( fraction* f, int64_t Num, int64_t Den ) { while (Num > MAX_INT || Den > MAX_INT) { Num >>= 1; Den >>= 1; } f->Num = (int)Num; f->Den = (int)Den; } static bool_t SpeexHeader(ogg* p, oggstream* s, const char* Data, int Length ) { if (Length<80 && strncmp(Data,"Speex ",8)!=0) return 0; PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_AUDIO; s->Stream.Format.Format.Audio.Format = AUDIOFMT_SPEEX; s->Stream.Format.Format.Audio.Channels = INT32LE(*(int32_t*)(Data+48)); s->Stream.Format.ByteRate = INT32LE(*(int32_t*)(Data+52))/8; s->Stream.Format.Format.Audio.SampleRate = INT32LE(*(int32_t*)(Data+36)); s->MediaRateDen = TICKSPERSEC; s->MediaRateNum = s->Stream.Format.Format.Audio.SampleRate; s->Native = 1; if (PacketFormatExtra(&s->Stream.Format,Length)) memcpy(s->Stream.Format.Extra,Data,s->Stream.Format.ExtraLength); return 1; } static bool_t OGMHeader(ogg* p, oggstream* s, const char* Data, int Length ) { int i; if (Length<40 || (*Data & PACKET_TYPE_BITS) != PACKET_TYPE_HEADER) return 0; if (strncmp(Data+1, "Direct Show Samples embedded in Ogg", 35) == 0) { // old header if (INT32LE(*(int32_t*)(Data+96)) == 0x05589F80) { PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_VIDEO; s->Stream.Format.Format.Video.Width = INT32LE(*(int32_t*)(Data+176)); s->Stream.Format.Format.Video.Height = INT32LE(*(int32_t*)(Data+180)); s->Stream.Format.Format.Video.Pixel.FourCC = INT32LE(*(int32_t*)(Data+68)); s->Stream.Format.Format.Video.Pixel.BitCount = INT16LE(*(int16_t*)(Data+182)); i = INT16LE(*(int16_t*)(Data+136)); // extrasize if (i && PacketFormatExtra(&s->Stream.Format,i)) memcpy(s->Stream.Format.Extra,Data+142,s->Stream.Format.ExtraLength); s->MediaRateDen = INT64LE(*(int32_t*)(Data+164))*TICKSPERSEC; s->MediaRateNum = 10000000; s->DefaultLen = 1; FrameRate(&s->Stream.Format.PacketRate,s->MediaRateNum,s->MediaRateDen/TICKSPERSEC); return 1; } if (INT32LE(*(int32_t*)(Data+96)) == 0x05589F81) { PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_AUDIO; s->Stream.Format.Format.Audio.Format = INT16LE(*(int16_t*)(Data+124)); s->Stream.Format.Format.Audio.Channels = INT16LE(*(int16_t*)(Data+126)); s->Stream.Format.Format.Audio.BlockAlign = INT16LE(*(int16_t*)(Data+136)); s->Stream.Format.Format.Audio.Bits = INT16LE(*(int16_t*)(Data+138)); s->Stream.Format.Format.Audio.SampleRate = INT32LE(*(int32_t*)(Data+128)); s->Stream.Format.ByteRate = INT32LE(*(int32_t*)(p+132)); i = INT16LE(*(int16_t*)(Data+136)); // extrasize if (i && PacketFormatExtra(&s->Stream.Format,i)) memcpy(s->Stream.Format.Extra,Data+142,s->Stream.Format.ExtraLength); s->MediaRateDen = TICKSPERSEC; s->MediaRateNum = INT32LE(*(int32_t*)(Data+128)); s->DefaultLen = 1; return 1; } } else if (Length >= sizeof(ogm_header)+1) { ogm_header Head; memcpy(&Head,Data+1,sizeof(Head)); // new header if (strncmp(Head.streamtype, MT_Video, strlen(MT_Video)) == 0) { PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_VIDEO; s->Stream.Format.Format.Video.Width = INT32LE(Head.format.video.width); s->Stream.Format.Format.Video.Height = INT32LE(Head.format.video.height); s->Stream.Format.Format.Video.Pixel.FourCC = INT32LE(*(int32_t*)Head.subtype); s->Stream.Format.Format.Video.Pixel.BitCount = INT16LE(Head.bits_per_sample); s->MediaRateDen = INT64LE(Head.time_unit)*TICKSPERSEC; s->MediaRateNum = INT64LE(Head.samples_per_unit) * 10000000; s->DefaultLen = INT32LE(Head.default_len); FrameRate(&s->Stream.Format.PacketRate,s->MediaRateNum,s->MediaRateDen/TICKSPERSEC); i = Length - (sizeof(ogm_header)+1); if (i && PacketFormatExtra(&s->Stream.Format,i)) memcpy(s->Stream.Format.Extra,Data+1+sizeof(ogm_header),s->Stream.Format.ExtraLength); return 1; } if (strncmp(Head.streamtype, MT_Audio, strlen(MT_Audio)) == 0) { PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_AUDIO; s->Stream.Format.Format.Audio.Format = 0; for (i=0;i<4;++i) if (Head.subtype[i]) s->Stream.Format.Format.Audio.Format = s->Stream.Format.Format.Audio.Format*16+Hex(Head.subtype[i]); s->Stream.Format.Format.Audio.Channels = INT16LE(Head.format.audio.channels); s->Stream.Format.Format.Audio.Bits = INT16LE(Head.bits_per_sample); s->Stream.Format.Format.Audio.BlockAlign = INT16LE(Head.format.audio.blockalign); s->Stream.Format.ByteRate = INT32LE(Head.format.audio.avgbytespersec); s->Stream.Format.Format.Audio.SampleRate = (int)INT64LE(Head.samples_per_unit); s->MediaRateDen = INT64LE(Head.time_unit)*TICKSPERSEC; s->MediaRateNum = INT64LE(Head.samples_per_unit) * 10000000; s->DefaultLen = INT32LE(Head.default_len); i = Length - (sizeof(ogm_header)+1); if (i && PacketFormatExtra(&s->Stream.Format,i)) memcpy(s->Stream.Format.Extra,Data+1+sizeof(ogm_header),s->Stream.Format.ExtraLength); return 1; } if (strncmp(Data+1, MT_Text, strlen(MT_Text)) == 0) { PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_SUBTITLE; s->Stream.Format.Format.Subtitle.FourCC = SUBTITLE_OEM; //??? s->MediaRateDen = INT64LE(Head.time_unit)*TICKSPERSEC; s->MediaRateNum = INT64LE(Head.samples_per_unit) * 10000000; s->DefaultLen = INT32LE(Head.default_len); i = Length - (sizeof(ogm_header)+1); if (i && PacketFormatExtra(&s->Stream.Format,i)) memcpy(s->Stream.Format.Extra,Data+1+sizeof(ogm_header),s->Stream.Format.ExtraLength); return 1; } } return 0; } static void SendComments(oggstream* Stream) { tchar_t s[256]; if (Stream->Stream.Comment.Node) { int No; for (No=0;NoComment.comments;++No) { UTF8ToTcs(s,TSIZEOF(s),Stream->Comment.user_comments[No]); Stream->Stream.Comment.Node->Set(Stream->Stream.Comment.Node,Stream->Stream.Comment.No,s,sizeof(s)); } } } static bool_t VorbisHeader(ogg* p,oggstream* s) { tchar_t URL[MAXPATH]; if (vorbis_synthesis_headerin(&s->Info,&s->Comment,&s->OggPacket)<0) return 0; PacketFormatClear(&s->Stream.Format); s->Stream.Format.Type = PACKET_AUDIO; s->Stream.Format.Format.Audio.Channels = s->Info.channels; s->Stream.Format.Format.Audio.SampleRate = s->Info.rate; s->Stream.Format.ByteRate = s->Info.bitrate_nominal >> 3; if (p->Format.Reader->Input->Get(p->Format.Reader->Input,STREAM_URL,URL,sizeof(URL))==ERR_NONE && CheckExts(URL,T("ogg:A")) != 0) s->Stream.Format.Format.Audio.Format = AUDIOFMT_VORBIS_INTERNAL_AUDIO; else s->Stream.Format.Format.Audio.Format = AUDIOFMT_VORBIS_INTERNAL_VIDEO; s->Vorbis = 1; s->MediaRateDen = TICKSPERSEC; s->MediaRateNum = s->Info.rate; return 1; } static int FillQueue(ogg* p,format_reader* Reader) { for (;;) { int Bytes = ogg_sync_pageseek(p->OggSync,&p->OggPage); if (Bytes == 0) // need more data { int Result; format_buffer* Buffer; if (!Reader->BufferAvailable && (!p->Format.SyncMode || p->Format.SyncRead<=0)) return ERR_NEED_MORE_DATA; Buffer = Format_BufferRemove(Reader); if (!Buffer && p->Format.SyncMode && p->Format.SyncRead>0 && Format_ReadBuffer(Reader,0)) Buffer = Format_BufferRemove(Reader); Result = AddBuffer(p,Buffer); if (Result != ERR_NONE) return Result; } else if (Bytes < 0) Reader->FilePos -= Bytes; else if (Bytes > 0) { int StreamNo; oggstream* s; int Id; Reader->FilePos += Bytes; Id = ogg_page_serialno(&p->OggPage); DEBUG_MSG4(DEBUG_FORMAT,T("OGG Page id:%d size:%d gran:%d filepos:%d"),Id,p->OggPage.body_len,(int)ogg_page_granulepos(&p->OggPage),Reader->FilePos - Bytes); for (StreamNo=0;StreamNoFormat.StreamCount;++StreamNo) if (p->Format.Streams[StreamNo]->Id == Id) break; if (StreamNo==p->Format.StreamCount) { // check for restarted audio http streaming (comments changed) if (p->Format.StreamCount==1 && p->Format.Streams[0]->Format.Type == PACKET_AUDIO && p->Format.Streams[0]->LastTime>0) { StreamNo = 0; s = (oggstream*) p->Format.Streams[0]; if (s->Vorbis) { // vorbis decoder have to release s->Info s->Stream.Format.Extra = NULL; s->Stream.Format.ExtraLength = 0; ConnectionUpdate((node*)&p->Format,FORMAT_STREAM+0,s->Stream.Pin.Node,s->Stream.Pin.No); } FreeStream(p,s); } else { s = (oggstream*) Format_AddStream(&p->Format,sizeof(oggstream)); if (!s) continue; } // init stream s->Stream.Id = Id; s->OggStream = ogg_stream_create(Id); s->NeedMorePage = 1; s->MediaTime = 0; s->Invalid = 0; s->Vorbis = 0; s->Native = 0; s->PacketNo = 0; vorbis_info_init(&s->Info); vorbis_comment_init(&s->Comment); } s = (oggstream*) p->Format.Streams[StreamNo]; if (s->Invalid) // drop invalid streams continue; if (s->PacketNo>=3) { if (!s->Stream.Pin.Node) // drop unused streams continue; if (p->Format.InSeek) { // reftime needed for SeekByPacket if ((s->MediaTime = ogg_page_granulepos(&p->OggPage)) != -1) { // no need for GlobalOffset here s->Stream.LastTime = (tick_t)(s->MediaTime * s->MediaRateDen / s->MediaRateNum); if (s->Stream.Format.Type == PACKET_AUDIO) { s->Stream.LastTime += p->Format.AVOffset; if (s->Stream.LastTime < 0) s->Stream.LastTime = 0; } } } } // add page to stream if (ogg_stream_pagein(s->OggStream,&p->OggPage) >= 0) { if (s->PacketNo<3) // header packet needed? { int i = ogg_stream_packetout(s->OggStream,&s->OggPacket); if (i == 0) // stream needs more pages continue; if (++s->PacketNo==1) // first packet? { ogg_reference* Ref; const void* Data; int Length; if (i < 0) { // first header packet is a must have s->Invalid = 1; continue; } if (p->Format.UseBufferBlock) { for (Length=0,Ref=s->OggPacket.packet;Ref;Ref=Ref->next) Length += Ref->length; if (s->Stream.BufferBlockLengthFormat,&s->Stream,Length)) { Length = 0; Data = NULL; } else { for (Length=0,Ref=s->OggPacket.packet;Ref;Ref=Ref->next) { WriteBlock(&s->Stream.BufferBlock,Length,Ref->buffer->data + Ref->begin,Ref->length); Length += Ref->length; } Data = s->Stream.BufferBlock.Ptr; } } else { BufferDrop(&s->Stream.BufferMem); for (Ref=s->OggPacket.packet;Ref;Ref=Ref->next) BufferWrite(&s->Stream.BufferMem,Ref->buffer->data + Ref->begin, Ref->length, 16384); Data = s->Stream.BufferMem.Data; Length = s->Stream.BufferMem.WritePos; } if (OGMHeader(p,s,(char*)Data,Length) || SpeexHeader(p,s,(char*)Data,Length)) { PacketFormatDefault(&s->Stream.Format); s->PacketNo = 3; // no more headers } else if (!VorbisHeader(p,s)) { s->Invalid = 1; continue; } while (s->MediaRateNum > (1<<30)) { s->MediaRateDen >>= 1; s->MediaRateNum >>= 1; } Format_PrepairStream(&p->Format,&s->Stream); continue; } else { assert(s->Vorbis); // error in second or third header packet will not cause fatal error vorbis_synthesis_headerin(&s->Info,&s->Comment,&s->OggPacket); if (s->PacketNo == 3) { // got the three header packets: reinit codec with vorbis_info s->Stream.Format.Extra = &s->Info; s->Stream.Format.ExtraLength = -1; ConnectionUpdate((node*)&p->Format,FORMAT_STREAM+StreamNo,s->Stream.Pin.Node,s->Stream.Pin.No); SendComments(s); } continue; } } s->NeedMorePage = 0; break; } } } return ERR_NONE; } static int Process(ogg* p,oggstream* Stream) { int Result; int No; int Burst = 1; if (Stream->Invalid) return ERR_NONE; if (Stream->Stream.Pending) { Result = Format_Send(&p->Format,&Stream->Stream); if (Result == ERR_BUFFER_FULL || Result == ERR_SYNCED) return Result; } // process a limited number of packets at once (other streams need cpu time too) Result = ERR_NONE; Burst = Stream->Stream.PacketBurst; for (No=0;NoNeedMorePage) { Result = FillQueue(p,Stream->Stream.Reader); if (Result == ERR_NEED_MORE_DATA && Stream->Stream.Reader->NoMoreInput) Result = Format_CheckEof(&p->Format,&Stream->Stream); if (Result != ERR_NONE || (p->Format.Bench && Stream->NeedMorePage)) return Result; } // sync: only SyncStream is processed if (p->Format.SyncMode && Stream != (oggstream*)p->Format.SyncStream) return ERR_NEED_MORE_DATA; Result = ogg_stream_packetout(Stream->OggStream,&Stream->OggPacket); if (Result == 0) { // stream needs more pages Stream->NeedMorePage = 1; if (p->Format.Bench) break; // benchmark mode: no additional pages are added in this loop --No; continue; } if (Result < 0) // packet hole or span continue; DEBUG_MSG3(DEBUG_FORMAT,T("OGG Packet id:%d size:%d gran:%d"),Stream->Stream.Id,Stream->OggPacket.bytes,(int)Stream->OggPacket.granulepos); if (!Stream->Vorbis && Stream->PacketNo==3) { // non vorbis: second packet can be a comment (after first header) ++Stream->PacketNo; if (Stream->Native) { oggpack_buffer opb; oggpack_readinit(&opb,Stream->OggPacket.packet); if (_vorbis_unpack_comment(&Stream->Comment,&opb,0)>=0) SendComments(Stream); continue; // always assume comment packet } else { Stream->Info.rate=1; if (vorbis_synthesis_headerin(&Stream->Info,&Stream->Comment,&Stream->OggPacket) >= 0) { SendComments(Stream); continue; } } } // build output packet if (Stream->OggPacket.granulepos >= 0) Stream->MediaTime = Stream->OggPacket.granulepos; if (Stream->MediaTime >= 0) { tick_t t = (tick_t)((Stream->MediaTime * Stream->MediaRateDen) / Stream->MediaRateNum); t += p->Format.GlobalOffset; if (Stream->Stream.Format.Type == PACKET_AUDIO) { t += p->Format.AVOffset; if (t<0) t=0; } if (Stream->Stream.LastTime>t && t-p->Format.GlobalOffsetFormat,&Stream->Stream,&t); Stream->Stream.LastTime = Stream->Stream.Packet.RefTime = t; } else { if (Stream->Stream.LastTime < 0) continue; Stream->Stream.Packet.RefTime = TIME_UNKNOWN; } if (!Stream->Vorbis) { ogg_reference* Ref; const uint8_t* Data; int Length; int Len; int PacketTime; // merge buffers if (p->Format.UseBufferBlock) { for (Length=0,Ref=Stream->OggPacket.packet;Ref;Ref=Ref->next) Length += Ref->length; if (Stream->Stream.BufferBlockLengthFormat,&Stream->Stream,Length)) { Length = 0; Data = NULL; } else { for (Length=0,Ref=Stream->OggPacket.packet;Ref;Ref=Ref->next) { WriteBlock(&Stream->Stream.BufferBlock,Length,Ref->buffer->data + Ref->begin,Ref->length); Length += Ref->length; } Data = Stream->Stream.BufferBlock.Ptr; } } else { BufferDrop(&Stream->Stream.BufferMem); for (Ref=Stream->OggPacket.packet;Ref;Ref=Ref->next) BufferWrite(&Stream->Stream.BufferMem,Ref->buffer->data + Ref->begin, Ref->length, 16384); Data = (const uint8_t*)Stream->Stream.BufferMem.Data; Length = Stream->Stream.BufferMem.WritePos; } if (Stream->Native) { Stream->Stream.Packet.Data[0] = Data; Stream->Stream.Packet.Length = Length; Stream->MediaTime = -1; } else { // process OGM information if (*Data & PACKET_TYPE_HEADER) continue; // not data packet Len = (*Data & PACKET_LEN_BITS01) >> 6; Len |= (*Data & PACKET_LEN_BITS2) << 1; Stream->Stream.Packet.Key = (*Data & PACKET_IS_SYNCPOINT) != 0; Stream->Stream.Packet.Data[0] = Data + 1 + Len; Stream->Stream.Packet.Length = Length - 1 - Len; if (!Len) PacketTime = Stream->DefaultLen; else for (PacketTime=0;Len;--Len) { PacketTime <<= 8; PacketTime |= Data[Len]; } Stream->MediaTime += PacketTime; } } else { // send oggpacket directly if (!Stream->OggPacket.packet) continue; Stream->Stream.Packet.Data[0] = &Stream->OggPacket; Stream->Stream.Packet.Length = sizeof(Stream->OggPacket); Stream->MediaTime = -1; } Stream->Stream.Pending = 1; Result = Format_Send(&p->Format,&Stream->Stream); if (Result == ERR_BUFFER_FULL || Result == ERR_SYNCED) break; if (Stream->Stream.State.DropLevel==2) Burst = Stream->Stream.PacketBurst*2; // try to catch up } // buffer full: there was possibly some processing so don't allow sleeping // need data: burst count exceeded, but it doesn't mean there is no data left in buffers if (Result == ERR_BUFFER_FULL || Result == ERR_NEED_MORE_DATA) Result = ERR_NONE; return Result; } static int Create(ogg* p) { p->Format.Init = (fmtfunc)Init; p->Format.Done = (fmtvoid)Done; p->Format.Seek = (fmtseek)Format_SeekByPacket; p->Format.FillQueue = (fmtfill)FillQueue; p->Format.ReadPacket = (fmtreadpacket)ReadPacketTime; p->Format.ReleaseStream = (fmtstream)ReleaseStream; p->Format.AfterSeek = (fmtvoid)AfterSeek; p->Format.FreeStream = (fmtstream)FreeStream; p->Format.Process = (fmtstreamprocess)Process; p->Format.DisableReader = 1; p->Format.Sended = NULL; return ERR_NONE; } static const nodedef OGG = { sizeof(ogg), OGG_ID, FORMATBASE_CLASS, PRI_DEFAULT, (nodecreate)Create, }; void OGG_Init() { NodeRegisterClass(&OGG); } void OGG_Done() { NodeUnRegisterClass(OGG_ID); }