📄 framing.c
字号:
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis 'TREMOR' 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 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
function: decode Ogg streams back into raw packets
note: The CRC code is directly derived from public domain code by
Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html
for details.
********************************************************************/
#include <stdlib.h>
#include <string.h>
#include "ogg.h"
#include "misc.h"
/* A complete description of Ogg framing exists in docs/framing.html */
/* basic, centralized Ogg memory management based on linked lists of
references to refcounted memory buffers. References and buffers
are both recycled. Buffers are passed around and consumed in
reference form. */
static ogg_buffer_state *ogg_buffer_create(void){
ogg_buffer_state *bs=_ogg_calloc(1,sizeof(*bs));
return bs;
}
/* destruction is 'lazy'; there may be memory references outstanding,
and yanking the buffer state out from underneath would be
antisocial. Dealloc what is currently unused and have
_release_one watch for the stragglers to come in. When they do,
finish destruction. */
/* call the helper while holding lock */
static void _ogg_buffer_destroy(ogg_buffer_state *bs){
ogg_buffer *bt;
ogg_reference *rt;
if(bs->shutdown){
bt=bs->unused_buffers;
rt=bs->unused_references;
while(bt){
ogg_buffer *b=bt;
bt=b->ptr.next;
if(b->data)_ogg_free(b->data);
_ogg_free(b);
}
bs->unused_buffers=0;
while(rt){
ogg_reference *r=rt;
rt=r->next;
_ogg_free(r);
}
bs->unused_references=0;
if(!bs->outstanding){
_ogg_free(bs);
return;
}
}
}
static void ogg_buffer_destroy(ogg_buffer_state *bs){
bs->shutdown=1;
_ogg_buffer_destroy(bs);
}
static ogg_buffer *_fetch_buffer(ogg_buffer_state *bs,long bytes){
ogg_buffer *ob;
bs->outstanding++;
/* do we have an unused buffer sitting in the pool? */
if(bs->unused_buffers){
ob=bs->unused_buffers;
bs->unused_buffers=ob->ptr.next;
/* if the unused buffer is too small, grow it */
if(ob->size<bytes){
ob->data=_ogg_realloc(ob->data,bytes);
ob->size=bytes;
}
}else{
/* allocate a new buffer */
ob=_ogg_malloc(sizeof(*ob));
ob->data=_ogg_malloc(bytes<16?16:bytes);
ob->size=bytes;
}
ob->refcount=1;
ob->ptr.owner=bs;
return ob;
}
static ogg_reference *_fetch_ref(ogg_buffer_state *bs){
ogg_reference *or;
bs->outstanding++;
/* do we have an unused reference sitting in the pool? */
if(bs->unused_references){
or=bs->unused_references;
bs->unused_references=or->next;
}else{
/* allocate a new reference */
or=_ogg_malloc(sizeof(*or));
}
or->begin=0;
or->length=0;
or->next=0;
return or;
}
/* fetch a reference pointing to a fresh, initially continguous buffer
of at least [bytes] length */
static ogg_reference *ogg_buffer_alloc(ogg_buffer_state *bs,long bytes){
ogg_buffer *ob=_fetch_buffer(bs,bytes);
ogg_reference *or=_fetch_ref(bs);
or->buffer=ob;
return or;
}
/* enlarge the data buffer in the current link */
static void ogg_buffer_realloc(ogg_reference *or,long bytes){
ogg_buffer *ob=or->buffer;
/* if the unused buffer is too small, grow it */
if(ob->size<bytes){
ob->data=_ogg_realloc(ob->data,bytes);
ob->size=bytes;
}
}
static void _ogg_buffer_mark_one(ogg_reference *or){
or->buffer->refcount++;
}
/* increase the refcount of the buffers to which the reference points */
static void ogg_buffer_mark(ogg_reference *or){
while(or){
_ogg_buffer_mark_one(or);
or=or->next;
}
}
/* duplicate a reference (pointing to the same actual buffer memory)
and increment buffer refcount. If the desired segment begins out
of range, NULL is returned; if the desired segment is simply zero
length, a zero length ref is returned. Partial range overlap
returns the overlap of the ranges */
static ogg_reference *ogg_buffer_sub(ogg_reference *or,long begin,long length){
ogg_reference *ret=0,*head=0;
/* walk past any preceeding fragments we don't want */
while(or && begin>=or->length){
begin-=or->length;
or=or->next;
}
/* duplicate the reference chain; increment refcounts */
while(or && length){
ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner);
if(head)
head->next=temp;
else
ret=temp;
head=temp;
head->buffer=or->buffer;
head->begin=or->begin+begin;
head->length=length;
if(head->length>or->length-begin)
head->length=or->length-begin;
begin=0;
length-=head->length;
or=or->next;
}
ogg_buffer_mark(ret);
return ret;
}
ogg_reference *ogg_buffer_dup(ogg_reference *or){
ogg_reference *ret=0,*head=0;
/* duplicate the reference chain; increment refcounts */
while(or){
ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner);
if(head)
head->next=temp;
else
ret=temp;
head=temp;
head->buffer=or->buffer;
head->begin=or->begin;
head->length=or->length;
or=or->next;
}
ogg_buffer_mark(ret);
return ret;
}
/* split a reference into two references; 'return' is a reference to
the buffer preceeding pos and 'head'/'tail' are the buffer past the
split. If pos is at or past the end of the passed in segment,
'head/tail' are NULL */
static ogg_reference *ogg_buffer_split(ogg_reference **tail,
ogg_reference **head,long pos){
/* walk past any preceeding fragments to one of:
a) the exact boundary that seps two fragments
b) the fragment that needs split somewhere in the middle */
ogg_reference *ret=*tail;
ogg_reference *or=*tail;
while(or && pos>or->length){
pos-=or->length;
or=or->next;
}
if(!or || pos==0){
return 0;
}else{
if(pos>=or->length){
/* exact split, or off the end? */
if(or->next){
/* a split */
*tail=or->next;
or->next=0;
}else{
/* off or at the end */
*tail=*head=0;
}
}else{
/* split within a fragment */
long lengthA=pos;
long beginB=or->begin+pos;
long lengthB=or->length-pos;
/* make a new reference to tail the second piece */
*tail=_fetch_ref(or->buffer->ptr.owner);
(*tail)->buffer=or->buffer;
(*tail)->begin=beginB;
(*tail)->length=lengthB;
(*tail)->next=or->next;
_ogg_buffer_mark_one(*tail);
if(head && or==*head)*head=*tail;
/* update the first piece */
or->next=0;
or->length=lengthA;
}
}
return ret;
}
static void ogg_buffer_release_one(ogg_reference *or){
ogg_buffer *ob=or->buffer;
ogg_buffer_state *bs=ob->ptr.owner;
ob->refcount--;
if(ob->refcount==0){
bs->outstanding--; /* for the returned buffer */
ob->ptr.next=bs->unused_buffers;
bs->unused_buffers=ob;
}
bs->outstanding--; /* for the returned reference */
or->next=bs->unused_references;
bs->unused_references=or;
_ogg_buffer_destroy(bs); /* lazy cleanup (if needed) */
}
/* release the references, decrease the refcounts of buffers to which
they point, release any buffers with a refcount that drops to zero */
static void ogg_buffer_release(ogg_reference *or){
while(or){
ogg_reference *next=or->next;
ogg_buffer_release_one(or);
or=next;
}
}
static ogg_reference *ogg_buffer_pretruncate(ogg_reference *or,long pos){
/* release preceeding fragments we don't want */
while(or && pos>=or->length){
ogg_reference *next=or->next;
pos-=or->length;
ogg_buffer_release_one(or);
or=next;
}
if (or) {
or->begin+=pos;
or->length-=pos;
}
return or;
}
static ogg_reference *ogg_buffer_walk(ogg_reference *or){
if(!or)return NULL;
while(or->next){
or=or->next;
}
return(or);
}
/* *head is appended to the front end (head) of *tail; both continue to
be valid pointers, with *tail at the tail and *head at the head */
static ogg_reference *ogg_buffer_cat(ogg_reference *tail, ogg_reference *head){
if(!tail)return head;
while(tail->next){
tail=tail->next;
}
tail->next=head;
return ogg_buffer_walk(head);
}
static void _positionB(oggbyte_buffer *b,int pos){
if(pos<b->pos){
/* start at beginning, scan forward */
b->ref=b->baseref;
b->pos=0;
b->end=b->pos+b->ref->length;
b->ptr=b->ref->buffer->data+b->ref->begin;
}
}
static void _positionF(oggbyte_buffer *b,int pos){
/* scan forward for position */
while(pos>=b->end){
/* just seek forward */
b->pos+=b->ref->length;
b->ref=b->ref->next;
b->end=b->ref->length+b->pos;
b->ptr=b->ref->buffer->data+b->ref->begin;
}
}
static int oggbyte_init(oggbyte_buffer *b,ogg_reference *or){
memset(b,0,sizeof(*b));
if(or){
b->ref=b->baseref=or;
b->pos=0;
b->end=b->ref->length;
b->ptr=b->ref->buffer->data+b->ref->begin;
return 0;
}else
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -