int get_packet(ogg_sync_state *oy,ogg_stream_state *os,int *init,ogg_packet *op) { char *buffer; size_t bytes; ogg_page og; /* try to get a packet from the stream */ if (*init && ogg_stream_packetout(os,op)) return 0; /* read data and feed the pages to ogg */ buffer=ogg_sync_buffer(oy,4096); bytes=fread(buffer,1,4096,stdin); if (bytes==0) return 1; /* we're done */ ogg_sync_wrote(oy,bytes); while (ogg_sync_pageout(oy,&og)>0) { if (!*init && ogg_page_bos(&og)) { ogg_stream_init(os,ogg_page_serialno(&og)); } ogg_stream_pagein(os,&og); if (!*init && ogg_page_bos(&og)) { ogg_packet op; ogg_stream_packetpeek(os,&op); if (op.bytes>=8 && !memcmp(op.packet,"\200kate\0\0\0",8)) { *init=1; } else { ogg_stream_clear(os); } } } /* try again with the new data */ return get_packet(oy,os,init,op); }
static ChainCodec get_page_codec (ogg_page * page) { ChainCodec codec = CODEC_UNKNOWN; ogg_stream_state state; ogg_packet packet; ogg_stream_init (&state, ogg_page_serialno (page)); if (ogg_stream_pagein (&state, page) == 0) { if (ogg_stream_packetpeek (&state, &packet) > 0) { if (strncmp ((char *) &packet.packet[1], "vorbis", strlen ("vorbis")) == 0) codec = CODEC_VORBIS; else if (strncmp ((char *) &packet.packet[1], "theora", strlen ("theora")) == 0) codec = CODEC_THEORA; else if (strncmp ((char *) &packet.packet[0], "Speex ", strlen ("Speex ")) == 0) codec = CODEC_SPEEX; } } ogg_stream_clear (&state); return codec; }
static PyObject * py_ogg_ogg_stream_packetpeek(PyObject *self, PyObject *args) { int c_out; int size1, size2; ogg_stream_state * os; ogg_packet * op; PyArg_ParseTuple(args, "s#s#", &os, &size1, &op, &size2); c_out = ogg_stream_packetpeek(os, op); return Py_BuildValue("i", c_out); };
bool OggReader::compact_eval_state(ogg_stream_state* os) { bool readFarFromEnding = (os->body_storage - os->body_returned >= 2 * 1024 * 1024); // try to minimize compact operation, but... bool hasAudio = (ogg_stream_packetpeek(&movie->v_osstate, NULL) > 0); // ... if we run out of audio, don't risk audio glitches! bool pastMinimumReturn = (os->body_returned > 3 * 1024 * 1024); return !readFarFromEnding || (!hasAudio && movie->vorbis_p /*&& !::cfeof(this->file) && !buffering **/ && pastMinimumReturn); }
/* * Class: org_tritonus_lowlevel_ogg_StreamState * Method: packetPeek * Signature: (Lorg/tritonus/lowlevel/ogg/Packet;)I */ JNIEXPORT jint JNICALL Java_org_tritonus_lowlevel_ogg_StreamState_packetPeek (JNIEnv* env, jobject obj, jobject packet) { ogg_stream_state* handle; ogg_packet* packetHandle; int nReturn; if (debug_flag) { fprintf(debug_file, "Java_org_tritonus_lowlevel_ogg_StreamState_packetPeek(): begin\n"); } handle = getHandle(env, obj); packetHandle = getPacketNativeHandle(env, packet); nReturn = ogg_stream_packetpeek(handle, packetHandle); if (debug_flag) { fprintf(debug_file, "Java_org_tritonus_lowlevel_ogg_StreamState_packetPeek(): end\n"); } return nReturn; }
void test_pack(const int *pl, const int **headers){ unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ long inptr=0; long outptr=0; long deptr=0; long depacket=0; long granule_pos=7,pageno=0; int i,j,packets,pageout=0; int eosflag=0; int bosflag=0; ogg_stream_reset(&os_en); ogg_stream_reset(&os_de); ogg_sync_reset(&oy); for(packets=0;;packets++)if(pl[packets]==-1)break; for(i=0;i<packets;i++){ /* construct a test packet */ ogg_packet op; int len=pl[i]; op.packet=data+inptr; op.bytes=len; op.e_o_s=(pl[i+1]<0?1:0); op.granulepos=granule_pos; granule_pos+=1024; for(j=0;j<len;j++)data[inptr++]=i+j; /* submit the test packet */ ogg_stream_packetin(&os_en,&op); /* retrieve any finished pages */ { ogg_page og; while(ogg_stream_pageout(&os_en,&og)){ /* We have a page. Check it carefully */ fprintf(stderr,"%ld, ",pageno); if(headers[pageno]==NULL){ fprintf(stderr,"coded too many pages!\n"); exit(1); } check_page(data+outptr,headers[pageno],&og); outptr+=og.body_len; pageno++; /* have a complete page; submit it to sync/decode */ { ogg_page og_de; ogg_packet op_de,op_de2; char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len); memcpy(buf,og.header,og.header_len); memcpy(buf+og.header_len,og.body,og.body_len); ogg_sync_wrote(&oy,og.header_len+og.body_len); while(ogg_sync_pageout(&oy,&og_de)>0){ /* got a page. Happy happy. Verify that it's good. */ check_page(data+deptr,headers[pageout],&og_de); deptr+=og_de.body_len; pageout++; /* submit it to deconstitution */ ogg_stream_pagein(&os_de,&og_de); /* packets out? */ while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ ogg_stream_packetpeek(&os_de,NULL); ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ /* verify peek and out match */ if(memcmp(&op_de,&op_de2,sizeof(op_de))){ fprintf(stderr,"packetout != packetpeek! pos=%ld\n", depacket); exit(1); } /* verify the packet! */ /* check data */ if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", depacket); exit(1); } /* check bos flag */ if(bosflag==0 && op_de.b_o_s==0){ fprintf(stderr,"b_o_s flag not set on packet!\n"); exit(1); } if(bosflag && op_de.b_o_s){ fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); exit(1); } bosflag=1; depacket+=op_de.bytes; /* check eos flag */ if(eosflag){ fprintf(stderr,"Multiple decoded packets with eos flag!\n"); exit(1); } if(op_de.e_o_s)eosflag=1; /* check granulepos flag */ if(op_de.granulepos!=-1){ fprintf(stderr," granule:%ld ",(long)op_de.granulepos); } } } } } } } _ogg_free(data); if(headers[pageno]!=NULL){ fprintf(stderr,"did not write last page!\n"); exit(1); } if(headers[pageout]!=NULL){ fprintf(stderr,"did not decode last page!\n"); exit(1); } if(inptr!=outptr){ fprintf(stderr,"encoded page data incomplete!\n"); exit(1); } if(inptr!=deptr){ fprintf(stderr,"decoded page data incomplete!\n"); exit(1); } if(inptr!=depacket){ fprintf(stderr,"decoded packet data incomplete!\n"); exit(1); } if(!eosflag){ fprintf(stderr,"Never got a packet with EOS set!\n"); exit(1); } fprintf(stderr,"ok.\n"); }
/* this is called for each file to process */ enum codec_status codec_run(void) { int error = CODEC_ERROR; intptr_t param; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int64_t page_granule = 0; int stream_init = 0; int sample_rate = 48000; OpusDecoder *st = NULL; OpusHeader header; int ret; unsigned long strtoffset = ci->id3->offset; int skip = 0; int64_t seek_target; uint64_t granule_pos; ogg_malloc_init(); global_stack = 0; #if defined(CPU_COLDFIRE) /* EMAC rounding is disabled because of MULT16_32_Q15, which will be inaccurate with rounding in its current incarnation */ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); #endif /* pre-init the ogg_sync_state buffer, so it won't need many reallocs */ ogg_sync_init(&oy); oy.storage = 64*1024; oy.data = _ogg_malloc(oy.storage); /* allocate output buffer */ uint16_t *output = (uint16_t*) _ogg_malloc(MAX_FRAME_SIZE*sizeof(uint16_t)); ci->seek_buffer(0); ci->set_elapsed(0); while (1) { enum codec_command_action action = ci->get_command(¶m); if (action == CODEC_ACTION_HALT) break; if (action == CODEC_ACTION_SEEK_TIME) { if (st != NULL) { /* calculate granule to seek to (including seek rewind) */ seek_target = (48LL * param) + header.preskip; skip = MIN(seek_target, SEEK_REWIND); seek_target -= skip; LOGF("Opus seek page:%lld,%lld,%ld\n", seek_target, page_granule, (long)param); speex_seek_page_granule(seek_target, page_granule, &oy, &os); } ci->set_elapsed(param); ci->seek_complete(); } /*Get the ogg buffer for writing*/ if (get_more_data(&oy) < 1) { goto done; } /* Loop for all complete pages we got (most likely only one) */ while (ogg_sync_pageout(&oy, &og) == 1) { if (stream_init == 0) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = 1; } /* Add page to the bitstream */ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); granule_pos = page_granule; /* Do this to avoid allocating space for huge comment packets (embedded Album Art) */ if(os.packetno == 1 && ogg_stream_packetpeek(&os, &op) != 1){ ogg_sync_reset(&oy); } while ((ogg_stream_packetout(&os, &op) == 1) && !op.e_o_s) { if (op.packetno == 0){ /* identification header */ if (opus_header_parse(op.packet, op.bytes, &header) == 0) { LOGF("Could not parse header"); goto done; } skip = header.preskip; st = opus_decoder_create(sample_rate, header.channels, &ret); if (ret != OPUS_OK) { LOGF("opus_decoder_create failed %d", ret); goto done; } LOGF("Decoder inited"); codec_set_replaygain(ci->id3); opus_decoder_ctl(st, OPUS_SET_GAIN(header.gain)); ci->configure(DSP_SET_FREQUENCY, sample_rate); ci->configure(DSP_SET_SAMPLE_DEPTH, 16); ci->configure(DSP_SET_STEREO_MODE, (header.channels == 2) ? STEREO_INTERLEAVED : STEREO_MONO); } else if (op.packetno == 1) { /* Comment header */ } else { if (strtoffset) { ci->seek_buffer(strtoffset); ogg_sync_reset(&oy); strtoffset = 0; break;//next page } /* report progress */ ci->set_elapsed((granule_pos - header.preskip) / 48); /* Decode audio packets */ ret = opus_decode(st, op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); if (ret > 0) { if (skip > 0) { if (ret <= skip) { /* entire output buffer is skipped */ skip -= ret; ret = 0; } else { /* part of output buffer is played */ ret -= skip; ci->pcmbuf_insert(&output[skip * header.channels], NULL, ret); skip = 0; } } else { /* entire buffer is played */ ci->pcmbuf_insert(output, NULL, ret); } granule_pos += ret; } else { if (ret < 0) { LOGF("opus_decode failed %d", ret); goto done; } break; } } } } } LOGF("Returned OK"); error = CODEC_OK; done: ogg_malloc_destroy(); return error; }
static void theora_process(stream_processor *stream, ogg_page *page) { ogg_packet packet; misc_theora_info *inf = stream->data; int i, header=0; int res; ogg_stream_pagein(&stream->os, page); if(inf->doneheaders < 3) header = 1; while(1) { res = ogg_stream_packetout(&stream->os, &packet); if(res < 0) { warn(_("WARNING: discontinuity in stream (%d)\n"), stream->num); continue; } else if (res == 0) break; if(inf->doneheaders < 3) { if(theora_decode_header(&inf->ti, &inf->tc, &packet) < 0) { warn(_("WARNING: Could not decode Theora header " "packet - invalid Theora stream (%d)\n"), stream->num); continue; } inf->doneheaders++; if(inf->doneheaders == 3) { if(ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1) warn(_("WARNING: Theora stream %d does not have headers " "correctly framed. Terminal header page contains " "additional packets or has non-zero granulepos\n"), stream->num); info(_("Theora headers parsed for stream %d, " "information follows...\n"), stream->num); info(_("Version: %d.%d.%d\n"), inf->ti.version_major, inf->ti.version_minor, inf->ti.version_subminor); info(_("Vendor: %s\n"), inf->tc.vendor); info(_("Width: %d\n"), inf->ti.frame_width); info(_("Height: %d\n"), inf->ti.frame_height); info(_("Total image: %d by %d, crop offset (%d, %d)\n"), inf->ti.width, inf->ti.height, inf->ti.offset_x, inf->ti.offset_y); if(inf->ti.offset_x + inf->ti.frame_width > inf->ti.width) warn(_("Frame offset/size invalid: width incorrect\n")); if(inf->ti.offset_y + inf->ti.frame_height > inf->ti.height) warn(_("Frame offset/size invalid: height incorrect\n")); if(inf->ti.fps_numerator == 0 || inf->ti.fps_denominator == 0) warn(_("Invalid zero framerate\n")); else info(_("Framerate %d/%d (%.02f fps)\n"), inf->ti.fps_numerator, inf->ti.fps_denominator, (float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator); if(inf->ti.aspect_numerator == 0 || inf->ti.aspect_denominator == 0) { info(_("Aspect ratio undefined\n")); } else { float frameaspect = (float)inf->ti.frame_width/(float)inf->ti.frame_height * (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator; info(_("Pixel aspect ratio %d:%d (%f:1)\n"), inf->ti.aspect_numerator, inf->ti.aspect_denominator, (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator); if(fabs(frameaspect - 4.0/3.0) < 0.02) info(_("Frame aspect 4:3\n")); else if(fabs(frameaspect - 16.0/9.0) < 0.02) info(_("Frame aspect 16:9\n")); else info(_("Frame aspect %f:1\n"), frameaspect); } if(inf->ti.colorspace == OC_CS_ITU_REC_470M) info(_("Colourspace: Rec. ITU-R BT.470-6 System M (NTSC)\n")); else if(inf->ti.colorspace == OC_CS_ITU_REC_470BG) info(_("Colourspace: Rec. ITU-R BT.470-6 Systems B and G (PAL)\n")); else info(_("Colourspace unspecified\n")); if(inf->ti.pixelformat == OC_PF_420) info(_("Pixel format 4:2:0\n")); else if(inf->ti.pixelformat == OC_PF_422) info(_("Pixel format 4:2:2\n")); else if(inf->ti.pixelformat == OC_PF_444) info(_("Pixel format 4:4:4\n")); else warn(_("Pixel format invalid\n")); info(_("Target bitrate: %d kbps\n"), inf->ti.target_bitrate/1000); info(_("Nominal quality setting (0-63): %d\n"), inf->ti.quality); if(inf->tc.comments > 0) info(_("User comments section follows...\n")); for(i=0; i < inf->tc.comments; i++) { char *comment = inf->tc.user_comments[i]; check_xiph_comment(stream, i, comment, inf->tc.comment_lengths[i]); } } } else { ogg_int64_t framenum; ogg_int64_t iframe,pframe; ogg_int64_t gp = packet.granulepos; if(gp > 0) { iframe=gp>>inf->ti.granule_shift; pframe=gp-(iframe<<inf->ti.granule_shift); framenum = iframe+pframe; if(inf->framenum_expected >= 0 && inf->framenum_expected != framenum) { warn(_("WARNING: Expected frame %" I64FORMAT ", got %" I64FORMAT "\n"), inf->framenum_expected, framenum); } inf->framenum_expected = framenum + 1; } else if (inf->framenum_expected >= 0) { inf->framenum_expected++; } }
static int init_vorbis(mm_file *mf, ogg_page *pg) { int pkts = 0; int res = 0; int rval = 0; vorbis_block *vo_blk = NULL; vorbis_info *vo_info = NULL; vorbis_comment vo_comm; ogg_packet pkt; ogg_stream_state stream; assert(mf); vo_info = (vorbis_info *)xmalloc(sizeof(*vo_info)); vorbis_info_init(vo_info); vorbis_comment_init(&vo_comm); ogg_stream_init(&stream, ogg_page_serialno(pg)); if (ogg_page_packets(pg) != 1 || ogg_page_granulepos(pg) != 0) { goto end; } if (ogg_stream_pagein(&stream, pg) < 0) /* should not happen */ { goto end; } /* * Three first packets must go successfully through the loop. */ for (pkts = 0; pkts < 3; ++pkts) { while ((res = ogg_stream_packetpeek(&stream, &pkt)) != 1) { if (res < 0 || get_page(mf, pg) <= 0 || ogg_stream_pagein(&stream, pg) < 0) { rval = -1; goto end; } } switch (vorbis_synthesis_headerin(vo_info, &vo_comm, &pkt)) { case 0: break; case OV_EBADHEADER: INFO1("bad vorbis header"); case OV_ENOTVORBIS: default: goto end; } /* decode successful so grab packet */ ogg_stream_packetout(&stream, &pkt); } /* maybe print something about comment or etc? */ mf->audio_ctx = (vorbis_dsp_state *)xmalloc(sizeof(*mf->audio_ctx)); mf->audio = (ogg_stream_state *)xmalloc(sizeof(*mf->audio)); vo_blk = (vorbis_block *)xmalloc(sizeof(*vo_blk)); memcpy(mf->audio, &stream, sizeof(stream)); vorbis_synthesis_init(mf->audio_ctx, vo_info); vorbis_block_init(mf->audio_ctx, vo_blk); mf->audio_info = vo_info; mf->audio_blk = vo_blk; rval = 1; end: vorbis_comment_clear(&vo_comm); if (rval <= 0) { ogg_stream_clear(&stream); vorbis_info_clear(vo_info); free(vo_info); } return rval; }
static int init_theora(mm_file *mf, ogg_page *pg) { int pkts = 0; int res = 0; int rval = 0; theora_info *th_info = NULL; theora_comment th_comm; ogg_packet pkt; ogg_stream_state stream; assert(mf); th_info = (theora_info *)xmalloc(sizeof(*mf->video_info)); theora_info_init(th_info); theora_comment_init(&th_comm); ogg_stream_init(&stream, ogg_page_serialno(pg)); if (ogg_page_packets(pg) != 1 || ogg_page_granulepos(pg) != 0) { goto end; } if (ogg_stream_pagein(&stream, pg)) /* should not happen */ { goto end; } /* Three first packets must go successfully through the loop. */ for (pkts = 0; pkts < 3; ++pkts) { while ((res = ogg_stream_packetpeek(&stream, &pkt)) != 1) { if (res < 0 || get_page(mf, pg) <= 0 || ogg_stream_pagein(&stream, pg) < 0) { rval = -1; goto end; } } switch (theora_decode_header(th_info, &th_comm, &pkt)) { case 0: break; case OC_VERSION: case OC_NEWPACKET: INFO1("incompatible theora file"); /* fall through */ case OC_BADHEADER: default: goto end; } /* decode successful so grab packet */ ogg_stream_packetout(&stream, &pkt); } mf->video_ctx = (theora_state *)xmalloc(sizeof(*mf->video_ctx)); mf->video = (ogg_stream_state *)xmalloc(sizeof(*mf->video)); memcpy(mf->video, &stream, sizeof(stream)); theora_decode_init(mf->video_ctx, th_info); mf->video_info = th_info; rval = 1; end: theora_comment_clear(&th_comm); if (rval <= 0) { ogg_stream_clear(&stream); theora_info_clear(th_info); free(th_info); mf->video_info = NULL; } return rval; }
static void vorbis_process(stream_processor *stream, ogg_page *page, MP3FILE *pmp3) { ogg_packet packet; misc_vorbis_info *inf = stream->data; int i, header=0; int k; ogg_stream_pagein(&stream->os, page); if(inf->doneheaders < 3) header = 1; while(ogg_stream_packetout(&stream->os, &packet) > 0) { if(inf->doneheaders < 3) { if(vorbis_synthesis_headerin(&inf->vi, &inf->vc, &packet) < 0) { DPRINTF(E_WARN, L_SCAN, "Could not decode vorbis header " "packet - invalid vorbis stream (%d)\n", stream->num); continue; } inf->doneheaders++; if(inf->doneheaders == 3) { if(ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1) DPRINTF(E_WARN, L_SCAN, "Vorbis stream %d does not have headers " "correctly framed. Terminal header page contains " "additional packets or has non-zero granulepos\n", stream->num); DPRINTF(E_DBG, L_SCAN, "Vorbis headers parsed for stream %d, " "information follows...\n", stream->num); /* DPRINTF(E_INF, L_SCAN, "Version: %d\n", inf->vi.version); k = 0; while(releases[k].vendor_string) { if(!strcmp(inf->vc.vendor, releases[k].vendor_string)) { //PENDING: DPRINTF(E_INF, L_SCAN, "Vendor: %s (%s)\n", inf->vc.vendor, releases[k].desc); break; } k++; } if(!releases[k].vendor_string) DPRINTF(E_INF, L_SCAN, "Vendor: %s\n", inf->vc.vendor);*/ DPRINTF(E_DBG, L_SCAN, "Channels: %d\n", inf->vi.channels); DPRINTF(E_DBG, L_SCAN, "Rate: %ld\n\n", inf->vi.rate); pmp3->samplerate = inf->vi.rate; if(inf->vi.bitrate_nominal > 0) { DPRINTF(E_DBG, L_SCAN, "Nominal bitrate: %f kb/s\n", (double)inf->vi.bitrate_nominal / 1000.0); pmp3->bitrate = inf->vi.bitrate_nominal / 1000; } else { int upper_rate, lower_rate; DPRINTF(E_DBG, L_SCAN, "Nominal bitrate not set\n"); /* Average the lower and upper bitrates if set */ upper_rate = 0; lower_rate = 0; if(inf->vi.bitrate_upper > 0) { DPRINTF(E_DBG, L_SCAN, "Upper bitrate: %f kb/s\n", (double)inf->vi.bitrate_upper / 1000.0); upper_rate = inf->vi.bitrate_upper; } else { DPRINTF(E_DBG, L_SCAN, "Upper bitrate not set\n"); } if(inf->vi.bitrate_lower > 0) { DPRINTF(E_DBG, L_SCAN,"Lower bitrate: %f kb/s\n", (double)inf->vi.bitrate_lower / 1000.0); lower_rate = inf->vi.bitrate_lower;; } else { DPRINTF(E_DBG, L_SCAN, "Lower bitrate not set\n"); } if (upper_rate && lower_rate) { pmp3->bitrate = (upper_rate + lower_rate) / 2; } else { pmp3->bitrate = upper_rate + lower_rate; } } if(inf->vc.comments > 0) DPRINTF(E_DBG, L_SCAN, "User comments section follows...\n"); for(i=0; i < inf->vc.comments; i++) { char *sep = strchr(inf->vc.user_comments[i], '='); char *decoded; int j; int broken = 0; unsigned char *val; int bytes; int remaining; if(sep == NULL) { DPRINTF(E_WARN, L_SCAN, "Comment %d in stream %d is invalidly " "formatted, does not contain '=': \"%s\"\n", i, stream->num, inf->vc.user_comments[i]); continue; } for(j=0; j < sep-inf->vc.user_comments[i]; j++) { if(inf->vc.user_comments[i][j] < 0x20 || inf->vc.user_comments[i][j] > 0x7D) { DPRINTF(E_WARN, L_SCAN, "Warning: Invalid comment fieldname in " "comment %d (stream %d): \"%s\"\n", i, stream->num, inf->vc.user_comments[i]); broken = 1; break; } } if(broken) continue; val = inf->vc.user_comments[i]; j = sep-inf->vc.user_comments[i]+1; while(j < inf->vc.comment_lengths[i]) { remaining = inf->vc.comment_lengths[i] - j; if((val[j] & 0x80) == 0) bytes = 1; else if((val[j] & 0x40) == 0x40) { if((val[j] & 0x20) == 0) bytes = 2; else if((val[j] & 0x10) == 0) bytes = 3; else if((val[j] & 0x08) == 0) bytes = 4; else if((val[j] & 0x04) == 0) bytes = 5; else if((val[j] & 0x02) == 0) bytes = 6; else { DPRINTF(E_WARN, L_SCAN, "Illegal UTF-8 sequence in " "comment %d (stream %d): length " "marker wrong\n", i, stream->num); broken = 1; break; } } else { DPRINTF(E_WARN, L_SCAN, "Illegal UTF-8 sequence in comment " "%d (stream %d): length marker wrong\n", i, stream->num); broken = 1; break; } if(bytes > remaining) { DPRINTF(E_WARN, L_SCAN, "Illegal UTF-8 sequence in comment " "%d (stream %d): too few bytes\n", i, stream->num); broken = 1; break; } switch(bytes) { case 1: /* No more checks needed */ break; case 2: if((val[j+1] & 0xC0) != 0x80) broken = 1; if((val[j] & 0xFE) == 0xC0) broken = 1; break; case 3: if(!((val[j] == 0xE0 && val[j+1] >= 0xA0 && val[j+1] <= 0xBF && (val[j+2] & 0xC0) == 0x80) || (val[j] >= 0xE1 && val[j] <= 0xEC && (val[j+1] & 0xC0) == 0x80 && (val[j+2] & 0xC0) == 0x80) || (val[j] == 0xED && val[j+1] >= 0x80 && val[j+1] <= 0x9F && (val[j+2] & 0xC0) == 0x80) || (val[j] >= 0xEE && val[j] <= 0xEF && (val[j+1] & 0xC0) == 0x80 && (val[j+2] & 0xC0) == 0x80))) broken = 1; if(val[j] == 0xE0 && (val[j+1] & 0xE0) == 0x80) broken = 1; break; case 4: if(!((val[j] == 0xF0 && val[j+1] >= 0x90 && val[j+1] <= 0xBF && (val[j+2] & 0xC0) == 0x80 && (val[j+3] & 0xC0) == 0x80) || (val[j] >= 0xF1 && val[j] <= 0xF3 && (val[j+1] & 0xC0) == 0x80 && (val[j+2] & 0xC0) == 0x80 && (val[j+3] & 0xC0) == 0x80) || (val[j] == 0xF4 && val[j+1] >= 0x80 && val[j+1] <= 0x8F && (val[j+2] & 0xC0) == 0x80 && (val[j+3] & 0xC0) == 0x80))) broken = 1; if(val[j] == 0xF0 && (val[j+1] & 0xF0) == 0x80) broken = 1; break; /* 5 and 6 aren't actually allowed at this point*/ case 5: broken = 1; break; case 6: broken = 1; break; } if(broken) { DPRINTF(E_WARN, L_SCAN, "Illegal UTF-8 sequence in comment " "%d (stream %d): invalid sequence\n", i, stream->num); broken = 1; break; } j += bytes; } if(!broken) { /* if(utf8_decode(sep+1, &decoded) < 0) { DPRINTF(E_WARN, L_SCAN, "Failure in utf8 decoder. This " "should be impossible\n"); continue; }*/ DPRINTF(E_DBG, L_SCAN, "\t%s\n", inf->vc.user_comments[i]); if (!strncasecmp(inf->vc.user_comments[i],"TITLE",5)) { pmp3->title = strdup(sep + 1); DPRINTF(E_DBG, L_SCAN, "Mapping %s to title.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "ARTIST", 6)) { pmp3->artist = strdup(sep + 1); DPRINTF(E_DBG, L_SCAN, "Mapping %s to artist.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "ALBUM", 5)) { pmp3->album = strdup(sep + 1); DPRINTF(E_DBG, L_SCAN, "Mapping %s to album.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "TRACKNUMBER", 11)) { pmp3->track = atoi(sep + 1); DPRINTF(E_INF, L_SCAN, "Mapping %s to track.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "GENRE", 5)) { pmp3->genre = strdup(sep + 1); DPRINTF(E_INF, L_SCAN, "Mapping %s to genre.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "DATE", 4)) { //PENDING: Should only parse first 4 characters pmp3->year = atoi(sep + 1); DPRINTF(E_INF, L_SCAN, "Mapping %s to year.\n", sep + 1); } else if (!strncasecmp(inf->vc.user_comments[i], "COMMENT", 7)) { pmp3->comment = strdup(sep + 1); DPRINTF(E_INF, L_SCAN, "Mapping %s to comment.\n", sep + 1); } *sep = 0; /* free(decoded);*/ } } } } } if(!header) { ogg_int64_t gp = ogg_page_granulepos(page); if(gp > 0) { if(gp < inf->lastgranulepos) #ifdef _WIN32 DPRINTF(E_WARN, L_SCAN, "granulepos in stream %d decreases from %I64d to %I64d", stream->num, inf->lastgranulepos, gp); #else DPRINTF(E_WARN, L_SCAN, "granulepos in stream %d decreases from %lld to %lld", stream->num, inf->lastgranulepos, gp); #endif inf->lastgranulepos = gp; } else { DPRINTF(E_WARN, L_SCAN, "Negative granulepos on vorbis stream outside of headers. This file was created by a buggy encoder\n"); } if(inf->firstgranulepos < 0) { /* Not set yet */ } inf->bytes += page->header_len + page->body_len; } }
int main(int argc,char *argv[]){ ogg_packet op; int long_option_index; int c; struct timeb start; struct timeb after; struct timeb last; int fps_only=0; int frames = 0; FILE *infile = stdin; outfile = stdout; #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */ /* Beware the evil ifdef. We avoid these where we can, but this one we cannot. Don't add any more, you'll probably go to hell if you do. */ _setmode( _fileno( stdin ), _O_BINARY ); _setmode( _fileno( stdout ), _O_BINARY ); #endif /* Process option arguments. */ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){ switch(c){ case 'o': if(strcmp(optarg,"-")!=0){ outfile=fopen(optarg,"wb"); if(outfile==NULL){ fprintf(stderr,"Unable to open output file '%s'\n", optarg); exit(1); } }else{ outfile=stdout; } break; case 'c': crop=1; break; case 'r': raw=1; break; case 'f': fps_only = 1; outfile = NULL; break; default: usage(); } } if(optind<argc){ infile=fopen(argv[optind],"rb"); if(infile==NULL){ fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]); exit(1); } if(++optind<argc){ usage(); exit(1); } } /*Ok, Ogg parsing. The idea here is we have a bitstream that is made up of Ogg pages. The libogg sync layer will find them for us. There may be pages from several logical streams interleaved; we find the first theora stream and ignore any others. Then we pass the pages for our stream to the libogg stream layer which assembles our original set of packets out of them. It's the packets that libtheora actually knows how to handle.*/ /* start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Theora structures needed in header parsing */ th_comment_init(&tc); th_info_init(&ti); /*Ogg file open; parse the headers. Theora (like Vorbis) depends on some initial header packets for decoder setup and initialization. We retrieve these first before entering the main decode loop.*/ /* Only interested in Theora streams */ while(!stateflag){ int ret=buffer_data(infile,&oy); if(ret==0)break; while(ogg_sync_pageout(&oy,&og)>0){ int got_packet; ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if(!ogg_page_bos(&og)){ /* don't leak the page; get it into the appropriate stream */ queue_page(&og); stateflag=1; break; } ogg_stream_init(&test,ogg_page_serialno(&og)); ogg_stream_pagein(&test,&og); got_packet = ogg_stream_packetpeek(&test,&op); /* identify the codec: try theora */ if((got_packet==1) && !theora_p && (theora_processing_headers= th_decode_headerin(&ti,&tc,&ts,&op))>=0){ /* it is theora -- save this stream state */ memcpy(&to,&test,sizeof(test)); theora_p=1; /*Advance past the successfully processed header.*/ if(theora_processing_headers)ogg_stream_packetout(&to,NULL); }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-bos page parsing */ } /* we're expecting more header packets. */ while(theora_p && theora_processing_headers){ int ret; /* look for further theora headers */ while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){ if(ret<0)continue; theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op); if(theora_processing_headers<0){ fprintf(stderr,"Error parsing Theora stream headers; " "corrupt stream?\n"); exit(1); } else if(theora_processing_headers>0){ /*Advance past the successfully processed header.*/ ogg_stream_packetout(&to,NULL); } theora_p++; } /*Stop now so we don't fail if there aren't enough pages in a short stream.*/ if(!(theora_p && theora_processing_headers))break; /* The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec */ if(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); /* demux into the appropriate stream */ }else{ int ret=buffer_data(infile,&oy); /* someone needs more data */ if(ret==0){ fprintf(stderr,"End of file while searching for codec headers.\n"); exit(1); } } } /* and now we have it all. initialize decoders */ if(theora_p){ dump_comments(&tc); td=th_decode_alloc(&ti,ts); fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n" "Encoded frame content is %dx%d with %dx%d offset\n", to.serialno,ti.frame_width,ti.frame_height, (double)ti.fps_numerator/ti.fps_denominator, ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y); /*{ int arg = 0xffff; th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg)); th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg)); th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg)); arg=10; th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg)); }*/ }else{ /* tear down the partial theora setup */ th_info_clear(&ti); th_comment_clear(&tc); } /*Either way, we're done with the codec setup data.*/ th_setup_free(ts); /* open video */ if(theora_p)open_video(); if(!raw && outfile){ static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422jpeg","444"}; int width; int height; if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){ fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt); exit(1); } if(crop){ int hdec; int vdec; hdec=!(ti.pixel_fmt&1); vdec=!(ti.pixel_fmt&2); if((ti.pic_x&hdec)||(ti.pic_width&hdec) ||(ti.pic_y&vdec)||(ti.pic_height&vdec)){ fprintf(stderr, "Error: Cropped images with odd offsets/sizes and chroma subsampling\n" "cannot be output to YUV4MPEG2. Remove the --crop flag or add the\n" "--raw flag.\n"); exit(1); } width=ti.pic_width; height=ti.pic_height; } else{ width=ti.frame_width; height=ti.frame_height; } fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n", CHROMA_TYPES[ti.pixel_fmt],width,height, ti.fps_numerator,ti.fps_denominator,'p', ti.aspect_numerator,ti.aspect_denominator); } /* install signal handler */ signal (SIGINT, sigint_handler); /*Finally the main decode loop. It's one Theora packet per frame, so this is pretty straightforward if we're not trying to maintain sync with other multiplexed streams. The videobuf_ready flag is used to maintain the input buffer in the libogg stream state. If there's no output frame available at the end of the decode step, we must need more input data. We could simplify this by just using the return code on ogg_page_packetout(), but the flag system extends easily to the case where you care about more than one multiplexed stream (like with audio playback). In that case, just maintain a flag for each decoder you care about, and pull data when any one of them stalls. videobuf_time holds the presentation time of the currently buffered video frame. We ignore this value.*/ stateflag=0; /* playback has not begun */ /* queue any remaining pages from data we buffered but that did not contain headers */ while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } if(fps_only){ ftime(&start); ftime(&last); } while(!got_sigint){ while(theora_p && !videobuf_ready){ /* theora is one in, one out... */ if(ogg_stream_packetout(&to,&op)>0){ if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){ videobuf_time=th_granule_time(td,videobuf_granulepos); videobuf_ready=1; frames++; if(fps_only) ftime(&after); } }else break; } if(fps_only && (videobuf_ready || fps_only==2)){ long ms = after.time*1000.+after.millitm- (last.time*1000.+last.millitm); if(ms>500 || fps_only==1 || (feof(infile) && !videobuf_ready)){ float file_fps = (float)ti.fps_numerator/ti.fps_denominator; fps_only=2; ms = after.time*1000.+after.millitm- (start.time*1000.+start.millitm); fprintf(stderr,"\rframe:%d rate:%.2fx ", frames, frames*1000./(ms*file_fps)); memcpy(&last,&after,sizeof(last)); } } if(!videobuf_ready && feof(infile))break; if(!videobuf_ready){ /* no data yet for somebody. Grab another page */ buffer_data(infile,&oy); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* dumpvideo frame, and get new one */ else if(outfile)video_write(); videobuf_ready=0; } /* end of decoder loop -- close everything */ if(theora_p){ ogg_stream_clear(&to); th_decode_free(td); th_comment_clear(&tc); th_info_clear(&ti); } ogg_sync_clear(&oy); if(infile && infile!=stdin)fclose(infile); if(outfile && outfile!=stdout)fclose(outfile); fprintf(stderr, "\n\n%d frames\n", frames); fprintf(stderr, "\nDone.\n"); return(0); }
static void vorbis_process(stream_processor *stream, ogg_page *page ) { ogg_packet packet; misc_vorbis_info *inf = stream->data; int i, header=0; int k; ogg_stream_pagein(&stream->os, page); if(inf->doneheaders < 3) header = 1; while(ogg_stream_packetout(&stream->os, &packet) > 0) { if(inf->doneheaders < 3) { if(vorbis_synthesis_headerin(&inf->vi, &inf->vc, &packet) < 0) { warn(_("Warning: Could not decode vorbis header " "packet - invalid vorbis stream (%d)\n"), stream->num); continue; } inf->doneheaders++; if(inf->doneheaders == 3) { if(ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1) warn(_("Warning: Vorbis stream %d does not have headers " "correctly framed. Terminal header page contains " "additional packets or has non-zero granulepos\n"), stream->num); info(_("Vorbis headers parsed for stream %d, " "information follows...\n"), stream->num); info(_("Version: %d\n"), inf->vi.version); k = 0; while(releases[k].vendor_string) { if(!strcmp(inf->vc.vendor, releases[k].vendor_string)) { info(_("Vendor: %s (%s)\n"), inf->vc.vendor, releases[k].desc); break; } k++; } if(!releases[k].vendor_string) info(_("Vendor: %s\n"), inf->vc.vendor); info(_("Channels: %d\n"), inf->vi.channels); info(_("Rate: %ld\n\n"), inf->vi.rate); if(inf->vi.bitrate_nominal > 0) info(_("Nominal bitrate: %f kb/s\n"), (double)inf->vi.bitrate_nominal / 1000.0); else info(_("Nominal bitrate not set\n")); if(inf->vi.bitrate_upper > 0) info(_("Upper bitrate: %f kb/s\n"), (double)inf->vi.bitrate_upper / 1000.0); else info(_("Upper bitrate not set\n")); if(inf->vi.bitrate_lower > 0) info(_("Lower bitrate: %f kb/s\n"), (double)inf->vi.bitrate_lower / 1000.0); else info(_("Lower bitrate not set\n")); if(inf->vc.comments > 0) info(_("User comments section follows...\n")); for(i=0; i < inf->vc.comments; i++) { char *comment = inf->vc.user_comments[i]; check_xiph_comment(stream, i, comment, inf->vc.comment_lengths[i]); } } } } if(!header) { ogg_int64_t gp = ogg_page_granulepos(page); if(gp > 0) { if(gp < inf->lastgranulepos) #ifdef _WIN32 warn(_("Warning: granulepos in stream %d decreases from %I64d to %I64d" ), stream->num, inf->lastgranulepos, gp); #else warn(_("Warning: granulepos in stream %d decreases from %lld to %lld" ), stream->num, inf->lastgranulepos, gp); #endif inf->lastgranulepos = gp; } else { warn(_("Negative granulepos on vorbis stream outside of headers. This file was created by a buggy encoder\n")); } if(inf->firstgranulepos < 0) { /* Not set yet */ } inf->bytes += page->header_len + page->body_len; } }
static void theora_process(stream_processor *stream, ogg_page *page) { ogg_packet packet; misc_theora_info *inf = stream->data; int i, header=0; ogg_stream_pagein(&stream->os, page); if(inf->doneheaders < 3) header = 1; while(ogg_stream_packetout(&stream->os, &packet) > 0) { if(inf->doneheaders < 3) { if(theora_decode_header(&inf->ti, &inf->tc, &packet) < 0) { warn(_("Warning: Could not decode theora header " "packet - invalid theora stream (%d)\n"), stream->num); continue; } inf->doneheaders++; if(inf->doneheaders == 3) { if(ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1) warn(_("Warning: Theora stream %d does not have headers " "correctly framed. Terminal header page contains " "additional packets or has non-zero granulepos\n"), stream->num); info(_("Theora headers parsed for stream %d, " "information follows...\n"), stream->num); info(_("Version: %d.%d.%d\n"), inf->ti.version_major, inf->ti.version_minor, inf->ti.version_subminor); info(_("Vendor: %s\n"), inf->tc.vendor); info(_("Width: %d\n"), inf->ti.frame_width); info(_("Height: %d\n"), inf->ti.frame_height); info(_("Total image: %d by %d, crop offset (%d, %d)\n"), inf->ti.width, inf->ti.height, inf->ti.offset_x, inf->ti.offset_y); if(inf->ti.offset_x + inf->ti.frame_width > inf->ti.width) warn(_("Frame offset/size invalid: width incorrect\n")); if(inf->ti.offset_y + inf->ti.frame_height > inf->ti.height) warn(_("Frame offset/size invalid: height incorrect\n")); if(inf->ti.fps_numerator == 0 || inf->ti.fps_denominator == 0) warn(_("Invalid zero framerate\n")); else info(_("Framerate %d/%d (%.02f fps)\n"), inf->ti.fps_numerator, inf->ti.fps_denominator, (float)inf->ti.fps_numerator/(float)inf->ti.fps_denominator); if(inf->ti.aspect_numerator == 0 || inf->ti.aspect_denominator == 0) { info(_("Aspect ratio undefined\n")); } else { float frameaspect = (float)inf->ti.frame_width/(float)inf->ti.frame_height * (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator; info(_("Pixel aspect ratio %d:%d (1:%f)\n"), inf->ti.aspect_numerator, inf->ti.aspect_denominator, (float)inf->ti.aspect_numerator/(float)inf->ti.aspect_denominator); if(abs(frameaspect - 4.0/3.0) < 0.02) info(_("Frame aspect 4:3\n")); else if(abs(frameaspect - 16.0/9.0) < 0.02) info(_("Frame aspect 16:9\n")); else info(_("Frame aspect 1:%d\n"), frameaspect); } if(inf->ti.colorspace == OC_CS_ITU_REC_470M) info(_("Colourspace: Rec. ITU-R BT.470-6 System M (NTSC)\n")); else if(inf->ti.colorspace == OC_CS_ITU_REC_470BG) info(_("Colourspace: Rec. ITU-R BT.470-6 Systems B and G (PAL)\n")); else info(_("Colourspace unspecified\n")); if(inf->ti.pixelformat == OC_PF_420) info(_("Pixel format 4:2:0\n")); else if(inf->ti.pixelformat == OC_PF_422) info(_("Pixel format 4:2:2\n")); else if(inf->ti.pixelformat == OC_PF_444) info(_("Pixel format 4:4:4\n")); else warn(_("Pixel format invalid\n")); info(_("Target bitrate: %d kbps\n"), inf->ti.target_bitrate/1000); info(_("Nominal quality setting (0-63): %d\n"), inf->ti.quality); if(inf->tc.comments > 0) info(_("User comments section follows...\n")); for(i=0; i < inf->tc.comments; i++) { char *comment = inf->tc.user_comments[i]; check_xiph_comment(stream, i, comment, inf->tc.comment_lengths[i]); } } } } if(!header) { ogg_int64_t gp = ogg_page_granulepos(page); if(gp > 0) { if(gp < inf->lastgranulepos) #ifdef _WIN32 warn(_("Warning: granulepos in stream %d decreases from %I64d to %I64d" ), stream->num, inf->lastgranulepos, gp); #else warn(_("Warning: granulepos in stream %d decreases from %lld to %lld" ), stream->num, inf->lastgranulepos, gp); #endif inf->lastgranulepos = gp; } if(inf->firstgranulepos < 0) { /* Not set yet */ } inf->bytes += page->header_len + page->body_len; } }
int main(int argc, char *argv[]) { daala_packet dp; ogg_packet op; int long_option_index; int c; ogg_sync_state oy; ogg_page og; ogg_stream_state to; daala_info di; daala_comment dc; daala_setup_info *ds; daala_dec_ctx *dd; daala_image img; const char *optstring = "o:r"; struct option options [] = { { "output", required_argument, NULL, 'o' }, { "raw", no_argument, NULL, 'r' }, /*Disable YUV4MPEG2 headers:*/ { "version", no_argument, NULL, 0}, { NULL, 0, NULL, 0 } }; int frames = 0; int pix_fmt = 1; int daala_p = 0; int daala_processing_headers = 0; int stateflag = 0; /* single frame video buffering */ int videobuf_ready = 0; int raw = 0; FILE *outfile = NULL; ogg_int32_t pic_width = 0; ogg_int32_t pic_height = 0; ogg_int32_t fps_num = 0; ogg_int32_t fps_denom = 0; FILE *infile = stdin; outfile = stdout; dd = NULL; ds = NULL; daala_log_init(); #ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */ /* Beware the evil ifdef. We avoid these where we can, but this one we cannot. Don't add any more, you'll probably go to hell if you do. */ _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); #endif /* Process option arguments. */ while ((c = getopt_long(argc, argv, optstring, options, &long_option_index)) != EOF) { switch (c) { case 'o': { if (strcmp(optarg, "-") != 0) { outfile = fopen(optarg, "wb"); if (outfile == NULL) { fprintf(stderr, "Unable to open output file '%s'\n", optarg); exit(1); } } else { outfile = stdout; } break; } case 'r': { raw = 1; break; } case 0: { if (strcmp(options[long_option_index].name, "version") == 0) { version(); } break; } default: usage(); break; } } if (optind < argc) { infile = fopen(argv[optind], "rb"); if (infile == NULL) { fprintf(stderr, "Unable to open '%s' for extraction.\n", argv[optind]); exit(1); } if (++optind < argc) { usage(); exit(1); } } /*Ok, Ogg parsing. The idea here is we have a bitstream that is made up of Ogg pages. The libogg sync layer will find them for us. There may be pages from several logical streams interleaved; we find the first daala stream and ignore any others. Then we pass the pages for our stream to the libogg stream layer which assembles our original set of packets out of them. start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Theora structures needed in header parsing */ daala_comment_init(&dc); daala_info_init(&di); /*Ogg file open; parse the headers. Theora (like Vorbis) depends on some initial header packets for decoder setup and initialization. We retrieve these first before entering the main decode loop.*/ /* Only interested in Daala streams */ while (!stateflag) { int ret = buffer_data(infile, &oy); if (ret == 0) break; while (ogg_sync_pageout(&oy, &og) > 0) { int got_packet; ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if (!ogg_page_bos(&og)) { /* don't leak the page; get it into the appropriate stream */ queue_page(&og, &to, daala_p); stateflag = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&og)); ogg_stream_pagein(&test, &og); got_packet = ogg_stream_packetpeek(&test, &op); ogg_to_daala_packet(&dp, &op); /* identify the codec: try daala */ if ((got_packet == 1) && !daala_p && (daala_processing_headers = daala_decode_header_in(&di, &dc, &ds, &dp)) >= 0) { /* it is daala -- save this stream state */ memcpy(&to, &test, sizeof(test)); daala_p = 1; /*Advance past the successfully processed header.*/ if (daala_processing_headers) ogg_stream_packetout(&to, NULL); } else { /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-bos page parsing */ } /* we're expecting more header packets. */ while (daala_p && daala_processing_headers) { int ret; /* look for further daala headers */ while (daala_processing_headers && (ret = ogg_stream_packetpeek(&to, &op))) { if (ret < 0) continue; ogg_to_daala_packet(&dp, &op); daala_processing_headers = daala_decode_header_in(&di, &dc, &ds, &dp); if (daala_processing_headers < 0) { fprintf(stderr, "Error parsing Daala stream headers; " "corrupt stream?\n"); exit(1); } else if (daala_processing_headers >= 0) { /*Advance past the successfully processed header.*/ ogg_stream_packetout(&to, NULL); } daala_p++; } /*Stop now so we don't fail if there aren't enough pages in a short stream.*/ if (!(daala_p && daala_processing_headers)) break; /* The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec */ if (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og, &to, daala_p); /* demux into the appropriate stream */ } else { if (buffer_data(infile, &oy) == 0) { /* someone needs more data */ fprintf(stderr, "End of file while searching for codec headers.\n"); exit(1); } } } /* and now we have it all. initialize decoders */ if (daala_p) { dump_comments(&dc); dd = daala_decode_create(&di, ds); fprintf(stderr, "Ogg logical stream %lx is Daala %dx%d %.02f fps video\n", to.serialno, di.pic_width, di.pic_height, di.timebase_numerator/(double)di.timebase_denominator*di.frame_duration); } else { /* tear down the partial daala setup */ daala_info_clear(&di); daala_comment_clear(&dc); } /*Either way, we're done with the codec setup data.*/ daala_setup_free(ds); if (!raw && outfile) { static const char *CHROMA_TYPES[5] = { "420jpeg", NULL, "422jpeg", "444", "mono" }; pic_width = di.pic_width; pic_height = di.pic_height; fps_num = di.timebase_numerator; fps_denom = di.timebase_denominator*di.frame_duration; if (di.nplanes > 1) { /*calculate pixel_fmt based on the xdec & ydec values from one of the chroma planes.*/ if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 1) { pix_fmt = 0; } else if (di.plane_info[1].xdec == 1 && di.plane_info[1].ydec == 0) { pix_fmt = 2; } else if (di.plane_info[1].xdec == 0 && di.plane_info[1].ydec == 0) { pix_fmt = 3; } } else { pix_fmt = 4; } if (pix_fmt >= 5 || pix_fmt == 1) { fprintf(stderr, "Unknown pixel format: %i\n", pix_fmt); exit(1); } /*Store header information*/ fprintf(outfile, "YUV4MPEG2 W%d H%d F%d:%d Ip A%d:%d C%s\n", pic_width, pic_height, fps_num, fps_denom, di.pixel_aspect_numerator, di.pixel_aspect_denominator, CHROMA_TYPES[pix_fmt]); } /* install signal handler */ signal(SIGINT, sigint_handler); /*Finally the main decode loop. It's one Daala packet per frame, so this is pretty straightforward if we're not trying to maintain sync with other multiplexed streams. The videobuf_ready flag is used to maintain the input buffer in the libogg stream state. If there's no output frame available at the end of the decode step, we must need more input data. We could simplify this by just using the return code on ogg_page_packetout(), but the flag system extends easily to the case where you care about more than one multiplexed stream (like with audio playback). In that case, just maintain a flag for each decoder you care about, and pull data when any one of them stalls.*/ stateflag = 0; /* playback has not begun */ /* queue any remaining pages from data we buffered but that did not contain headers */ while (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og, &to, daala_p); } while (!got_sigint) { while (daala_p && !videobuf_ready) { if (ogg_stream_packetout(&to, &op) > 0) { ogg_to_daala_packet(&dp, &op); if (daala_decode_packet_in(dd, &dp) >= 0) { videobuf_ready = 1; frames++; } } else break; } if (!videobuf_ready && feof(infile)) break; if (!videobuf_ready) { /* no data yet for somebody. Grab another page */ buffer_data(infile, &oy); while (ogg_sync_pageout(&oy, &og) > 0) { queue_page(&og, &to, daala_p); } } /* dumpvideo frame, and get new one */ else if (outfile && daala_decode_img_out(dd, &img)) { video_write(outfile, &img, raw); } videobuf_ready = 0; } /*Flush the output frame buffer of decoder.*/ while (!got_sigint && daala_decode_img_out(dd, &img)) { video_write(outfile, &img, raw); } /* end of decoder loop -- close everything */ if (daala_p) { ogg_stream_clear(&to); daala_decode_free(dd); daala_comment_clear(&dc); daala_info_clear(&di); } ogg_sync_clear(&oy); if (infile && infile != stdin) fclose(infile); if (outfile && outfile != stdout) fclose(outfile); fprintf(stderr, "\n\n%d frames\n", frames); fprintf(stderr, "\nDone.\n"); return 0; }
/* Return 1 if the page is a header page */ static int vorbis_process(RIP_MANAGER_INFO * rmi, stream_processor * stream, ogg_page * page, TRACK_INFO * ti) { ogg_packet packet; misc_vorbis_info *inf = stream->data; int i, header = 0; int k; ogg_stream_pagein(&stream->os, page); if (inf->doneheaders < 3) header = 1; while (ogg_stream_packetout(&stream->os, &packet) > 0) { if (inf->doneheaders < 3) { if (vorbis_synthesis_headerin(&inf->vi, &inf->vc, &packet) < 0) { warn(_("Warning: Could not decode vorbis header " "packet - invalid vorbis stream (%d)\n"), stream->num); continue; } inf->doneheaders++; if (inf->doneheaders == 3) { if (ogg_page_granulepos(page) != 0 || ogg_stream_packetpeek(&stream->os, NULL) == 1) warn(_("Warning: Vorbis stream %d does not have headers " "correctly framed. Terminal header page contains " "additional packets or has non-zero granulepos\n"), stream->num); debug_printf("Vorbis headers parsed for stream %d, " "information follows...\n", stream->num); debug_printf("Version: %d\n", inf->vi.version); k = 0; while (releases[k].vendor_string) { if (!strcmp(inf->vc.vendor, releases[k].vendor_string)) { debug_printf("Vendor: %s (%s)\n", inf->vc.vendor, releases[k].desc); break; } k++; } if (!releases[k].vendor_string) debug_printf("Vendor: %s\n", inf->vc.vendor); debug_printf("Channels: %d\n", inf->vi.channels); debug_printf("Rate: %ld\n\n", inf->vi.rate); if (inf->vi.bitrate_nominal > 0) debug_printf("Nominal bitrate: %f kb/s\n", (double)inf->vi.bitrate_nominal / 1000.0); else debug_printf("Nominal bitrate not set\n"); if (inf->vi.bitrate_upper > 0) debug_printf("Upper bitrate: %f kb/s\n", (double)inf->vi.bitrate_upper / 1000.0); else debug_printf("Upper bitrate not set\n"); if (inf->vi.bitrate_lower > 0) debug_printf("Lower bitrate: %f kb/s\n", (double)inf->vi.bitrate_lower / 1000.0); else debug_printf("Lower bitrate not set\n"); if (inf->vc.comments > 0) debug_printf("User comments section follows...\n"); for (i = 0; i < inf->vc.comments; i++) { char *sep = strchr(inf->vc.user_comments[i], '='); char *decoded; int j; int broken = 0; unsigned char *val; int bytes; int remaining; if (sep == NULL) { warn(_("Warning: Comment %d in stream %d is invalidly " "formatted, does not contain '=': \"%s\"\n"), i, stream->num, inf->vc.user_comments[i]); continue; } for (j = 0; j < sep - inf->vc.user_comments[i]; j++) { if (inf->vc.user_comments[i][j] < 0x20 || inf->vc.user_comments[i][j] > 0x7D) { warn(_("Warning: Invalid comment fieldname in " "comment %d (stream %d): \"%s\"\n"), i, stream->num, inf->vc.user_comments[i]); broken = 1; break; } } if (broken) continue; val = (unsigned char *)inf->vc.user_comments[i]; j = sep - inf->vc.user_comments[i] + 1; while (j < inf->vc.comment_lengths[i]) { remaining = inf->vc.comment_lengths[i] - j; if ((val[j] & 0x80) == 0) bytes = 1; else if ((val[j] & 0x40) == 0x40) { if ((val[j] & 0x20) == 0) bytes = 2; else if ((val[j] & 0x10) == 0) bytes = 3; else if ((val[j] & 0x08) == 0) bytes = 4; else if ((val[j] & 0x04) == 0) bytes = 5; else if ((val[j] & 0x02) == 0) bytes = 6; else { warn(_("Warning: Illegal UTF-8 sequence in " "comment %d (stream %d): length " "marker wrong\n"), i, stream->num); broken = 1; break; } } else { warn(_("Warning: Illegal UTF-8 sequence in comment " "%d (stream %d): length marker wrong\n"), i, stream->num); broken = 1; break; } if (bytes > remaining) { warn(_("Warning: Illegal UTF-8 sequence in comment " "%d (stream %d): too few bytes\n"), i, stream->num); broken = 1; break; } switch (bytes) { case 1: /* No more checks needed */ break; case 2: if ((val[j + 1] & 0xC0) != 0x80) broken = 1; if ((val[j] & 0xFE) == 0xC0) broken = 1; break; case 3: if (!((val[j] == 0xE0 && val[j + 1] >= 0xA0 && val[j + 1] <= 0xBF && (val[j + 2] & 0xC0) == 0x80) || (val[j] >= 0xE1 && val[j] <= 0xEC && (val[j + 1] & 0xC0) == 0x80 && (val[j + 2] & 0xC0) == 0x80) || (val[j] == 0xED && val[j + 1] >= 0x80 && val[j + 1] <= 0x9F && (val[j + 2] & 0xC0) == 0x80) || (val[j] >= 0xEE && val[j] <= 0xEF && (val[j + 1] & 0xC0) == 0x80 && (val[j + 2] & 0xC0) == 0x80))) broken = 1; if (val[j] == 0xE0 && (val[j + 1] & 0xE0) == 0x80) broken = 1; break; case 4: if (!((val[j] == 0xF0 && val[j + 1] >= 0x90 && val[j + 1] <= 0xBF && (val[j + 2] & 0xC0) == 0x80 && (val[j + 3] & 0xC0) == 0x80) || (val[j] >= 0xF1 && val[j] <= 0xF3 && (val[j + 1] & 0xC0) == 0x80 && (val[j + 2] & 0xC0) == 0x80 && (val[j + 3] & 0xC0) == 0x80) || (val[j] == 0xF4 && val[j + 1] >= 0x80 && val[j + 1] <= 0x8F && (val[j + 2] & 0xC0) == 0x80 && (val[j + 3] & 0xC0) == 0x80))) broken = 1; if (val[j] == 0xF0 && (val[j + 1] & 0xF0) == 0x80) broken = 1; break; /* 5 and 6 aren't actually allowed at this point */ case 5: broken = 1; break; case 6: broken = 1; break; } if (broken) { warn(_("Warning: Illegal UTF-8 sequence in comment " "%d (stream %d): invalid sequence\n"), i, stream->num); broken = 1; break; } j += bytes; } if (!broken) { if (utf8_decode(sep + 1, &decoded) < 0) { warn(_("Warning: Failure in utf8 decoder. This " "should be impossible\n")); continue; } *sep = 0; debug_printf("\t%s=%s\n", inf->vc.user_comments[i], decoded); /* GCS FIX: Need case insensitive compare */ if (!strcmp(inf->vc.user_comments[i], "artist") || !strcmp(inf->vc.user_comments[i], "ARTIST") || !strcmp(inf->vc.user_comments[i], "Artist")) { /* GCS FIX: This is a bit funky, maybe I need to get rid of the ogg built-in utf8 decoder */ gstring_from_string(rmi, ti->artist, MAX_TRACK_LEN, decoded, CODESET_LOCALE); } else if (!strcmp(inf->vc.user_comments[i], "title") || !strcmp(inf->vc.user_comments[i], "TITLE") || !strcmp(inf->vc.user_comments[i], "Title")) { /* GCS FIX: This is a bit funky, maybe I need to get rid of the ogg built-in utf8 decoder */ gstring_from_string(rmi, ti->title, MAX_TRACK_LEN, decoded, CODESET_LOCALE); ti->have_track_info = 1; } else if (!strcmp(inf->vc.user_comments[i], "album") || !strcmp(inf->vc.user_comments[i], "ALBUM") || !strcmp(inf->vc.user_comments[i], "Album")) { /* GCS FIX: This is a bit funky, maybe I need to get rid of the ogg built-in utf8 decoder */ gstring_from_string(rmi, ti->album, MAX_TRACK_LEN, decoded, CODESET_LOCALE); } else if (!strcmp(inf->vc.user_comments[i], "tracknumber") || !strcmp(inf->vc.user_comments[i], "TRACKNUMBER") || !strcmp(inf->vc.user_comments[i], "Tracknumber")) { /* GCS FIX: This is a bit funky, maybe I need to get rid of the ogg built-in utf8 decoder */ gstring_from_string(rmi, ti->track_p, MAX_TRACK_LEN, decoded, CODESET_LOCALE); } free(decoded); } } /* Done looping through vorbis comments. If we didn't find a title, give a default title. */ if (!ti->have_track_info) { strncpy(ti->title, "Title Unknown", MAX_TRACK_LEN); ti->have_track_info = 1; } } } } if (!header) { ogg_int64_t gp = ogg_page_granulepos(page); if (gp > 0) { if (gp < inf->lastgranulepos) #ifdef _WIN32 warn(_("Warning: granulepos in stream %d decreases from %I64d to %I64d"), stream->num, inf->lastgranulepos, gp); #else warn(_("Warning: granulepos in stream %d decreases from %lld to %lld"), stream->num, inf->lastgranulepos, gp); #endif inf->lastgranulepos = gp; } else { warn(_ ("Negative granulepos on vorbis stream outside of headers. This file was created by a buggy encoder\n")); } if (inf->firstgranulepos < 0) { /* Not set yet */ } inf->bytes += page->header_len + page->body_len; } return header; }