int w_theora_decode_header(theora_info* ci, theora_comment* cc, ogg_packet* op) { int ret = theora_decode_header(ci, cc, op); if (ret == OC_BADHEADER) { TRACE_ERROR("theora_decode_header failure (OC_BADHEADER:%d)", ret); exit(EXIT_FAILURE); } else if (ret == OC_VERSION) { TRACE_ERROR("theora_decode_header failure (OC_VERSION:%d)", ret); exit(EXIT_FAILURE); } else if (ret == OC_NEWPACKET) { TRACE_ERROR("theora_decode_header failure (OC_NEWPACKET:%d)", ret); exit(EXIT_FAILURE); } else if (ret == OC_NOTFORMAT) { TRACE_WARNING("theora_decode_header (OC_NOTFORMAT:%d)", ret); } return ret; }
static GstFlowReturn theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet) { GstFlowReturn res; GST_DEBUG_OBJECT (dec, "parsing header packet"); if (theora_decode_header (&dec->info, &dec->comment, packet)) goto header_read_error; switch (packet->packet[0]) { case 0x81: res = theora_handle_comment_packet (dec, packet); break; case 0x82: res = theora_handle_type_packet (dec, packet); break; default: /* ignore */ g_warning ("unknown theora header packet found"); case 0x80: /* nothing special, this is the identification header */ res = GST_FLOW_OK; break; } return res; /* ERRORS */ header_read_error: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read header packet")); return GST_FLOW_ERROR; } }
bool Oggeyman::is_theora_stream(ogg_stream_state *probe_state_ptr, ogg_packet * probe_packet_ptr) { bool found_theora_packet = false; while (next_packet(probe_state_ptr, probe_packet_ptr)) { // theo info and theo comment are private variables of the Oggeyman object if (theora_decode_header(&theo_info, &theo_comment, probe_packet_ptr)>= 0) { found_theora_packet = true; // I need to go through all of them (or so the src code makes me believe) // therefore, no break here } } return found_theora_packet; }
/* * We're forced to use a dirty hack here, due to Theora's idiotic API * Theora needs three separate pieces of data, called headers to initialize * its internal decoder structure. After all three pieces have been received, * we can call theora_decode_init. * We use a counter and a flag to make sure we have decoded our three headers and then * we call theora_decode_init so we can initialize a theora_state structure. * We use the ts structure to convert a granule position into an actual timestamp. * There are many ways in which this can fail, but we rely on having all three headers * at the beginning of the ogg video bitstream. * * To whoever came up with this convoluted scheme: please consider a change of careers. */ int read_theora_cb(OGGZ *oggz, ogg_packet *op, long serialno, void *data) { struct op_node *node; struct theora_headers *th; long timestamp = 0; //mylog("Got theora packet, serialno=%d, size=%d, packetno=%lld, granulepos=%lld\n", // serialno, op->bytes, op->packetno, op->granulepos); th = (struct theora_headers *)video_stream->data; if ( theora_packet_isheader(op) ) { theora_decode_header(&(th->ti), &(th->tc), op); th->header_count++; } if ( th->header_count >= 3 && !th->have_headers ) { theora_decode_init(&(th->ts), &(th->ti)); th->have_headers = 1; } if ( th->have_headers ) { double d; d = theora_granule_time(&(th->ts), op->granulepos); timestamp = (long)(d * 1000); } if ( timestamp < 0 ) { timestamp = video_stream->page_ts + video_stream->page_count * THEORA_FRAME_DURATION; video_stream->page_count++; } else { video_stream->page_ts = timestamp; video_stream->page_count = 0; } if ( !theora_packet_isheader(op) ) { node = create_node(op, serialno, timestamp); append_node(video_stream, node); } return 0; }
static bool_t dec_init_theora(DecState *s, ogg_packet *op){ theora_comment tcom; static const int ident_packet_size=42; theora_comment_init(&tcom); tcom.vendor="dummy"; op->b_o_s=1; if (theora_decode_header(&s->tinfo,&tcom,op)==0){ op->packet+=ident_packet_size; op->bytes-=ident_packet_size; /*recall once to decode tables*/ if (theora_decode_header(&s->tinfo,&tcom,op)==0){ if (theora_decode_init(&s->tstate,&s->tinfo)==0){ ms_debug("theora decoder ready, pixfmt=%i", s->tinfo.pixelformat); return TRUE; } }else{ ms_warning("error decoding theora tables"); } }else{ ms_warning("error decoding theora header"); } return FALSE; }
static GF_Err THEO_AttachStream(GF_BaseDecoder *ifcg, u16 ES_ID, char *decSpecInfo, u32 decSpecInfoSize, u16 DependsOnES_ID, u32 objectTypeIndication, Bool UpStream) { ogg_packet oggpacket; GF_BitStream *bs; THEORACTX(); if (ctx->ES_ID) return GF_BAD_PARAM; if (!decSpecInfo) return GF_NON_COMPLIANT_BITSTREAM; if (objectTypeIndication != GPAC_OGG_MEDIA_OTI) return GF_NON_COMPLIANT_BITSTREAM; if ( (decSpecInfoSize<9) || strncmp(&decSpecInfo[3], "theora", 6)) return GF_NON_COMPLIANT_BITSTREAM; oggpacket.granulepos = -1; oggpacket.b_o_s = 1; oggpacket.e_o_s = 0; oggpacket.packetno = 0; ctx->ES_ID = ES_ID; theora_info_init(&ctx->ti); theora_comment_init(&ctx->tc); bs = gf_bs_new(decSpecInfo, decSpecInfoSize, GF_BITSTREAM_READ); while (gf_bs_available(bs)) { oggpacket.bytes = gf_bs_read_u16(bs); oggpacket.packet = malloc(sizeof(char) * oggpacket.bytes); gf_bs_read_data(bs, oggpacket.packet, oggpacket.bytes); if (theora_decode_header(&ctx->ti, &ctx->tc, &oggpacket) < 0 ) { free(oggpacket.packet); gf_bs_del(bs); return GF_NON_COMPLIANT_BITSTREAM; } free(oggpacket.packet); } theora_decode_init(&ctx->td, &ctx->ti); gf_bs_del(bs); return GF_OK; }
int main(int argc,char *argv[]){ int i,j; ogg_packet op; FILE *infile = stdin; #ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn 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 ); #endif /* open the input file if any */ if(argc==2){ infile=fopen(argv[1],"rb"); if(infile==NULL){ fprintf(stderr,"Unable to open '%s' for playback.\n", argv[1]); exit(1); } } if(argc>2){ usage(); exit(1); } /* start up Ogg stream synchronization layer */ ogg_sync_init(&oy); /* init supporting Vorbis structures needed in header parsing */ vorbis_info_init(&vi); vorbis_comment_init(&vc); /* init supporting Theora structures needed in header parsing */ theora_comment_init(&tc); theora_info_init(&ti); /* Ogg file open; parse the headers */ /* Only interested in Vorbis/Theora streams */ while(!stateflag){ int ret=buffer_data(infile,&oy); if(ret==0)break; while(ogg_sync_pageout(&oy,&og)>0){ 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); ogg_stream_packetout(&test,&op); /* identify the codec: try theora */ if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){ /* it is theora */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){ /* it is vorbis */ memcpy(&vo,&test,sizeof(test)); vorbis_p=1; }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_p<3) || (vorbis_p && vorbis_p<3)){ int ret; /* look for further theora headers */ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){ if(ret<0){ fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } if(theora_decode_header(&ti,&tc,&op)){ printf("Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } theora_p++; if(theora_p==3)break; } /* look for more vorbis header packets */ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){ if(ret<0){ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } if(vorbis_synthesis_headerin(&vi,&vc,&op)){ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } vorbis_p++; if(vorbis_p==3)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){ theora_decode_init(&td,&ti); printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n", (unsigned int)to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator); if(ti.width!=ti.frame_width || ti.height!=ti.frame_height) printf(" Frame content is %dx%d with offset (%d,%d).\n", ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y); report_colorspace(&ti); dump_comments(&tc); }else{ /* tear down the partial theora setup */ theora_info_clear(&ti); theora_comment_clear(&tc); } if(vorbis_p){ vorbis_synthesis_init(&vd,&vi); vorbis_block_init(&vd,&vb); fprintf(stderr,"Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n", (unsigned int)vo.serialno,vi.channels,(int)vi.rate); }else{ /* tear down the partial vorbis setup */ vorbis_info_clear(&vi); vorbis_comment_clear(&vc); } /* open audio */ if(vorbis_p)open_audio(); /* open video */ if(theora_p)open_video(); /* install signal handler as SDL clobbered the default */ signal (SIGINT, sigint_handler); /* on to the main decode loop. We assume in this example that audio and video start roughly together, and don't begin playback until we have a start frame for both. This is not necessarily a valid assumption in Ogg A/V streams! It will always be true of the example_encoder (and most streams) though. */ stateflag=0; /* playback has not begun */ while(!got_sigint){ /* we want a video and audio frame ready to go at all times. If we have to buffer incoming, buffer the compressed data (ie, let ogg do the buffering) */ while(vorbis_p && !audiobuf_ready){ int ret; float **pcm; /* if there's pending, decoded audio, grab it */ if((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0){ int count=audiobuf_fill/2; int maxsamples=(audiofd_fragsize-audiobuf_fill)/2/vi.channels; for(i=0;i<ret && i<maxsamples;i++) for(j=0;j<vi.channels;j++){ int val=rint(pcm[j][i]*32767.f); if(val>32767)val=32767; if(val<-32768)val=-32768; audiobuf[count++]=val; } vorbis_synthesis_read(&vd,i); audiobuf_fill+=i*vi.channels*2; if(audiobuf_fill==audiofd_fragsize)audiobuf_ready=1; if(vd.granulepos>=0) audiobuf_granulepos=vd.granulepos-ret+i; else audiobuf_granulepos+=i; }else{ /* no pending audio; is there a pending packet to decode? */ if(ogg_stream_packetout(&vo,&op)>0){ if(vorbis_synthesis(&vb,&op)==0) /* test for success! */ vorbis_synthesis_blockin(&vd,&vb); }else /* we need more data; break out to suck in another page */ break; } } while(theora_p && !videobuf_ready){ /* theora is one in, one out... */ if(ogg_stream_packetout(&to,&op)>0){ theora_decode_packetin(&td,&op); videobuf_granulepos=td.granulepos; videobuf_time=theora_granule_time(&td,videobuf_granulepos); /* is it already too old to be useful? This is only actually useful cosmetically after a SIGSTOP. Note that we have to decode the frame even if we don't show it (for now) due to keyframing. Soon enough libtheora will be able to deal with non-keyframe seeks. */ if(videobuf_time>=get_time()) videobuf_ready=1; }else break; } if(!videobuf_ready && !audiobuf_ready && feof(infile))break; if(!videobuf_ready || !audiobuf_ready){ /* no data yet for somebody. Grab another page */ int bytes=buffer_data(infile,&oy); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* If playback has begun, top audio buffer off immediately. */ if(stateflag) audio_write_nonblocking(); /* are we at or past time for this video frame? */ if(stateflag && videobuf_ready && videobuf_time<=get_time()){ video_write(); videobuf_ready=0; } if(stateflag && (audiobuf_ready || !vorbis_p) && (videobuf_ready || !theora_p) && !got_sigint){ /* we have an audio frame ready (which means the audio buffer is full), it's not time to play video, so wait until one of the audio buffer is ready or it's near time to play video */ /* set up select wait on the audiobuffer and a timeout for video */ struct timeval timeout; fd_set writefs; fd_set empty; int n=0; FD_ZERO(&writefs); FD_ZERO(&empty); if(audiofd>=0){ FD_SET(audiofd,&writefs); n=audiofd+1; } if(theora_p){ long milliseconds=(videobuf_time-get_time())*1000-5; if(milliseconds>500)milliseconds=500; if(milliseconds>0){ timeout.tv_sec=milliseconds/1000; timeout.tv_usec=(milliseconds%1000)*1000; n=select(n,&empty,&writefs,&empty,&timeout); if(n)audio_calibrate_timer(0); } }else{ select(n,&empty,&writefs,&empty,NULL); } } /* if our buffers either don't exist or are ready to go, we can begin playback */ if((!theora_p || videobuf_ready) && (!vorbis_p || audiobuf_ready))stateflag=1; /* same if we've run out of input */ if(feof(infile))stateflag=1; } /* tear it all down */ audio_close(); SDL_Quit(); if(vorbis_p){ ogg_stream_clear(&vo); vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); } if(theora_p){ ogg_stream_clear(&to); theora_clear(&td); theora_comment_clear(&tc); theora_info_clear(&ti); } ogg_sync_clear(&oy); if(infile && infile!=stdin)fclose(infile); fprintf(stderr, "\r " "\nDone.\n"); return(0); }
void DGVideo::load() { int stateFlag = 0; if (!_hasResource) { log->error(DGModVideo, "%s", DGMsg280005); return; } //log->trace(DGModVideo, "%s %s", DGMsg080001, _resource); _handle = fopen(_resource, "rb"); if (_handle == NULL) { log->error(DGModVideo, "%s: %s", DGMsg280002, _resource); return; } ogg_sync_init(&_theoraInfo->oy); theora_comment_init(&_theoraInfo->tc); theora_info_init(&_theoraInfo->ti); while (!stateFlag) { size_t ret = _bufferData(&_theoraInfo->oy); if (ret == 0) break; while (ogg_sync_pageout(&_theoraInfo->oy, &_theoraInfo->og) > 0) { ogg_stream_state test; if (!ogg_page_bos(&_theoraInfo->og)) { _queuePage(_theoraInfo, &_theoraInfo->og); stateFlag = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&_theoraInfo->og)); ogg_stream_pagein(&test, &_theoraInfo->og); ogg_stream_packetout(&test, &_theoraInfo->op); if (!_theoraInfo->theora_p && theora_decode_header(&_theoraInfo->ti, &_theoraInfo->tc, &_theoraInfo->op) >= 0) { memcpy(&_theoraInfo->to, &test, sizeof(test)); _theoraInfo->theora_p = 1; } else { ogg_stream_clear(&test); } } } while (_theoraInfo->theora_p && _theoraInfo->theora_p < 3) { int ret; while (_theoraInfo->theora_p && (_theoraInfo->theora_p < 3) && (ret = ogg_stream_packetout(&_theoraInfo->to, &_theoraInfo->op))) { if (ret < 0) { log->error(DGModVideo, "%s", DGMsg280003); return; } if (theora_decode_header(&_theoraInfo->ti, &_theoraInfo->tc, &_theoraInfo->op)) { log->error(DGModVideo, "%s", DGMsg280003); return; } _theoraInfo->theora_p++; if (_theoraInfo->theora_p == 3) break; } if (ogg_sync_pageout(& _theoraInfo->oy, & _theoraInfo->og) > 0) _queuePage(_theoraInfo, &_theoraInfo->og); else { size_t ret = _bufferData(&_theoraInfo->oy); if (ret == 0) { log->error(DGModVideo, "%s", DGMsg280004); return; } } } if (_theoraInfo->theora_p) { theora_decode_init(&_theoraInfo->td, &_theoraInfo->ti); } else { theora_info_clear(&_theoraInfo->ti); theora_comment_clear(&_theoraInfo->tc); } _currentFrame.width = _theoraInfo->ti.width; _currentFrame.height = _theoraInfo->ti.height; _currentFrame.depth = 24; // NOTE: We only support flat RGB for now _currentFrame.data = (unsigned char*)malloc((_theoraInfo->ti.width * _theoraInfo->ti.height) * 3); while (ogg_sync_pageout(&_theoraInfo->oy, &_theoraInfo->og) > 0) { _queuePage(_theoraInfo, &_theoraInfo->og); } _frameDuration = (float)(1/((double)_theoraInfo->ti.fps_numerator / _theoraInfo->ti.fps_denominator)); _isLoaded = true; }
/* Opens the Ogg stream, searching for and initializing Theora and Vorbis media */ int alogg_open(APEG_LAYER *layer) { ALOGG_INFO *info; int vok = 0, aok = 0; int flag, cs, size; info = calloc(1, sizeof(ALOGG_INFO)); if(!info) return APEG_ERROR; LOCK_DATA(info, sizeof(ALOGG_INFO)); ogg_sync_init(&info->osync); theora_comment_init(&info->tcomment); theora_info_init(&info->tinfo); vorbis_info_init(&info->vinfo); vorbis_comment_init(&info->vcomment); flag = FALSE; while(!flag) { int ret = buffer_data(layer, info); if(ret == 0) break; while(ogg_sync_pageout(&info->osync, &info->opage) > 0) { ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if(!ogg_page_bos(&info->opage)) { if(vok > 0) ogg_stream_pagein(&info->ostream[0], &info->opage); if(aok > 0) ogg_stream_pagein(&info->ostream[1], &info->opage); flag = TRUE; break; } ogg_stream_init(&test, ogg_page_serialno(&info->opage)); ogg_stream_pagein(&test, &info->opage); ogg_stream_packetout(&test, &info->opkt); /* identify the codec: try theora */ if(!vok && theora_decode_header(&info->tinfo, &info->tcomment, &info->opkt) >= 0) { /* it is theora */ if(!_apeg_ignore_video) { memcpy(&info->ostream[0], &test, sizeof(test)); vok = 1; } else ogg_stream_clear(&test); } else if(!aok && vorbis_synthesis_headerin(&info->vinfo, &info->vcomment, &info->opkt) >= 0) { /* it is vorbis */ if(!_apeg_ignore_audio) { memcpy(&info->ostream[1], &test, sizeof(test)); aok = 1; } else ogg_stream_clear(&test); } /* whatever it is, we don't care about it */ else ogg_stream_clear(&test); } /* fall through to non-bos page parsing */ } /* look for further theora headers */ while((vok > 0 && vok < 3) || (aok > 0 && aok < 3)) { int ret; // Get the last two of three Theora headers while(vok > 0 && vok < 3 && (ret = ogg_stream_packetout(&info->ostream[0], &info->opkt))) { if(ret < 0) goto error; if(theora_decode_header(&info->tinfo, &info->tcomment, &info->opkt)) goto error; ++vok; } // Get the last two of three Vorbis headers while(aok > 0 && aok < 3 && (ret = ogg_stream_packetout(&info->ostream[1], &info->opkt))) { if(ret < 0) goto error; if(vorbis_synthesis_headerin(&info->vinfo, &info->vcomment, &info->opkt)) goto error; ++aok; } if(ogg_sync_pageout(&info->osync, &info->opage) <= 0) { /* need more data */ if(buffer_data(layer, info) == 0) break; } else { if(vok > 0) ogg_stream_pagein(&info->ostream[0], &info->opage); if(aok > 0) ogg_stream_pagein(&info->ostream[1], &info->opage); } } // Neither Vorbis or Theora fully initialized. Error. if(vok != 3 && aok != 3) goto error; layer->ogg_info = info; if(aok == 3) { vorbis_synthesis_init(&info->vdsp, &info->vinfo); vorbis_block_init(&info->vdsp, &info->vblock); if(info->vinfo.channels == 1) layer->stream.audio.down_channel = FALSE; layer->stream.audio.channels = info->vinfo.channels; layer->stream.audio.freq = info->vinfo.rate >> layer->stream.audio.down_sample; if(_apeg_audio_reset_parameters(layer) != APEG_OK) { vorbis_block_clear(&info->vblock); vorbis_dsp_clear(&info->vdsp); goto error; } // layer->audio.inited = TRUE; layer->stream.flags |= APEG_VORBIS_AUDIO; }
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++; } }
bool parseHeaders(CStretchyBuffer& buffer) // Parse the headers // Only interested in Vorbis/Theora streams { // extracted from player_sample.c test file for theora alpha ogg_packet op; bool stateflag=false; while (!stateflag) { int ret=buffer_data(&oy, buffer); if (ret==0) break; while (ogg_sync_pageout(&oy,&og)>0) { 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=true; break; } ogg_stream_init(&test,ogg_page_serialno(&og)); ogg_stream_pagein(&test,&og); ogg_stream_packetout(&test,&op); // identify the codec: try theora if (!theora_p && theora_decode_header(&ti,&tc,&op)>=0) { // it is theora memcpy(&to,&test,sizeof(test)); theora_p=1; } else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0) { // it is vorbis memcpy(&vo,&test,sizeof(test)); vorbis_p=1; } else { // whatever it is, we don't care about it ogg_stream_clear(&test); } } } // we've now identified all the bitstreams. parse the secondary header packets. while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)) { int ret; // look for further theora headers while (theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))) { if (ret<0) { ASSERT(!"Error parsing Theora stream headers; corrupt stream?"); return false; } if (theora_decode_header(&ti,&tc,&op)) { ASSERT(!"Error parsing Theora stream headers; corrupt stream?"); return false; } ++theora_p; if (theora_p==3) break; } // look for more vorbis header packets while (vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))) { if (ret<0) { ASSERT(!"Error parsing Vorbis stream headers; corrupt stream?"); return false; } if (vorbis_synthesis_headerin(&vi,&vc,&op)) { ASSERT(!"Error parsing Vorbis stream headers; corrupt stream?"); return false; } ++vorbis_p; if (vorbis_p==3) 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(&oy, buffer); if(ret==0){ ASSERT(!"End of file while searching for codec headers."); return false; } } } return true; }
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; }
bool Oggeyman::get_next_frame(unsigned char *BGRAbuffer) { if (stream_done) return false; timer_update(); #ifdef ANDR //printf ("in get_next_frame: last_packet_time %12.2lf total_play_time %12.2lf \n", last_packet_time, total_play_time); // "foo" will be used as a tag in LogCat __android_log_print(ANDROID_LOG_INFO, "foo", "in get_next_frame: last_packet_time %12.2lf total_play_time %12.2lf \n", last_packet_time, total_play_time ); # endif if (last_packet_time > total_play_time) return false; // TODO: what's with this pp_level reduction business? // do we have a leftover packet from the last page? (and is it really theora?) #ifdef ANDR struct timespec time1,time2; clock_gettime(CLOCK_MONOTONIC, &time1); # endif bool packet_found = (next_packet(&ogg_theora_stream_state, &ogg_theora_packet) && theora_decode_header(&theo_info, &theo_comment, &ogg_theora_packet) >= 0); #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "in get_next_frame: time to get next packet and decode header %6.2lf ms\n", (time2.tv_sec - time1.tv_sec)*1.e3 + (time2.tv_nsec - time1.tv_nsec)/1000.0f ); clock_gettime(CLOCK_MONOTONIC, &time1); # endif while (!packet_found && !stream_done) { bool is_theora_page = false; while (!stream_done && !is_theora_page) { stream_done = !next_page(); if (stream_done) continue; // submit the completed page to the streaming layer with ogg_stream_pagein. // check out the return value - it will be < 0 it the page does not correspond // to the stream whose state we are providing as the first argument is_theora_page = (ogg_stream_pagein(&ogg_theora_stream_state, ¤t_ogg_page) >= 0); } if (stream_done) continue; // Packets can span multiple pages, but next_packet, or rather ogg_stream_packetout, // should take care of that - not return packets until they are complete. packet_found = next_packet(&ogg_theora_stream_state, &ogg_theora_packet); } #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "in get_next_frame: looping for next packet %6.2lf ms\n", (time2.tv_sec - time1.tv_sec)*1.e3 + (time2.tv_nsec - time1.tv_nsec)/1000.0f ); # endif if (packet_found) { #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time1); # endif bool processing_ok = process_packet(BGRAbuffer); #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "[Aug 2015] in get_next_frame: time to process the packet %6.2lf ms\n", (time2.tv_sec - time1.tv_sec)*1.e3 + (time2.tv_nsec - time1.tv_nsec)/1.e6 ); # endif return processing_ok; } return false; }
//TODO: vorbis/theora-header&init in sub-functions //TODO: "clean" error-returns ... int Cin_OGM_Init(const char* filename) { int status; ogg_page og; ogg_packet op; int i; if(g_ogm.ogmFile) { Com_Printf("WARNING: it seams there was already a ogm running, it will be killed to start %s\n", filename); Cin_OGM_Shutdown(); } memset(&g_ogm,0,sizeof(cin_ogm_t)); FS_FOpenFileRead(filename, &g_ogm.ogmFile, qtrue); if(!g_ogm.ogmFile) { Com_Printf("Can't open ogm-file for reading (%s)\n", filename); return -1; } ogg_sync_init(&g_ogm.oy); /* Now we can read pages */ //FIXME? can serialno be 0 in ogg? (better way to check inited?) //TODO: support for more than one audio stream? / detect files with one stream(or without correct ones) while(!g_ogm.os_audio.serialno || !g_ogm.os_video.serialno) { if(ogg_sync_pageout(&g_ogm.oy,&og)==1) { if(strstr((char*)(og.body+1),"vorbis")) { //FIXME? better way to find audio stream if(g_ogm.os_audio.serialno) { Com_Printf("more than one audio stream, in ogm-file(%s) ... we will stay at the first one\n",filename); } else { ogg_stream_init(&g_ogm.os_audio, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_audio,&og); } } #ifdef USE_CIN_THEORA if(strstr((char*)(og.body+1),"theora")) { if(g_ogm.os_video.serialno) { Com_Printf("more than one video stream, in ogm-file(%s) ... we will stay at the first one\n",filename); } else{ g_ogm.videoStreamIsTheora = qtrue; ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_video,&og); } } #endif #ifdef USE_CIN_XVID if(strstr((char*)(og.body+1),"video")) { //FIXME? better way to find video stream if(g_ogm.os_video.serialno) { Com_Printf("more than one video stream, in ogm-file(%s) ... we will stay at the first one\n",filename); } else{ stream_header_t* sh; g_ogm.videoStreamIsXvid = qtrue; sh = (stream_header_t*)(og.body+1); //TODO: one solution for checking xvid and theora if(!isPowerOf2(sh->sh.stream_header_video.width)) { Com_Printf("VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); return -5; } if(!isPowerOf2(sh->sh.stream_header_video.height)) { Com_Printf("VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); return -6; } g_ogm.Vtime_unit = sh->time_unit; ogg_stream_init(&g_ogm.os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm.os_video,&og); } } #endif } else if(loadBlockToSync()) break; } if(g_ogm.videoStreamIsXvid && g_ogm.videoStreamIsTheora) { Com_Printf("Found \"video\"- and \"theora\"-stream ,ogm-file (%s)\n", filename); return -2; } if(!g_ogm.os_audio.serialno) { Com_Printf("Haven't found a audio(vorbis) stream in ogm-file (%s)\n", filename); return -2; } if(!g_ogm.os_video.serialno) { Com_Printf("Haven't found a video stream in ogm-file (%s)\n", filename); return -3; } //load vorbis header vorbis_info_init(&g_ogm.vi); vorbis_comment_init(&g_ogm.vc); i=0; while(i<3) { status = ogg_stream_packetout(&g_ogm.os_audio,&op); if(status<0) { Com_Printf("Corrupt ogg packet while loading vorbis-headers, ogm-file(%s)\n", filename); return -8; } if(status>0) { status = vorbis_synthesis_headerin(&g_ogm.vi,&g_ogm.vc,&op); if(i==0 && status<0) { Com_Printf("This Ogg bitstream does not contain Vorbis audio data, ogm-file(%s)\n", filename); return -9; } ++i; } else if(loadPagesToStreams()) { if(loadBlockToSync()) { Com_Printf("Couldn't find all vorbis headers before end of ogm-file (%s)\n", filename); return -10; } } } vorbis_synthesis_init(&g_ogm.vd,&g_ogm.vi); #ifdef USE_CIN_XVID status = init_xvid(); if (status) { Com_Printf("[Xvid]Decore INIT problem, return value %d(ogm-file: %s)\n", status, filename); return -4; } #endif #ifdef USE_CIN_THEORA if(g_ogm.videoStreamIsTheora) { ROQ_GenYUVTables(); theora_info_init(&g_ogm.th_info); theora_comment_init(&g_ogm.th_comment); i=0; while(i<3) { status = ogg_stream_packetout(&g_ogm.os_video,&op); if(status<0) { Com_Printf("Corrupt ogg packet while loading theora-headers, ogm-file(%s)\n", filename); return -8; } if(status>0) { status = theora_decode_header(&g_ogm.th_info, &g_ogm.th_comment, &op); if(i==0 && status!=0) { Com_Printf("This Ogg bitstream does not contain theora data, ogm-file(%s)\n", filename); return -9; } ++i; } else if(loadPagesToStreams()) { if(loadBlockToSync()) { Com_Printf("Couldn't find all theora headers before end of ogm-file (%s)\n", filename); return -10; } } } theora_decode_init(&g_ogm.th_state, &g_ogm.th_info); if(!isPowerOf2(g_ogm.th_info.width)) { Com_Printf("VideoWidth of the ogm-file isn't a power of 2 value (%s)\n", filename); return -5; } if(!isPowerOf2(g_ogm.th_info.height)) { Com_Printf("VideoHeight of the ogm-file isn't a power of 2 value (%s)\n", filename); return -6; } g_ogm.Vtime_unit = ((ogg_int64_t)g_ogm.th_info.fps_denominator *1000*10000 / g_ogm.th_info.fps_numerator); } #endif Com_DPrintf("OGM-Init done (%s)\n",filename); return 0; }
qboolean OGV_StartRead(cinematic_t *cin) { int status; ogg_page og; ogg_packet op; int i; cin->data = Com_Allocate(sizeof(cin_ogv_t)); Com_Memset(cin->data, 0, sizeof(cin_ogv_t)); ogg_sync_init(&g_ogm->oy); /* Now we can read pages */ //FIXME? can serialno be 0 in ogg? (better way to check inited?) //TODO: support for more than one audio stream? / detect files with one stream(or without correct ones) while (!g_ogm->os_audio.serialno || !g_ogm->os_video.serialno) { if (ogg_sync_pageout(&g_ogm->oy, &og) == 1) { if (strstr((char *)(og.body + 1), "vorbis")) { //FIXME? better way to find audio stream if (g_ogm->os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one audio stream, in ogm-file(%s) ... we will stay at the first one\n", cin->name); } else { ogg_stream_init(&g_ogm->os_audio, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm->os_audio, &og); } } if (strstr((char *)(og.body + 1), "theora")) { if (g_ogm->os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: more than one video stream, in ogm-file(%s) ... we will stay at the first one\n", cin->name); } else { ogg_stream_init(&g_ogm->os_video, ogg_page_serialno(&og)); ogg_stream_pagein(&g_ogm->os_video, &og); } } } else if (OGV_LoadBlockToSync(cin)) { break; } } if (!g_ogm->os_audio.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a audio(vorbis) stream in ogm-file (%s)\n", cin->name); return qfalse; } if (!g_ogm->os_video.serialno) { Com_Printf(S_COLOR_YELLOW "WARNING: Haven't found a video stream in ogm-file (%s)\n", cin->name); return qfalse; } //load vorbis header vorbis_info_init(&g_ogm->vi); vorbis_comment_init(&g_ogm->vc); i = 0; while (i < 3) { status = ogg_stream_packetout(&g_ogm->os_audio, &op); if (status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading vorbis-headers, ogm-file(%s)\n", cin->name); return qfalse; } if (status > 0) { status = vorbis_synthesis_headerin(&g_ogm->vi, &g_ogm->vc, &op); if (i == 0 && status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain Vorbis audio data, ogm-file(%s)\n", cin->name); return qfalse; } ++i; } else if (OGV_LoadPagesToStreams(cin)) { if (OGV_LoadBlockToSync(cin)) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all vorbis headers before end of ogm-file (%s)\n", cin->name); return qfalse; } } } vorbis_synthesis_init(&g_ogm->vd, &g_ogm->vi); // Do init { theora_info_init(&g_ogm->th_info); theora_comment_init(&g_ogm->th_comment); i = 0; while (i < 3) { status = ogg_stream_packetout(&g_ogm->os_video, &op); if (status < 0) { Com_Printf(S_COLOR_YELLOW "WARNING: Corrupt ogg packet while loading theora-headers, ogm-file(%s)\n", cin->name); return qfalse; } else if (status > 0) { status = theora_decode_header(&g_ogm->th_info, &g_ogm->th_comment, &op); if (i == 0 && status != 0) { Com_Printf(S_COLOR_YELLOW "WARNING: This Ogg bitstream does not contain theora data, ogm-file(%s)\n", cin->name); return qfalse; } ++i; } else if (OGV_LoadPagesToStreams(cin)) { if (OGV_LoadBlockToSync(cin)) { Com_Printf(S_COLOR_YELLOW "WARNING: Couldn't find all theora headers before end of ogm-file (%s)\n", cin->name); return qfalse; } } } theora_decode_init(&g_ogm->th_state, &g_ogm->th_info); g_ogm->Vtime_unit = ((ogg_int64_t) g_ogm->th_info.fps_denominator * 1000 * 10000 / g_ogm->th_info.fps_numerator); } Com_DPrintf("Theora init done (%s)\n", cin->name); return qtrue; }
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[]){ ogg_packet op; int long_option_index; int c; 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': outfile=fopen(optarg,"wb"); if(outfile==NULL){ fprintf(stderr,"Unable to open output file '%s'\n", optarg); exit(1); } 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 */ theora_comment_init(&tc); theora_info_init(&ti); /* Ogg file open; parse the headers */ /* Vorbis and Theora both depend 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){ 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); ogg_stream_packetout(&test,&op); /* identify the codec: try theora */ if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){ /* it is theora -- save this stream state */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } /* fall through to non-initial page parsing */ } /* we're expecting more header packets. */ while(theora_p && theora_p<3){ int ret; /* look for further theora headers */ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){ if(ret<0){ fprintf(stderr,"Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } if(theora_decode_header(&ti,&tc,&op)){ printf("Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } theora_p++; if(theora_p==3)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 stream state */ }else{ int ret=buffer_data(infile,&oy); /* need more data */ if(ret==0){ fprintf(stderr,"End of file while searching for codec headers.\n"); exit(1); } } } /* Now we have all the required headers. initialize the decoder. */ if(theora_p){ theora_decode_init(&td,&ti); fprintf(stderr,"Ogg logical stream %x is Theora %dx%d %.02f fps video\nEncoded frame content is %dx%d with %dx%d offset\n", to.serialno,ti.width,ti.height, (double)ti.fps_numerator/ti.fps_denominator, ti.frame_width, ti.frame_height, ti.offset_x, ti.offset_y); }else{ /* tear down the partial theora setup */ theora_info_clear(&ti); theora_comment_clear(&tc); } /* open video */ if(theora_p)open_video(); /* 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 were 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); } while(!got_sigint){ while(theora_p && !videobuf_ready){ /* theora is one in, one out... */ if(ogg_stream_packetout(&to,&op)>0){ theora_decode_packetin(&td,&op); videobuf_granulepos=td.granulepos; videobuf_time=theora_granule_time(&td,videobuf_granulepos); videobuf_ready=1; }else break; } if(!videobuf_ready && feof(infile))break; if(!videobuf_ready){ /* no data yet for somebody. Grab another page */ int ret=buffer_data(infile,&oy); while(ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* dumpvideo frame, and get new one */ else video_write(); videobuf_ready=0; } /* end of decoder loop -- close everything */ if(theora_p){ ogg_stream_clear(&to); theora_clear(&td); theora_comment_clear(&tc); theora_info_clear(&ti); } ogg_sync_clear(&oy); if(infile && infile!=stdin)fclose(infile); fprintf(stderr, "\r " "\nDone.\n"); return(0); }
void JVideoServer::Init() {return; memset( &m_StreamState, 0, sizeof( m_StreamState ) ); memset( &m_SyncState, 0, sizeof( m_SyncState ) ); memset( &m_Page, 0, sizeof( m_Page ) ); memset( &m_Packet, 0, sizeof( m_Packet ) ); memset( &m_Comment, 0, sizeof( m_Comment ) ); memset( &m_Info, 0, sizeof( m_Info ) ); memset( &m_State, 0, sizeof( m_State ) ); memset( &m_YUVBuffer, 0, sizeof( m_YUVBuffer ) ); ogg_stream_clear( &m_StreamState ); ogg_sync_init( &m_SyncState ); theora_comment_init( &m_Comment ); theora_info_init( &m_Info ); // теперь ищем начало логического потока theora bool bStartHeader = true; int nHeaderPackets = 0; // число обработанных пакетов заголовков theora do { if (LoadChunk( m_File, &m_SyncState ) ==0) { // кончился файл, на данном этапе это ошибка assert( "!eof searched, terminate..."); } // ogg_sync_pageout - формирует страницу while (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // 1-больше данных не требуется // 0-требуется больше данных для создания страницы { // что страница сформирована успешно // это страница заголовков? если нет, кончаем искать заголовки if (ogg_page_bos( &m_Page ) == false) { // нет, это не страница заголовков // значит, страницы заголовков всех логических потоков кончились // и начались данные этих потоков // таким образом надо переходить к чтению страниц данных // закидываем эту страничку в логический видеопоток PushPage( &m_Page ); // PushPage - закидывает страничку // в логический поток theora, если // совпадает идентификатор логического потока // в противном случае страница игнорируется // выходим из циклов bStartHeader = false; break; } else { // да, это страница заголовков // тестовый логический поток ogg_stream_state m_StreamStateTest; memset(&m_StreamStateTest, 0x00, sizeof(ogg_stream_state)); // инициализируем тестовый поток на тот же поток с таким же // идентификатором потока, как и у текущей странички if(0!= ogg_stream_init(&m_StreamStateTest,ogg_page_serialno(&m_Page)) ) assert( "!error during ogg_stream_init"); // добавляем страницу в тестовый поток if(0!= ogg_stream_pagein(&m_StreamStateTest,&m_Page) ) assert( "!error during ogg_stream_pagein"); // декодируем данные из этого тестового потока в пакет if( ogg_stream_packetout(&m_StreamStateTest,&m_Packet) ==-1) assert( "!error during ogg_stream_packetout"); // nHeaderPackets - число прочитанных // заголовочных ПАКЕТОВ theora (не страниц) // по спецификации theora таких пакетов должно быть три if(nHeaderPackets==0) { int dhr = theora_decode_header (&m_Info, &m_Comment, &m_Packet); // декодируем заголовок theora if(dhr<0) { // это не заголовок theora // очищаем структуру тестового потока ogg_stream_clear(&m_StreamStateTest); //и продолжаем цикл в поисках заголовков theora } else { // это заголовок theora! // вот таким образом "инициализируем" логический поток theora: memcpy(&m_StreamState, &m_StreamStateTest, sizeof(m_StreamStateTest)); // теперь из этого потока будут всегда сыпаться пакеты theora nHeaderPackets++; // после того, как мы нашли заголовочную страницу логического потока theora, // нам необходимо прочитать все остальные заголовочные страницы // других потоков и отбросить их (если таковые, конечно, имеются) } } } } } while (bStartHeader); // сейчас надо получить еще два пакета заголовков theora (см. её документацию) // и можно переходить к потоковому воспроизведению while(nHeaderPackets<3) { int result=ogg_stream_packetout(&m_StreamState,&m_Packet); // если функция возвращает нуль, значит не хватает данных для декодирования // почему то этого НЕТ в спецификации libogg, или я плохо искал if (result < 0) { // ошибка декодирования, поврежденный поток assert( "!error during ogg_stream_packetout"); } if (result > 0) { // удалось успешно извлечь пакет информации theora int result2 = theora_decode_header( &m_Info, &m_Comment, &m_Packet ); if(result2<0) { // ошибка декодирования, поврежденный поток rlog.err("VIDEO: error during theora_decode_header (corrupt stream)"); } ++nHeaderPackets; } // эту страничку обработали, надо извлечь новую // для этого проверяем буфер чтения, вдруг там осталось что-нить похожее // на страничку. Если не осталось, тогда просто читаем эти данные из файла: if (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // ogg_sync_pageout - функция, берет данные из буфера приема ogg // и записывает их в ogg_page { //мы нашли страничку в буфере и... PushPage( &m_Page ); // ...пихаем эти данные в подходящий поток } else { // ничего мы в буфере не нашли int ret = LoadChunk( m_File, &m_SyncState ); // надо больше данных! читаем их из файла if (ret == 0) { // опять файл кончился! rlog.err("VIDEO: eof searched. terminate..."); } } } // init videostream theora_decode_init( &m_State, &m_Info ); switch(m_Info.colorspace) { case OC_CS_UNSPECIFIED: // nothing to report break; case OC_CS_ITU_REC_470M: rlog.msg("Encoder specified ITU Rec 470M (NTSC) color."); // выводим в лог информацию о цветовом пространстве break; case OC_CS_ITU_REC_470BG: rlog.msg("Encoder specified ITU Rec 470BG (PAL) color."); break; default: rlog.msg("Warning: encoder specified unknown colorspace."); break; } // theora processing... while (ogg_stream_packetout( &m_StreamState, &m_Packet ) <= 0) { // не хватает данных в логическом потоке theora // надо надергать данных из физического потока и затолкать их в логический поток // читаем данные из файла int ret = LoadChunk( m_File, &m_SyncState ); if (ret == 0) { // файл кончился, необходимо выполнить закрывающие действия // и выйти из приложения TheoraClose(); return; } while (ogg_sync_pageout( &m_SyncState, &m_Page ) > 0) // декодируем данные из буфера в страницы (ogg_page) // пока они не кончатся в буфере { // пихаем эти страницы в соотв. логические потоки PushPage( &m_Page ); } } // удачно декодировали. в пакете содержится декодированная ogg-информация // (то бишь закодированная theora-информация) // загружаем пакет в декодер theora if (theora_decode_packetin(&m_State,&m_Packet) == OC_BADPACKET) { // ошибка декодирования rlog.err( "error during theora_decode_packetin..." ); } // все данные получены, готовим кадр // декодируем страничку в YUV-виде в спец. структуру yuv_buffer if (theora_decode_YUVout( &m_State, &m_YUVBuffer ) != 0) { // ошибка декодирования rlog.err( "error during theora_decode_YUVout..."); } // если это первый кадр, то создаем буфер кадра BYTE* frame = new BYTE[m_YUVBuffer.y_height*m_YUVBuffer.y_width*4]; // yuv to rgb for (int cy = 0; cy < m_YUVBuffer.y_height; cy++) { int nYShift = m_YUVBuffer.y_stride*cy; int nUVShift = m_YUVBuffer.uv_stride*(cy >> 1); for (int cx = 0; cx < m_YUVBuffer.y_width; cx++) { int nHX = (cx >> 1); BYTE nY = *(BYTE*)(m_YUVBuffer.y + nYShift + cx ); BYTE nU = *(BYTE*)(m_YUVBuffer.u + nUVShift + nHX ); BYTE nV = *(BYTE*)(m_YUVBuffer.v + nUVShift + nHX ); int index = (cy*m_YUVBuffer.y_width + cx)*4; float r = nY + 1.371f*(nV - 128); float g = nY - 0.698f*(nV - 128) - 0.336f*(nU - 128); float b = nY + 1.732f*(nU - 128); frame[index + 0] = (BYTE)clamp( r, 0.0f, 255.0f ); frame[index + 1] = (BYTE)clamp( g, 0.0f, 255.0f ); frame[index + 2] = (BYTE)clamp( b, 0.0f, 255.0f ); frame[index + 3] = 255; } } } // JVideoServer::Init
void probe_ogg(info_t *ipipe) { ogg_sync_state sync; ogg_page page; ogg_packet pack; char *buf; int nread, np, sno, nvtracks = 0, natracks = 0, i, idx; //int endofstream = 0, k, n; struct demux_t streams[MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS]; int fdin = -1; char vid_codec[5]; ogm_stream_header *sth; fdin = ipipe->fd_in; if (fdin == -1) { tc_log_error(__FILE__, "Could not open file."); goto ogg_out; } ipipe->probe_info->magic=TC_MAGIC_OGG; memset(streams, 0, sizeof(streams)); for (i = 0; i < (MAX_AUDIO_TRACKS + MAX_VIDEO_TRACKS); i++) streams[i].serial = -1; ogg_sync_init(&sync); while (1) { np = ogg_sync_pageseek(&sync, &page); if (np < 0) { tc_log_error(__FILE__, "ogg_sync_pageseek failed"); goto ogg_out; } if (np == 0) { buf = ogg_sync_buffer(&sync, BLOCK_SIZE); if (!buf) { tc_log_error(__FILE__, "ogg_sync_buffer failed"); goto ogg_out; } if ((nread = read(fdin, buf, BLOCK_SIZE)) <= 0) { } ogg_sync_wrote(&sync, nread); continue; } if (!ogg_page_bos(&page)) { break; } else { ogg_stream_state sstate; vorbis_info *inf = tc_malloc (sizeof(vorbis_info)); vorbis_comment *com = tc_malloc (sizeof(vorbis_comment)); if (!inf || !com) { tc_log_error(__FILE__, "Out of Memory at %d", __LINE__); goto ogg_out; } sno = ogg_page_serialno(&page); if (ogg_stream_init(&sstate, sno)) { tc_log_error(__FILE__, "ogg_stream_init failed"); goto ogg_out; } ogg_stream_pagein(&sstate, &page); ogg_stream_packetout(&sstate, &pack); switch (ogm_packet_type(pack)) { case Vorbis: vorbis_info_init(inf); vorbis_comment_init(com); if(vorbis_synthesis_headerin(inf, com, &pack) < 0) { tc_log_warn(__FILE__, "Could not decode vorbis header " "packet - invalid vorbis stream ()"); } else { #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) Vorbis audio; " "rate: %ldHz, channels: %d, bitrate %3.2f kb/s", natracks + 1, natracks + nvtracks + 1, inf->rate, inf->channels, (double)inf->bitrate_nominal/1000.0); #endif ipipe->probe_info->track[natracks].samplerate = inf->rate; ipipe->probe_info->track[natracks].chan = inf->channels; ipipe->probe_info->track[natracks].bits = 0; /* XXX --tibit*/ ipipe->probe_info->track[natracks].format = TC_CODEC_VORBIS; ipipe->probe_info->track[natracks].bitrate = (double)inf->bitrate_nominal/1000.0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[natracks].serial = sno; streams[natracks].vorbis = 1; ac_memcpy(&streams[natracks].state, &sstate, sizeof(sstate)); natracks++; } break; #ifdef HAVE_THEORA case Theora: { theora_info ti; theora_comment tc; theora_decode_header(&ti, &tc, &pack); ipipe->probe_info->width = ti.width; ipipe->probe_info->height = ti.height; ipipe->probe_info->fps = (double)ti.fps_numerator/ti.fps_denominator; tc_frc_code_from_ratio(&(ipipe->probe_info->frc), ti.fps_numerator, ti.fps_denominator); ipipe->probe_info->codec=TC_CODEC_THEORA; idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; break; } #endif case DirectShow: if ((*(int32_t*)(pack.packet+96) == 0x05589f80) && (pack.bytes >= 184)) { tc_log_warn(__FILE__, "(v%d/%d) Found old video " "header. Not supported.", nvtracks + 1, natracks + nvtracks + 1); } else if (*(int32_t*)pack.packet+96 == 0x05589F81) { tc_log_warn(__FILE__, "(a%d/%d) Found old audio " "header. Not supported.", natracks + 1, natracks + nvtracks + 1); } break; case StreamHeader: sth = (ogm_stream_header *)(pack.packet + 1); if (!strncmp(sth->streamtype, "video", 5)) { #ifdef OGM_DEBUG unsigned long codec; codec = (sth->subtype[0] << 24) + (sth->subtype[1] << 16) + (sth->subtype[2] << 8) + sth->subtype[3]; tc_log_msg(__FILE__, "(v%d/%d) video; fps: %.3f width height: %dx%d " "codec: %p (%c%c%c%c)", nvtracks + 1, natracks + nvtracks + 1, (double)10000000 / (double)sth->time_unit, sth->sh.video.width, sth->sh.video.height, (void *)codec, sth->subtype[0], sth->subtype[1], sth->subtype[2], sth->subtype[3]); #endif vid_codec[0] = sth->subtype[0]; vid_codec[1] = sth->subtype[1]; vid_codec[2] = sth->subtype[2]; vid_codec[3] = sth->subtype[3]; vid_codec[4] = '\0'; //ipipe->probe_info->frames = AVI_video_frames(avifile); ipipe->probe_info->width = sth->sh.video.width; ipipe->probe_info->height = sth->sh.video.height; ipipe->probe_info->fps = (double)10000000 / (double)sth->time_unit; tc_frc_code_from_value(&(ipipe->probe_info->frc), ipipe->probe_info->fps); ipipe->probe_info->codec=TC_CODEC_UNKNOWN; // gets rewritten if(strlen(vid_codec)==0) { ipipe->probe_info->codec=TC_CODEC_RGB24; } else { if(strcasecmp(vid_codec,"dvsd")==0) ipipe->probe_info->codec=TC_CODEC_DV; if(strcasecmp(vid_codec,"DIV3")==0) ipipe->probe_info->codec=TC_CODEC_DIVX3; if(strcasecmp(vid_codec,"DIVX")==0) ipipe->probe_info->codec=TC_CODEC_DIVX4; if(strcasecmp(vid_codec,"DX50")==0) ipipe->probe_info->codec=TC_CODEC_DIVX5; if(strcasecmp(vid_codec,"XVID")==0) ipipe->probe_info->codec=TC_CODEC_XVID; if(strcasecmp(vid_codec,"MJPG")==0) ipipe->probe_info->codec=TC_CODEC_MJPEG; } idx = natracks + MAX_AUDIO_TRACKS; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); nvtracks++; } else if (!strncmp(sth->streamtype, "audio", 5)) { int codec; char buf[5]; ac_memcpy(buf, sth->subtype, 4); buf[4] = 0; codec = strtoul(buf, NULL, 16); #ifdef OGM_DEBUG tc_log_msg(__FILE__, "(a%d/%d) codec: %d (0x%04x) (%s) bits per " "sample: %d channels: %hd samples per second: %ld " "avgbytespersec: %hd blockalign: %d", natracks + 1, natracks + nvtracks + 1, codec, codec, codec == 0x1 ? "PCM" : codec == 55 ? "MP3" : codec == 0x55 ? "MP3" : codec == 0x2000 ? "AC3" : "unknown", sth->bits_per_sample, sth->sh.audio.channels, (long)sth->samples_per_unit, sth->sh.audio.avgbytespersec, sth->sh.audio.blockalign); #endif idx = natracks; ipipe->probe_info->track[natracks].samplerate = sth->samples_per_unit; ipipe->probe_info->track[natracks].chan = sth->sh.audio.channels; ipipe->probe_info->track[natracks].bits = (sth->bits_per_sample<4)?sth->bits_per_sample*8:sth->bits_per_sample; ipipe->probe_info->track[natracks].format = codec; ipipe->probe_info->track[natracks].bitrate = 0; ipipe->probe_info->track[natracks].tid=natracks; if(ipipe->probe_info->track[natracks].chan>0) ++ipipe->probe_info->num_tracks; streams[idx].serial = sno; ac_memcpy(&streams[idx].state, &sstate, sizeof(sstate)); natracks++; } else { tc_log_warn(__FILE__, "(%d) found new header of unknown/" "unsupported type\n", nvtracks + natracks + 1); } break; case none: tc_log_warn(__FILE__, "OGG stream %d is of an unknown type " "(bad header?)", nvtracks + natracks + 1); break; } /* switch type */ free(inf); free(com); ogg_stream_clear(&sstate); } /* beginning of page */ } /* while (1) */ ogg_out: //close(fdin); return; }
/* * init driver */ static int init(sh_video_t *sh){ theora_struct_t *context = NULL; int failed = 1; int errorCode = 0; ogg_packet op; int i; /* check whether video output format is supported */ switch(sh->codec->outfmt[sh->outfmtidx]) { case IMGFMT_YV12: /* well, this should work... */ break; default: mp_msg (MSGT_DECVIDEO,MSGL_ERR,"Unsupported out_fmt: 0x%X\n", sh->codec->outfmt[sh->outfmtidx]); return 0; } /* this is not a loop, just a context, from which we can break on error */ do { context = calloc (sizeof (theora_struct_t), 1); sh->context = context; if (!context) break; theora_info_init(&context->inf); theora_comment_init(&context->cc); /* Read all header packets, pass them to theora_decode_header. */ for (i = 0; i < THEORA_NUM_HEADER_PACKETS; i++) { op.bytes = ds_get_packet (sh->ds, &op.packet); op.b_o_s = 1; if ( (errorCode = theora_decode_header (&context->inf, &context->cc, &op)) ) { mp_msg(MSGT_DECAUDIO, MSGL_ERR, "Broken Theora header; errorCode=%i!\n", errorCode); break; } } if (errorCode) break; /* now init codec */ errorCode = theora_decode_init (&context->st, &context->inf); if (errorCode) { mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Theora decode init failed: %i \n", errorCode); break; } failed = 0; } while (0); if (failed) { if (context) { free (context); sh->context = NULL; } return 0; } if(sh->aspect==0.0 && context->inf.aspect_denominator!=0) { sh->aspect = (float)(context->inf.aspect_numerator * context->inf.frame_width)/ (context->inf.aspect_denominator * context->inf.frame_height); } mp_msg(MSGT_DECVIDEO,MSGL_V,"INFO: Theora video init ok!\n"); return mpcodecs_config_vo (sh,context->inf.frame_width,context->inf.frame_height,IMGFMT_YV12); }
gboolean ucil_theora_combine_av_file( const char *path, const char *codec, gboolean remove, ucil_processing_info_func_t procfunc, void *func_data, GError **error ) { FILE *f; FILE *af; FILE *vf; gchar *apath; gchar *vpath; long len; long filepos = 0; ogg_sync_state oy; ogg_stream_state tins; theora_comment tc; theora_info ti; theora_state ts; ogg_stream_state vs; vorbis_info vi; vorbis_comment vc; vorbis_dsp_state vd; vorbis_block vb; ogg_page og; ogg_packet op; ogg_packet header_comm; ogg_packet header_code; int pkt; double videopos, audiopos; f = fopen( path, "w" ); if( !f ) { gchar *name = g_filename_display_name( path ); g_set_error( error, G_FILE_ERROR, g_file_error_from_errno( errno ), _("Could not open '%s' for writing: %s"), name, g_strerror( errno ) ); g_free( name ); return FALSE; } apath = g_strconcat( path, ".atmp", NULL ); af = fopen( apath, "r" ); if( !af ) { gchar *name = g_filename_display_name( apath ); g_set_error( error, G_FILE_ERROR, g_file_error_from_errno( errno ), _("Could not open '%s' for reading: %s"), name, g_strerror( errno ) ); g_free( name ); g_free( apath ); fclose( f ); return FALSE; } vpath = g_strconcat( path, ".vtmp", NULL ); vf = fopen( vpath, "r" ); if( !vf ) { gchar *name = g_filename_display_name( vpath ); g_set_error( error, G_FILE_ERROR, g_file_error_from_errno( errno ), _("Could not open '%s' for reading: %s"), name, g_strerror( errno ) ); g_free( name ); g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); return FALSE; } fseek( af, 0L, SEEK_END ); len = ftell( af ); fseek( af, 0L, SEEK_SET ); ogg_sync_init( &oy ); theora_comment_init( &tc ); theora_info_init( &ti ); if( ogg_stream_init( &vs, 2 ) != 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Failed to initialize audio stream") ); return FALSE; } vorbis_info_init( &vi ); if( vorbis_encode_init( &vi, 2, 44100, -1, 128000, -1 ) ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Failed to initialize audio encoder") ); return FALSE; } vorbis_comment_init( &vc ); vorbis_analysis_init( &vd, &vi ); vorbis_block_init( &vd, &vb ); // // write theora header page // { ogg_page og; while( ogg_sync_pageout( &oy, &og ) == 0 ) { int bytes; bytes = buffer_data( vf, &oy ); if( bytes == 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } } if( !ogg_page_bos( &og ) ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } ogg_stream_init( &tins, ogg_page_serialno( &og ) ); fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); if( ogg_stream_pagein( &tins, &og ) < 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } pkt = 0; while( ( pkt < 3 ) && ( ogg_stream_packetout( &tins, &op ) > 0 ) ) { if( theora_decode_header( &ti, &tc, &op ) != 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } pkt++; } } // // Write vorbis header page // vorbis_analysis_headerout( &vd, &vc, &op, &header_comm, &header_code ); ogg_stream_packetin( &vs, &op ); if( ogg_stream_pageout( &vs, &og ) != 1 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Could not create audio stream") ); return FALSE; } fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); ogg_stream_packetin( &vs, &header_comm ); ogg_stream_packetin( &vs, &header_code ); // write remaining theora headers for( pkt = 1; pkt < 3; ) { ogg_page og; if( ogg_sync_pageout( &oy, &og ) > 0 ) { fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); if( ogg_stream_pagein( &tins, &og ) < 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } while( ( pkt < 3 ) && ( ogg_stream_packetout( &tins, &op ) > 0 ) ) { if( theora_decode_header( &ti, &tc, &op ) != 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } pkt++; } } else { int bytes; bytes = buffer_data( vf, &oy ); if( bytes == 0 ) { g_free( apath ); g_free( vpath ); fclose( f ); fclose( af ); fclose( vf ); g_set_error( error, UCIL_ERROR, UCIL_ERROR_INVALID_STREAM, _("Invalid video stream") ); return FALSE; } } } theora_decode_init( &ts, &ti ); // // Write remaining vorbis headers // while( ogg_stream_flush( &vs, &og ) > 0 ) { fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); } audiopos = 0.0; videopos = 0.0; while( !feof( af ) ) { signed char abuf[512 * 4]; int n_samples; if( feof( vf ) || ( audiopos <= videopos ) ) { n_samples = fread( abuf, 4, 512, af ); audiopos = encode_vorbis( f, &vd, &vs, &vb, 44100, audiopos, abuf, n_samples ); filepos += n_samples * 4; } else { buffer_data( vf, &oy ); if( ogg_sync_pageout( &oy, &og ) > 0 ) { double tmppos; fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); if( ogg_stream_pagein( &tins, &og ) < 0 ) { TRACE( "failed to decode theora\n" ); } ogg_stream_packetout( &tins, &op ); tmppos = theora_granule_time( &ts, ogg_page_granulepos( &og ) ); if( tmppos > 0 ) { videopos = tmppos; } } } if( procfunc ) { procfunc( func_data, (double)filepos/(double)len ); } } while( !feof( vf ) && buffer_data( vf, &oy ) ) { if( ogg_sync_pageout( &oy, &og ) > 0 ) { fwrite( og.header, 1, og.header_len, f ); fwrite( og.body, 1, og.body_len, f ); } } if( remove ) { unlink( vpath ); unlink( apath ); } g_free( vpath ); g_free( apath ); fclose( f ); fclose( af ); fclose( vf ); return TRUE; }
/***************************************************************************** * ProcessHeaders: process Theora headers. *****************************************************************************/ static int ProcessHeaders( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; ogg_packet oggpacket; unsigned pi_size[XIPH_MAX_HEADER_COUNT]; void *pp_data[XIPH_MAX_HEADER_COUNT]; unsigned i_count; if( xiph_SplitHeaders( pi_size, pp_data, &i_count, p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) ) return VLC_EGENERIC; if( i_count < 3 ) goto error; oggpacket.granulepos = -1; oggpacket.e_o_s = 0; oggpacket.packetno = 0; /* Take care of the initial Vorbis header */ oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */ oggpacket.bytes = pi_size[0]; oggpacket.packet = pp_data[0]; if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 ) { msg_Err( p_dec, "this bitstream does not contain Theora video data" ); goto error; } /* Set output properties */ if( !p_sys->b_packetizer ) switch( p_sys->ti.pixelformat ) { case OC_PF_420: p_dec->fmt_out.i_codec = VLC_CODEC_I420; break; case OC_PF_422: p_dec->fmt_out.i_codec = VLC_CODEC_I422; break; case OC_PF_444: p_dec->fmt_out.i_codec = VLC_CODEC_I444; break; case OC_PF_RSVD: default: msg_Err( p_dec, "unknown chroma in theora sample" ); break; } p_dec->fmt_out.video.i_width = p_sys->ti.width; p_dec->fmt_out.video.i_height = p_sys->ti.height; if( p_sys->ti.frame_width && p_sys->ti.frame_height ) { p_dec->fmt_out.video.i_visible_width = p_sys->ti.frame_width; p_dec->fmt_out.video.i_visible_height = p_sys->ti.frame_height; if( p_sys->ti.offset_x || p_sys->ti.offset_y ) { p_dec->fmt_out.video.i_x_offset = p_sys->ti.offset_x; p_dec->fmt_out.video.i_y_offset = p_sys->ti.offset_y; } } if( p_sys->ti.aspect_denominator && p_sys->ti.aspect_numerator ) { p_dec->fmt_out.video.i_sar_num = p_sys->ti.aspect_numerator; p_dec->fmt_out.video.i_sar_den = p_sys->ti.aspect_denominator; } else { p_dec->fmt_out.video.i_sar_num = 1; p_dec->fmt_out.video.i_sar_den = 1; } if( p_sys->ti.fps_numerator > 0 && p_sys->ti.fps_denominator > 0 ) { p_dec->fmt_out.video.i_frame_rate = p_sys->ti.fps_numerator; p_dec->fmt_out.video.i_frame_rate_base = p_sys->ti.fps_denominator; } msg_Dbg( p_dec, "%dx%d %.02f fps video, frame content " "is %dx%d with offset (%d,%d)", p_sys->ti.width, p_sys->ti.height, (double)p_sys->ti.fps_numerator/p_sys->ti.fps_denominator, p_sys->ti.frame_width, p_sys->ti.frame_height, p_sys->ti.offset_x, p_sys->ti.offset_y ); /* Sanity check that seems necessary for some corrupted files */ if( p_sys->ti.width < p_sys->ti.frame_width || p_sys->ti.height < p_sys->ti.frame_height ) { msg_Warn( p_dec, "trying to correct invalid theora header " "(frame size (%dx%d) is smaller than frame content (%d,%d))", p_sys->ti.width, p_sys->ti.height, p_sys->ti.frame_width, p_sys->ti.frame_height ); if( p_sys->ti.width < p_sys->ti.frame_width ) p_sys->ti.width = p_sys->ti.frame_width; if( p_sys->ti.height < p_sys->ti.frame_height ) p_sys->ti.height = p_sys->ti.frame_height; } /* The next packet in order is the comments header */ oggpacket.b_o_s = 0; oggpacket.bytes = pi_size[1]; oggpacket.packet = pp_data[1]; if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 ) { msg_Err( p_dec, "2nd Theora header is corrupted" ); goto error; } ParseTheoraComments( p_dec ); /* The next packet in order is the codebooks header * We need to watch out that this packet is not missing as a * missing or corrupted header is fatal. */ oggpacket.b_o_s = 0; oggpacket.bytes = pi_size[2]; oggpacket.packet = pp_data[2]; if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 ) { msg_Err( p_dec, "3rd Theora header is corrupted" ); goto error; } if( !p_sys->b_packetizer ) { /* We have all the headers, initialize decoder */ theora_decode_init( &p_sys->td, &p_sys->ti ); } else { p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra; p_dec->fmt_out.p_extra = xrealloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra ); memcpy( p_dec->fmt_out.p_extra, p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra ); } for( unsigned i = 0; i < i_count; i++ ) free( pp_data[i] ); return VLC_SUCCESS; error: for( unsigned i = 0; i < i_count; i++ ) free( pp_data[i] ); return VLC_EGENERIC; }
int OggInit(CFile *f, OggData *data) { ogg_packet packet; int num_vorbis; #ifdef USE_THEORA int num_theora; #endif int stream_start; int ret; unsigned magic; f->read(&magic, sizeof(magic)); if (SDL_SwapLE32(magic) != 0x5367674F) { // "OggS" in ASCII return -1; } f->seek(0, SEEK_SET); ogg_sync_init(&data->sync); vorbis_info_init(&data->vinfo); vorbis_comment_init(&data->vcomment); #ifdef USE_THEORA theora_info_init(&data->tinfo); theora_comment_init(&data->tcomment); #endif #ifdef USE_THEORA num_theora = 0; #endif num_vorbis = 0; stream_start = 0; while (!stream_start) { ogg_stream_state test; if (OggGetNextPage(&data->page, &data->sync, f)) { return -1; } if (!ogg_page_bos(&data->page)) { if (num_vorbis) { ogg_stream_pagein(&data->astream, &data->page); } #ifdef USE_THEORA if (num_theora) { ogg_stream_pagein(&data->vstream, &data->page); } #endif stream_start = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&data->page)); ogg_stream_pagein(&test, &data->page); // initial codec headers while (ogg_stream_packetout(&test, &packet) == 1) { #ifdef USE_THEORA if (theora_decode_header(&data->tinfo, &data->tcomment, &packet) >= 0) { memcpy(&data->vstream, &test, sizeof(test)); ++num_theora; } else #endif if (!vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) { memcpy(&data->astream, &test, sizeof(test)); ++num_vorbis; } else { ogg_stream_clear(&test); } } } data->audio = num_vorbis; #ifdef USE_THEORA data->video = num_theora; #endif // remainint codec headers while ((num_vorbis && num_vorbis < 3) #ifdef USE_THEORA || (num_theora && num_theora < 3) ) { // are we in the theora page ? while (num_theora && num_theora < 3 && (ret = ogg_stream_packetout(&data->vstream, &packet))) { if (ret < 0) { return -1; } if (theora_decode_header(&data->tinfo, &data->tcomment, &packet)) { return -1; } ++num_theora; } #else ) { #endif // are we in the vorbis page ? while (num_vorbis && num_vorbis < 3 && (ret = ogg_stream_packetout(&data->astream, &packet))) { if (ret < 0) { return -1; } if (vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) { return -1; } ++num_vorbis; } if (OggGetNextPage(&data->page, &data->sync, f)) { break; } if (num_vorbis) { ogg_stream_pagein(&data->astream, &data->page); } #ifdef USE_THEORA if (num_theora) { ogg_stream_pagein(&data->vstream, &data->page); } #endif } if (num_vorbis) { vorbis_synthesis_init(&data->vdsp, &data->vinfo); vorbis_block_init(&data->vdsp, &data->vblock); } else { vorbis_info_clear(&data->vinfo); vorbis_comment_clear(&data->vcomment); } #ifdef USE_THEORA if (num_theora) { theora_decode_init(&data->tstate, &data->tinfo); data->tstate.internal_encode = NULL; // needed for a bug in libtheora (fixed in next release) } else { theora_info_clear(&data->tinfo); theora_comment_clear(&data->tcomment); } return !(num_vorbis || num_theora); #else return !num_vorbis; #endif }
void parseHeaders(){ /* extracted from player_sample.c test file for theora alpha */ ogg_packet op; /* Parse the headers */ /* Only interested in Vorbis/Theora streams */ while(!stateflag){ int ret=buffer_data(&oy); if(ret==0)break; while(ogg_sync_pageout(&oy,&og)>0){ 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); ogg_stream_packetout(&test,&op); /* identify the codec: try theora */ if(!theora_p && theora_decode_header(&ti,&tc,&op)>=0){ /* it is theora */ memcpy(&to,&test,sizeof(test)); theora_p=1; }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){ /* it is vorbis */ memcpy(&vo,&test,sizeof(test)); vorbis_p=1; }else{ /* whatever it is, we don't care about it */ ogg_stream_clear(&test); } } } /* we've now identified all the bitstreams. parse the secondary header packets. */ while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){ int ret; /* look for further theora headers */ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){ if(ret<0){ printf("Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } if(theora_decode_header(&ti,&tc,&op)){ printf("Error parsing Theora stream headers; corrupt stream?\n"); exit(1); } theora_p++; if(theora_p==3)break; } /* look for more vorbis header packets */ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){ if(ret<0){ printf("Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } if(vorbis_synthesis_headerin(&vi,&vc,&op)){ printf("Error parsing Vorbis stream headers; corrupt stream?\n"); exit(1); } vorbis_p++; if(vorbis_p==3)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(&oy); if(ret==0){ fprintf(stderr,"End of file while searching for codec headers.\n"); exit(1); } } } }