📄 vorbisfile.c
字号:
} ogg_page_release(&og);}static void _make_decode_ready(OggVorbis_File *vf){ if (vf->ready_state != STREAMSET) return; if (vf->seekable) { vorbis_synthesis_init(&vf->vd, vf->vi + vf->current_link); } else { vorbis_synthesis_init(&vf->vd, vf->vi); } vorbis_block_init(&vf->vd, &vf->vb); vf->ready_state = INITSET; vf->bittrack = 0; vf->samptrack = 0; return;}static int _open_seekable2(OggVorbis_File *vf){ ogg_uint32_t serialno = vf->current_serialno; ogg_uint32_t tempserialno; ogg_int64_t dataoffset = vf->offset, end; ogg_page og = {0, 0, 0, 0}; /* we're partially open and have a first link header state in storage in vf */ /* we can seek, so set out learning all about this file */ (vf->callbacks.seek_func)(vf->datasource, 0, SEEK_END); vf->offset = vf->end = (vf->callbacks.tell_func)(vf->datasource); /* We get the offset for the last page of the physical bitstream. Most OggVorbis files will contain a single logical bitstream */ end = _get_prev_page(vf, &og); if (end < 0) return (int)(end); /* more than one logical bitstream? */ tempserialno = ogg_page_serialno(&og); ogg_page_release(&og); if (tempserialno != serialno) { /* Chained bitstream. Bisect-search each logical bitstream section. Do so based on serial number only */ if (_bisect_forward_serialno(vf, 0, 0, end + 1, serialno, 0) < 0) return (OV_EREAD); } else { /* Only one logical bitstream */ if (_bisect_forward_serialno(vf, 0, end, end + 1, serialno, 0)) return (OV_EREAD); } /* the initial header memory is referenced by vf after; don't free it */ _prefetch_all_headers(vf, dataoffset); return (ov_raw_seek(vf, 0));}/* clear out the current logical bitstream decoder */static void _decode_clear(OggVorbis_File *vf){ vorbis_dsp_clear(&vf->vd); vorbis_block_clear(&vf->vb); vf->ready_state = OPENED;}/* fetch and process a packet. Handles the case where we're at a bitstream boundary and dumps the decoding machine. If the decoding machine is unloaded, it loads it. It also keeps pcm_offset up to date (seek and read both use this. seek uses a special hack with readp). return: <0) error, OV_HOLE (lost packet) or OV_EOF 0) need more data (only if readp==0) 1) got a packet */static int _fetch_and_process_packet(OggVorbis_File *vf, int readp, int spanp){ ogg_page og = {0, 0, 0, 0}; ogg_packet op = {0, 0, 0, 0, 0, 0}; int ret = 0; /* handle one packet. Try to fetch it from current stream state */ /* extract packets from page */ while (1) { /* process a packet if we can. If the machine isn't loaded, neither is a page */ if (vf->ready_state == INITSET) { while (1) { int result = ogg_stream_packetout(vf->os, &op); ogg_int64_t granulepos; if (result < 0) { ret = OV_HOLE; /* hole in the data. */ goto cleanup; } if (result > 0) { /* got a packet. process it */ granulepos = op.granulepos; if (!vorbis_synthesis(&vf->vb, &op, 1)) { /* lazy check for lazy header handling. The header packets aren't audio, so if/when we submit them, vorbis_synthesis will reject them */ /* suck in the synthesis data and track bitrate */ { int oldsamples = vorbis_synthesis_pcmout(&vf->vd, NULL); /* for proper use of libvorbis within libvorbisfile, oldsamples will always be zero. */ if (oldsamples) { ret = OV_EFAULT; goto cleanup; } vorbis_synthesis_blockin(&vf->vd, &vf->vb); vf->samptrack += vorbis_synthesis_pcmout(&vf->vd, NULL) - oldsamples; vf->bittrack += op.bytes *8; } /* update the pcm offset. */ if (granulepos != - 1 && !op.e_o_s) { int link = (vf->seekable ? vf->current_link : 0); int i, samples; /* this packet has a pcm_offset on it (the last packet completed on a page carries the offset) After processing (above), we know the pcm position of the *last* sample ready to be returned. Find the offset of the *first* As an aside, this trick is inaccurate if we begin reading anew right at the last page; the end-of-stream granulepos declares the last frame in the stream, and the last packet of the last page may be a partial frame. So, we need a previous granulepos from an in-sequence page to have a reference point. Thus the !op.e_o_s clause above */ if (vf->seekable && link > 0) granulepos -= vf->pcmlengths[link *2]; if (granulepos < 0) granulepos = 0; /* actually, this should not be possible here unless the stream is very broken */ samples = vorbis_synthesis_pcmout(&vf->vd, NULL); granulepos -= samples; for (i = 0; i < link; i++) granulepos += vf->pcmlengths[i *2 + 1]; vf->pcm_offset = granulepos; } ret = 1; goto cleanup; } } else break; } } if (vf->ready_state >= OPENED) { int ret; if (!readp) { ret = 0; goto cleanup; } if ((ret = (int)_get_next_page(vf, &og, - 1)) < 0) { ret = OV_EOF; /* eof. leave unitialized */ goto cleanup; } /* bitrate tracking; add the header's bytes here, the body bytes are done by packet above */ vf->bittrack += og.header_len *8; /* has our decoding just traversed a bitstream boundary? */ if (vf->ready_state == INITSET) { if (vf->current_serialno != ogg_page_serialno(&og)) { if (!spanp) { ret = OV_EOF; goto cleanup; } _decode_clear(vf); if (!vf->seekable) { vorbis_info_clear(vf->vi); vorbis_comment_clear(vf->vc); } } } } /* Do we need to load a new machine before submitting the page? */ /* This is different in the seekable and non-seekable cases. In the seekable case, we already have all the header information loaded and cached; we just initialize the machine with it and continue on our merry way. In the non-seekable (streaming) case, we'll only be at a boundary if we just left the previous logical bitstream and we're now nominally at the header of the next bitstream */ if (vf->ready_state != INITSET) {int link; if (vf->ready_state < STREAMSET) { if (vf->seekable) { vf->current_serialno = ogg_page_serialno(&og); /* match the serialno to bitstream section. We use this rather than offset positions to avoid problems near logical bitstream boundaries */ for (link = 0; link < vf->links; link++) if (vf->serialnos[link] == vf->current_serialno) break; if (link == vf->links) { ret = OV_EBADLINK; /* sign of a bogus stream. error out, leave machine uninitialized */ goto cleanup; } vf->current_link = link; ogg_stream_reset_serialno(vf->os, vf->current_serialno); vf->ready_state = STREAMSET; } else { /* we're streaming */ /* fetch the three header packets, build the info struct */ int ret = _fetch_headers(vf, vf->vi, vf->vc, &vf->current_serialno, &og); if (ret) goto cleanup; vf->current_link++; link = 0; } } _make_decode_ready(vf); } ogg_stream_pagein(vf->os, &og); }cleanup: ogg_packet_release(&op); ogg_page_release(&og); return ret;}/* if, eg, 64 bit stdio is configured by default, this will build with fseek64 */static int _ov_open1(void *f, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks){ int offsettest = (f ? callbacks.seek_func(f, 0, SEEK_CUR) : - 1); int ret; memset(vf, 0, sizeof(*vf)); vf->datasource = f; vf->callbacks = callbacks; /* init the framing state */ vf->oy = ogg_sync_create(); /* perhaps some data was previously read into a buffer for testing against other stream types. Allow initialization from this previously read data (as we may be reading from a non-seekable stream) */ if (initial) { char *buffer = ogg_sync_bufferin(vf->oy, ibytes); memcpy(buffer, initial, ibytes); ogg_sync_wrote(vf->oy, ibytes); } /* can we seek? Stevens suggests the seek test was portable */ if (offsettest != - 1) vf->seekable = 1; /* No seeking yet; Set up a 'single' (current) logical bitstream entry for partial open */ vf->links = 1; vf->vi = _ogg_calloc(vf->links, sizeof(*vf->vi)); vf->vc = _ogg_calloc(vf->links, sizeof(*vf->vc)); vf->os = ogg_stream_create(- 1); /* fill in the serialno later */ /* Try to fetch the headers, maintaining all the storage */ if ((ret = _fetch_headers(vf, vf->vi, vf->vc, &vf->current_serialno, NULL)) < 0) { vf->datasource = NULL; ov_clear(vf); } else if (vf->ready_state < PARTOPEN) vf->ready_state = PARTOPEN; return (ret);}static int _ov_open2(OggVorbis_File *vf){ if (vf->ready_state < OPENED) vf->ready_state = OPENED; if (vf->seekable) { int ret = _open_seekable2(vf); if (ret) { vf->datasource = NULL; ov_clear(vf); } return (ret); } return 0;}/* clear out the OggVorbis_File struct */int ov_clear(OggVorbis_File *vf){ if (vf) { vorbis_block_clear(&vf->vb); vorbis_dsp_clear(&vf->vd); ogg_stream_destroy(vf->os); if (vf->vi && vf->links) { int i; for (i = 0; i < vf->links; i++) { vorbis_info_clear(vf->vi + i); vorbis_comment_clear(vf->vc + i); } _ogg_free(vf->vi); _ogg_free(vf->vc); } if (vf->dataoffsets) _ogg_free(vf->dataoffsets); if (vf->pcmlengths) _ogg_free(vf->pcmlengths); if (vf->serialnos) _ogg_free(vf->serialnos); if (vf->offsets) _ogg_free(vf->offsets); ogg_sync_destroy(vf->oy); if (vf->datasource) (vf->callbacks.close_func)(vf->datasource); memset(vf, 0, sizeof(*vf)); }#ifdef DEBUG_LEAKS _VDBG_dump();#endif return (0);}/* inspects the OggVorbis file and finds/documents all the logical bitstreams contained in it. Tries to be tolerant of logical bitstream sections that are truncated/woogie. return: -1 error 0 OK*/int ov_open_callbacks(void *f, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks){ int ret = _ov_open1(f, vf, initial, ibytes, callbacks); if (ret) return ret; return _ov_open2(vf);}int ov_open(FILE *f, OggVorbis_File *vf, char *initial, long ibytes){ ov_callbacks callbacks = { (size_t(*)(void *, size_t, size_t, void *)) wrap_fread, (int (*)(void *, ogg_int64_t, int)) fseek64_wrap, (int (*)(void *)) wrap_fclose, (long (*)(void *)) wrap_ftell }; return ov_open_callbacks((void *) f, vf, initial, ibytes, callbacks);}/* Only partially open the vorbis file; test for Vorbisness, and load the headers for the first chain. Do not seek (although test for seekability). Use ov_test_open to finish opening the file, else ov_clear to close/free it. Same return codes as open. */int ov_test_callbacks(void *f, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks){ return _ov_open1(f, vf, initial, ibytes, callbacks);}int ov_test(FILE *f, OggVorbis_File *vf, char *initial, long ibytes){ ov_callbacks callbacks = { (size_t(*)(void *, size_t, size_t, void *)) wrap_fread, (int (*)(void *, ogg_int64_t, int)) fseek64_wrap, (int (*)(void *)) wrap_fclose, (long (*)(void *)) wrap_ftell }; return ov_test_callbacks((void *) f, vf, initial, ibytes, callbacks);}int ov_test_open(OggVorbis_File *vf){ if (vf->ready_state != PARTOPEN) return (OV_EINVAL); return _ov_open2(vf);}/* How many logical bitstreams in this physical bitstream? */long ov_streams(OggVorbis_File *vf){ return vf->links;}/* Is the FILE * associated with vf seekable? */long ov_seekable(OggVorbis_File *vf){ return vf->seekable;}/* returns the bitrate for a given logical bitstream or the entire physical bitstream. If the file is open for random access, it will find the *actual* average bitrate. If the file is streaming, it returns the nominal bitrate (if set) else the average of the upper/lower bounds (if set) else -1 (unset). If you want the actual bitrate field settings, get them from the vorbis_info structs */long ov_bitrate(OggVorbis_File *vf, int i){ if (vf->ready_state < OPENED) return (OV_EINVAL); if (i >= vf->links) return (OV_EINVAL); if (!vf->seekable && i != 0) return (ov_bitrate(vf, 0)); if (i < 0) { ogg_int64_t bits = 0; int i; for (i = 0; i < vf->links; i++) bits += (vf->offsets[i + 1] - vf->dataoffsets[i]) *8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -