/* sync the stream and get a page. Keep trying until we find a page. * Suppress '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 */ static public int ogg_sync_pageout(ref ogg_sync_state oy, ref ogg_page og) { if (ogg_sync_check(ref oy) != 0) { return(0); } /* 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 */ for (;;) { int ret = ogg_sync_pageseek(ref oy, ref og); if (ret > 0) { /* have a page */ return(1); } if (ret == 0) { /* need more data */ return(0); } if (oy.unsynced == 0) { oy.unsynced = 1; return(-1); } } }
/* checksum the page */ /* Direct table CRC; note that this will be faster in the future if we perform the checksum simultaneously with other copies */ static public void ogg_page_checksum_set(ref ogg_page og) { uint crc_reg = 0; /* 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 (int i = 0; i < og.header_len; i++) { crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >> 24) & 0xff) ^ og.header[i]]; } for (int i = 0; i < og.body_len; i++) { crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >> 24) & 0xff) ^ og.body[i]]; } og.header[22] = (byte)(crc_reg & 0xff); og.header[23] = (byte)((crc_reg >> 8) & 0xff); og.header[24] = (byte)((crc_reg >> 16) & 0xff); og.header[25] = (byte)((crc_reg >> 24) & 0xff); }
private void WritePage(ref ogg_page page, Stream stream) { byte[] headerData = ReadBuffer(page.header, page.header_len); byte[] bodyData = ReadBuffer(page.body, page.body_len); stream.Write(headerData, 0, headerData.Length); stream.Write(bodyData, 0, bodyData.Length); }
static public int ogg_page_packets(ref ogg_page og) { int n = og.header[26]; int count = 0; for (int i = 0; i < n; i++) { if (og.header[27 + i] < 255) { count++; } } return(count); }
/* Like the above, but an argument is provided to adjust the nominal page size for applications which are smart enough to provide their * own delay based flushing */ static public int ogg_stream_pageout_fill(ref ogg_stream_state os, ref ogg_page og, int nfill) { int force = 0; if (ogg_stream_check(ref os) != 0) { return(0); } if ((os.e_o_s != 0 && os.lacing_fill != 0) || (os.lacing_fill != 0 && os.b_o_s == 0)) { force = 1; } return(ogg_stream_flush_i(ref os, ref og, force, nfill)); }
public static extern int ogg_page_continued(ref ogg_page og);
public static extern int ogg_page_version(ref ogg_page og);
public static extern int ogg_stream_flush_fill(ref ogg_stream_state os, ref ogg_page og, int fillbytes);
public static extern int ogg_stream_flush(ref ogg_stream_state os, ref ogg_page og);
public static extern int ogg_sync_pageout(ref ogg_sync_state oy, ref ogg_page og);
static public int ogg_page_continued(ref ogg_page og) { return((int)og.header[5] & 0x01); }
public static extern int ogg_page_pageno(ref ogg_page og);
public static extern long ogg_page_granulepos(ref ogg_page og);
public static extern int ogg_page_packets(ref ogg_page og);
static public int ogg_page_pageno(ref ogg_page og) { return(Marshal.ReadInt32((IntPtr)og.header, 18)); }
/* Like the above, but an argument is provided to adjust the nominal page size for applications which are smart enough to provide their * own delay based flushing */ static public int ogg_stream_flush_fill(ref ogg_stream_state os, ref ogg_page og, int nfill) { return(ogg_stream_flush_i(ref os, ref og, 1, nfill)); }
static public long ogg_page_granulepos(ref ogg_page og) { return(Marshal.ReadInt64((IntPtr)og.header, 6)); }
/* 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 guarantee that all packets have flushed. * Only a return value of 0 from ogg_stream_flush indicates all packet data is flushed into pages. * * since ogg_stream_flush 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 specifically need to flush a page regardless of size in the middle of a stream. */ static public int ogg_stream_flush(ref ogg_stream_state os, ref ogg_page og) { return(ogg_stream_flush_i(ref os, ref og, 1, 4096)); }
static public int ogg_page_eos(ref ogg_page og) { return((int)og.header[5] & 0x04); }
public static extern int ogg_page_eos(ref ogg_page og);
static public int ogg_sync_pageseek(ref ogg_sync_state oy, ref ogg_page og) { byte *page = oy.data + oy.returned; byte *next = null; int bytes = oy.fill - oy.returned; if (ogg_sync_check(ref oy) != 0) { return(0); } if (oy.headerbytes == 0) { int headerbytes; if (bytes < 27) { return(0); } if ( page[0] != 'O' || page[1] != 'g' || page[2] != 'g' || page[3] != 'S' ) { goto sync_fail; } else { headerbytes = page[26] + 27; if (bytes < headerbytes) { return(0); } for (int i = 0; i < page[26]; i++) { oy.bodybytes += page[27 + i]; } oy.headerbytes = headerbytes; } } if (oy.bodybytes + oy.headerbytes > bytes) { return(0); } /* The whole test page is buffered. Verify the checksum */ { byte[] chksum = new byte[4]; ogg_page log = new ogg_page(); /* Grab the checksum bytes, set the header field to zero */ Marshal.Copy((IntPtr)(page + 22), chksum, 0, 4); ZeroMemory(page + 22, 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(ref log); /* Compare */ if ( chksum[0] != page[22] || chksum[1] != page[23] || chksum[2] != page[24] || chksum[3] != page[25] ) { /* D'oh. Mismatch! Corrupt page (or miscapture and not a page at all) */ /* replace the computed checksum with the one actually read in */ Marshal.Copy(chksum, 0, (IntPtr)(page + 22), 4); /* Bad checksum. Lose sync */ goto sync_fail; } } /* yes, have a whole page all ready to go */ { byte *_page = oy.data + oy.returned; bytes = oy.headerbytes + oy.bodybytes; if (og != null) { 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 = 0; oy.bodybytes = 0; return(bytes); } sync_fail: oy.headerbytes = 0; oy.bodybytes = 0; for (int i = 1; i < bytes; i++) { if (page[i] == 'O') { next = (byte *)(page + i); break; } } if (next == null) { next = oy.data + oy.fill; } oy.returned = (int)(next - oy.data); return((int)-(next - page)); }
public static extern int ogg_page_serialno(ref ogg_page og);
static public int ogg_page_version(ref ogg_page og) { return((int)og.header[4]); }
public static extern int ogg_page_checksum_set(ref ogg_page og);
/* Conditionally flush a page; force==0 will only flush nominal-size pages, force==1 forces us to flush a page regardless of page size so long as there's any data available at all. */ static public int ogg_stream_flush_i(ref ogg_stream_state os, ref ogg_page og, int force, int nfill) { int i; int vals = 0; int maxvals = (os.lacing_fill > 255 ? 255 : os.lacing_fill); int bytes = 0; int acc = 0; long granule_pos = -1; if (ogg_stream_check(ref os) != 0) { return(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; vals < maxvals; vals++) { if ((os.lacing_vals[vals] & 0xff) < 255) { vals++; break; } } } else { int packets_done = 0; int packet_just_done = 0; for (vals = 0; vals < maxvals; vals++) { if (acc > nfill && packet_just_done >= 4) { force = 1; break; } acc += os.lacing_vals[vals] & 0xff; if ((os.lacing_vals[vals] & 0xff) < 255) { granule_pos = os.granule_vals[vals]; packet_just_done = ++packets_done; } else { packet_just_done = 0; } if (vals == 255) { force = 1; } } } if (force == 0) { return(0); } /* construct the header in temp storage */ os.header[0] = (byte)'O'; os.header[1] = (byte)'g'; os.header[2] = (byte)'g'; os.header[3] = (byte)'S'; /* stream structure version */ os.header[4] = 0; /* continued packet flag? */ os.header[5] = 0; 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 != 0 && 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] = (byte)(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] = (byte)(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) { /* because someone called stream_reset; this would be a strange thing to do in an encode stream, but it has plausible uses */ os.pageno = 0; } long pageno = os.pageno++; for (i = 18; i < 22; i++) { os.header[i] = (byte)(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] = (byte)(vals & 0xff); for (i = 0; i < vals; i++) { os.header[i + 27] = (byte)(os.lacing_vals[i] & 0xff); bytes += os.header[i + 27]; } /* set pointers in the ogg_page struct */ og.header = os.header; og.header_len = vals + 27; 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; CopyMemory(os.lacing_vals, os.lacing_vals + vals, os.lacing_fill * sizeof(int)); CopyMemory(os.granule_vals, os.granule_vals + vals, os.lacing_fill * sizeof(long)); os.body_returned += bytes; /* calculate the checksum */ ogg_page_checksum_set(ref og); /* done */ return(1); }
/* add the incoming page to the stream state; we decompose the page * into packet segments here as well. */ static public int ogg_stream_pagein(ref ogg_stream_state os, ref ogg_page og) { byte *header = og.header; byte *body = og.body; int bodysize = og.body_len; int version = ogg_page_version(ref og); int continued = ogg_page_continued(ref og); int bos = ogg_page_bos(ref og); int eos = ogg_page_eos(ref og); long granulepos = ogg_page_granulepos(ref og); int serialno = ogg_page_serialno(ref og); int pageno = ogg_page_pageno(ref og); int segments = header[26]; if (ogg_stream_check(ref os) != 0) { return(-1); } /* clean up 'returned data' */ { int lr = os.lacing_returned; int br = os.body_returned; /* body data */ if (br > 0) { os.body_fill -= br; if (os.body_fill > 0) { CopyMemory(os.body_data, os.body_data + br, os.body_fill); } os.body_returned = 0; } /* segment table */ if (lr > 0) { if (os.lacing_fill - lr > 0) { CopyMemory(os.lacing_vals, os.lacing_vals + lr, (os.lacing_fill - lr) * sizeof(int)); } else { CopyMemory(os.granule_vals, os.granule_vals + lr, (os.lacing_fill - lr) * sizeof(long)); } } 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); } if (_os_lacing_expand(ref os, segments + 1) != 0) { return(-1); } /* are we in sequence? */ if (pageno != os.pageno) { /* unroll previous partial packet (if any) */ for (int i = os.lacing_packet; i < os.lacing_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++; } } int segptr = 0; /* are we a 'continued packet' page? If so, we may need to skip some segments */ if (continued != 0) { if (os.lacing_fill < 1 || os.lacing_vals[os.lacing_fill - 1] == 0x400) { bos = 0; for (segptr = 0; segptr < segments; segptr++) { int val = header[27 + segptr]; body += val; bodysize -= val; if (val < 255) { segptr++; break; } } } } if (bodysize > 0) { if (_os_body_expand(ref os, bodysize) != 0) { return(-1); } CopyMemory(os.body_data + os.body_fill, body, bodysize); os.body_fill += bodysize; } int saved = -1; while (segptr < segments) { int val = og.header[27 + segptr]; os.lacing_vals[os.lacing_fill] = val; os.granule_vals[os.lacing_fill] = -1; if (bos != 0) { 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; } } if (saved != -1) { os.granule_vals[saved] = granulepos; } if (eos != 0) { os.e_o_s = 1; if (os.lacing_fill > 0) { os.lacing_vals[os.lacing_fill - 1] |= 0x200; } } os.pageno = pageno + 1; return(0); }