int ffvorbis_decode(ffvorbis *v, const char *pkt, size_t len) { enum { R_HDR, R_TAGS, R_TAG, R_BOOK, R_DATA }; int r; if (len == 0) return FFVORBIS_RMORE; ogg_packet opkt; ffmem_tzero(&opkt); opkt.packet = (void*)pkt, opkt.bytes = len; opkt.packetno = v->pktno++; opkt.granulepos = -1; opkt.e_o_s = v->fin; switch (v->state) { case R_HDR: if (0 != vorb_info(pkt, len, &v->info.channels, &v->info.rate, &v->info.bitrate_nominal)) return ERR(v, FFVORBIS_EPKT); if (0 != (r = vorbis_decode_init(&v->vctx, &opkt))) return ERR(v, r); v->state = R_TAGS; return FFVORBIS_RHDR; case R_TAGS: if (NULL == (v->vtag.data = vorb_comm(pkt, len, &v->vtag.datalen))) return ERR(v, FFVORBIS_ETAG); v->state = R_TAG; // break case R_TAG: r = ffvorbtag_parse(&v->vtag); if (r == FFVORBTAG_ERR) return ERR(v, FFVORBIS_ETAG); else if (r == FFVORBTAG_DONE) { if (!(v->vtag.datalen != 0 && v->vtag.data[0] == 1)) return ERR(v, FFVORBIS_ETAG); v->state = R_BOOK; return FFVORBIS_RHDRFIN; } return FFVORBIS_RTAG; case R_BOOK: if (0 != (r = vorbis_decode_init(&v->vctx, &opkt))) return ERR(v, r); v->state = R_DATA; return FFVORBIS_RMORE; } r = vorbis_decode(v->vctx, &opkt, &v->pcm); if (r < 0) return v->err = r, FFVORBIS_RWARN; else if (r == 0) return FFVORBIS_RMORE; if (v->seek_sample != (uint64)-1) { if (v->seek_sample < v->cursample) { //couldn't find the target packet within the page v->seek_sample = v->cursample; } uint skip = ffmin(v->seek_sample - v->cursample, r); v->cursample += skip; if (v->cursample != v->seek_sample || (uint)r == skip) return FFVORBIS_RMORE; //not yet reached the target packet v->seek_sample = (uint64)-1; for (uint i = 0; i != v->info.channels; i++) { v->pcm_arr[i] = v->pcm[i] + skip; } v->pcm = v->pcm_arr; r -= skip; } if (v->total_samples != (uint64)-1) r = ffmin(r, v->total_samples - v->cursample); v->pkt_samples = r; v->pcmlen = r * sizeof(float) * v->info.channels; v->cursample += r; return FFVORBIS_RDATA; }
int main(int argc,char *argv[]){ int c,long_option_index; int eof=0; int vorbiscount=0; /* get options */ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){ switch(c){ case 'c': codebook_p=1; break; case 'g': pageinfo_p=1; break; case 'h': usage(stdout); exit(0); case 'H': headerinfo_p=1; break; case 'p': packetinfo_p=1; break; case 's': streaminfo_p=1; break; case 't': truncpacket_p=1; break; case 'v': codebook_p=1; pageinfo_p=1; headerinfo_p=1; packetinfo_p=1; streaminfo_p=1; truncpacket_p=1; warn_p=1; break; case 'w': warn_p=1; break; default: usage(stderr); exit(1); } } /* set up sync */ oy=ogg2_sync_create(); os=ogg2_stream_create(0); while(!eof){ long ret; long garbagecounter=0; long pagecounter=0; long packetcounter=0; int initialphase=0; memset(&vi,0,sizeof(vi)); /* packet parsing loop */ while(1){ /* is there a packet available? */ if(ogg2_stream_packetout(os,&op)>0){ /* yes, process it */ if(packetcounter<3){ /* header packet */ ret=vorbis_info_headerin(&vi,&op); if(ret){ switch(packetcounter){ case 0: /* initial header packet */ if((streaminfo_p || warn_p || headerinfo_p) && syncp) printf("WARN stream: page did not contain a valid Vorbis I " "identification\n" " header. Stream is not decodable as " "Vorbis I.\n\n"); break; case 1: if((streaminfo_p || warn_p || headerinfo_p) && syncp) printf("WARN stream: next packet is not a valid Vorbis I " "comment header as expected.\n" " Stream is not decodable as " "Vorbis I.\n\n"); break; case 2: if((streaminfo_p || warn_p || headerinfo_p) && syncp) printf("WARN stream: next packet is not a valid Vorbis I " "setup header as expected.\n" " Stream is not decodable as " "Vorbis I.\n\n"); break; } syncp=0; }else{ syncp=1; packetcounter++; if(packetcounter==3){ if(streaminfo_p || headerinfo_p) printf("info stream: Vorbis I header triad parsed successfully.\n\n"); vorbiscount++; } } }else{ /* audio packet */ vorbis_decode(&vi,&op); packetcounter++; } continue; } /* is there a page available? */ ret=ogg2_sync_pageseek(oy,&og); if(ret<0){ garbagecounter-=ret; } if(ret>0){ /* Garbage between pages? */ if(garbagecounter){ if(streaminfo_p || warn_p || pageinfo_p) fprintf(stdout,"WARN stream: %ld bytes of garbage before page %ld\n\n", garbagecounter,pagecounter); garbagecounter=0; } if(initialphase && !ogg2_page_bos(&og)){ /* initial header pages phase has ended */ if(streaminfo_p || headerinfo_p){ printf("info stream: All identification header pages parsed.\n" " %d logical stream%s muxed in this link.\n\n", initialphase,(initialphase==1?"":"s")); if(initialphase>1 && (warn_p || streaminfo_p)) printf("WARN stream: A 'Vorbis I audio stream' must contain uninterleaved\n" " Vorbis I logical streams only. This is a legal\n" " multimedia Ogg stream, but not a Vorbis I audio\n" " stream.\n\n"); } initialphase=0; } if(pageinfo_p)dump_page(&og); /* is this a stream transition? */ if(ogg2_page_bos(&og) || pagecounter==0){ if(initialphase){ /* we're in a muxed stream, which is illegal for Vorbis I audio-only, but perfectly legal for Ogg. */ if(!syncp){ /* we've not yet seen the Vorbis header go past; keep trying new streams */ ogg2_stream_reset_serialno(os,ogg2_page_serialno(&og)); } }else{ /* first new packet, signals new stream link. Dump the current vorbis stream, if any */ ogg2_stream_reset_serialno(os,ogg2_page_serialno(&og)); memset(&vi,0,sizeof(vi)); packetcounter=0; vorbis_info_clear(&vi); } initialphase++; /* got an initial page. Is it beginning of stream? */ if(!ogg2_page_bos(&og) && pagecounter==0 && streaminfo_p) if(warn_p || streaminfo_p) fprintf(stdout,"WARN stream: first page (0) is not marked beginning of stream.\n\n"); } ogg2_stream_pagein(os,&og); pagecounter++; continue; } if(get_data()<=0){ eof=1; break; } } } if(streaminfo_p) fprintf(stdout, "\nHit physical end of stream at %ld bytes.\n", ftell(stdin)); if(vorbiscount==0) fprintf(stdout,"No logical Vorbis streams found in data.\n"); else fprintf(stdout,"%d logical Vorbis stream%s found in data.\n", vorbiscount,(vorbiscount==1?"":"s")); fprintf(stdout,"Done.\n"); ogg2_page_release(&og); ogg2_stream_destroy(os); ogg2_sync_destroy(oy); return 0; }