/***************************************************************************** * DecodePacket: decodes a Theora packet. *****************************************************************************/ static picture_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket ) { decoder_sys_t *p_sys = p_dec->p_sys; picture_t *p_pic; yuv_buffer yuv; theora_decode_packetin( &p_sys->td, p_oggpacket ); /* Check for keyframe */ if( !(p_oggpacket->packet[0] & 0x80) /* data packet */ && !(p_oggpacket->packet[0] & 0x40) /* intra frame */ ) p_sys->b_decoded_first_keyframe = true; /* If we haven't seen a single keyframe yet, don't let Theora decode * anything, otherwise we'll get display artifacts. (This is impossible * in the general case, but can happen if e.g. we play a network stream * using a timed URL, such that the server doesn't start the video with a * keyframe). */ if( p_sys->b_decoded_first_keyframe ) theora_decode_YUVout( &p_sys->td, &yuv ); else return NULL; /* Get a new picture */ p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) return NULL; theora_CopyPicture( p_pic, &yuv ); p_pic->date = p_sys->i_pts; return p_pic; }
static void dec_process_frame(MSFilter *f, DecState *s, ogg_packet *op){ yuv_buffer yuv; if (theora_decode_packetin(&s->tstate,op)==0){ if (theora_decode_YUVout(&s->tstate,&yuv)==0){ mblk_t *om; int i; int ylen=yuv.y_width*yuv.y_height; int uvlen=yuv.uv_width*yuv.uv_height; ms_debug("Got yuv buffer from theora decoder"); if (s->yuv==NULL){ int len=(ylen)+(2*uvlen); s->yuv=allocb(len,0); } om=dupb(s->yuv); for(i=0;i<yuv.y_height;++i){ memcpy(om->b_wptr,yuv.y+yuv.y_stride*i,yuv.y_width); om->b_wptr+=yuv.y_width; } for(i=0;i<yuv.uv_height;++i){ memcpy(om->b_wptr,yuv.u+yuv.uv_stride*i,yuv.uv_width); om->b_wptr+=yuv.uv_width; } for(i=0;i<yuv.uv_height;++i){ memcpy(om->b_wptr,yuv.v+yuv.uv_stride*i,yuv.uv_width); om->b_wptr+=yuv.uv_width; } ms_queue_put(f->outputs[0],om); } }else{ ms_warning("theora decoding error"); } }
bool Oggeyman::next_theora_packet(ogg_stream_state *state_ptr, ogg_packet *packet_ptr, theora_state * theo_state_ptr) { bool have_packet = false; bool packet_ok = false; do { have_packet = next_packet(state_ptr, packet_ptr); if (have_packet) packet_ok =(theora_decode_packetin(theo_state_ptr, packet_ptr) >= 0); } while (have_packet && !packet_ok); return have_packet && packet_ok; // superfluous, but readable }
static GF_Err THEO_ProcessData(GF_MediaDecoder *ifcg, char *inBuffer, u32 inBufferLength, u16 ES_ID, char *outBuffer, u32 *outBufferLength, u8 PaddingBits, u32 mmlevel) { ogg_packet op; yuv_buffer yuv; u32 i; char *pYO, *pUO, *pVO; unsigned char *pYD, *pUD, *pVD; THEORACTX(); /*not using scalabilty*/ assert(ctx->ES_ID == ES_ID); op.granulepos = -1; op.b_o_s = 0; op.e_o_s = 0; op.packetno = 0; op.packet = inBuffer; op.bytes = inBufferLength; *outBufferLength = 0; if (theora_decode_packetin(&ctx->td, &op)<0) return GF_NON_COMPLIANT_BITSTREAM; if (mmlevel == GF_CODEC_LEVEL_SEEK) return GF_OK; if (theora_decode_YUVout(&ctx->td, &yuv)<0) return GF_OK; pYO = yuv.y; pUO = yuv.u; pVO = yuv.v; pYD = outBuffer; pUD = outBuffer + ctx->ti.width * ctx->ti.height; pVD = outBuffer + 5 * ctx->ti.width * ctx->ti.height / 4; for (i=0; i<(u32)yuv.y_height; i++) { memcpy(pYD, pYO, sizeof(char) * yuv.y_width); pYD += ctx->ti.width; pYO += yuv.y_stride; if (i%2) continue; memcpy(pUD, pUO, sizeof(char) * yuv.uv_width); memcpy(pVD, pVO, sizeof(char) * yuv.uv_width); pUD += ctx->ti.width/2; pVD += ctx->ti.width/2; pUO += yuv.uv_stride; pVO += yuv.uv_stride; } *outBufferLength = 3*ctx->ti.width*ctx->ti.height/2; return GF_OK; }
/** ** Process Ogg data */ static int TheoraProcessData(OggData *data) { ogg_packet packet; while (1) { if (ogg_stream_packetout(&data->vstream, &packet) != 1) { if (OggGetNextPage(&data->page, &data->sync, data->File)) { // EOF return -1; } ogg_stream_pagein(&data->vstream, &data->page); } else { theora_decode_packetin(&data->tstate, &packet); return 0; } } }
uint8_t decoderTheora::uncompress(uint8_t *in,uint8_t *out,uint32_t len,uint32_t *flagz) { int got_picture=0; if(len==0) // Null frame, silently skip { if(flagz) *flagz=0; return 1; } ogg_packet ogg; memset(&ogg,0,sizeof(ogg)); ogg.packet=in; ogg.bytes =len; /* typedef struct { unsigned char *packet; long bytes; long b_o_s; long e_o_s; ogg_int64_t granulepos; ogg_int64_t packetno; sequence number for decode; the framing knows where there's a hole in the data, but we need coupling so that the codec (which is in a seperate abstraction layer) also knows about the gap } ogg_packet;*/ if(theora_decode_packetin(&_tstate,&ogg)) { printf("\n error decoding theora ..\n"); return 0; } yuv_buffer yuv; theora_decode_YUVout(&_tstate,&yuv); memcpy(out, yuv.y,_w*_h); memset(out+_w*_h,128,(_w*_h)>>1); return 1; }
int DGVideo::_prepareFrame() { while (_state == DGVideoPlaying) { while (_theoraInfo->theora_p && !_theoraInfo->videobuf_ready) { if (ogg_stream_packetout(&_theoraInfo->to, &_theoraInfo->op) > 0) { theora_decode_packetin(&_theoraInfo->td, &_theoraInfo->op); _theoraInfo->videobuf_granulepos = _theoraInfo->td.granulepos; _theoraInfo->videobuf_time = theora_granule_time(&_theoraInfo->td, _theoraInfo->videobuf_granulepos); _theoraInfo->videobuf_ready = 1; if (!_theoraInfo->bos) { _theoraInfo->bos = _theoraInfo->td.granulepos; } } else break; } if (!_theoraInfo->videobuf_ready && feof(_handle)) { if (_isLoopable) { // This is the begin of stream (granule position) * 8 bits (in bytes) fseek(_handle, (long)_theoraInfo->bos * 8, SEEK_SET); ogg_stream_reset(&_theoraInfo->to); } else { this->stop(); } break; } if (!_theoraInfo->videobuf_ready) { _bufferData(&_theoraInfo->oy); while (ogg_sync_pageout(&_theoraInfo->oy, &_theoraInfo->og) > 0) { _queuePage(_theoraInfo, &_theoraInfo->og); } } else { _theoraInfo->videobuf_ready = 0; return 1; } _theoraInfo->videobuf_ready = 0; } return 0; }
bool Oggeyman::process_packet(unsigned char *BGRAbuffer) { // if we got to here, it should be that // a) we have theora stream // b) this particular packet belongs to theora double this_packet_time = get_packet_time(); #ifdef ANDR struct timespec time1,time2; clock_gettime(CLOCK_MONOTONIC, &time1); #endif int retvalue = theora_decode_packetin(&theo_state, &ogg_theora_packet); #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "\tin process_packet: theora_decode_packetin %6.2lf ms\n", (time2.tv_nsec - time1.tv_nsec)/1.e6 ); #endif if (retvalue != 0) return false; // decoding ok, give us the YUV buffer pls #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time1); #endif theora_decode_YUVout(&theo_state, &YUVbuffer); #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "\tin process_packet: theora_decode_YUVout %6.2lf ms\n", (time2.tv_nsec - time1.tv_nsec)/1.e6 ); #endif // translate the buffer to BGRA, the Opengl's favorite format #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time1); #endif yuv2bgra(BGRAbuffer); #ifdef ANDR clock_gettime(CLOCK_MONOTONIC, &time2); __android_log_print(ANDROID_LOG_INFO, "foo", "\tin process_packet: yuv2bgra %6.2lf ms\n", (time2.tv_sec - time1.tv_sec)*1.e3 + (time2.tv_nsec - time1.tv_nsec)/1.e6 ); #endif last_packet_time = this_packet_time; return true; }
int mm_decode_video(mm_file *mf, SDL_Overlay *ovl) { int rv = 0; ogg_packet pkt; yuv_buffer yuv; assert(mf); if (!mf->video) { return -1; } if (mf->drop_packets & MEDIA_VIDEO) { WARNING1("requested decode but MEDIA_VIDEO is set to ignore"); return -1; } for (;;) { rv = get_packet(mf, &pkt, MEDIA_VIDEO); if (rv <= 0) { return rv; } /* we got packet, decode */ if (theora_decode_packetin(mf->video_ctx, &pkt) == 0) { break; } else { WARNING1("packet does not contain theora frame"); /* get next packet */ } } theora_decode_YUVout(mf->video_ctx, &yuv); if (yuv_to_overlay(mf, &yuv, ovl) < 0) { return -1; } return 1; }
/* * decode frame */ static mp_image_t* decode(sh_video_t *sh,void* data,int len,int flags) { theora_struct_t *context = (theora_struct_t *)sh->context; int errorCode = 0; ogg_packet op; yuv_buffer yuv; mp_image_t* mpi; memset (&op, 0, sizeof (op)); op.bytes = len; op.packet = data; op.granulepos = -1; errorCode = theora_decode_packetin (&context->st, &op); if (errorCode) { mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Theora decode packetin failed: %i \n", errorCode); return NULL; } errorCode = theora_decode_YUVout (&context->st, &yuv); if (errorCode) { mp_msg(MSGT_DEMUX,MSGL_ERR,"Theora decode YUVout failed: %i \n", errorCode); return 0; } mpi = mpcodecs_get_image(sh, MP_IMGTYPE_EXPORT, 0, yuv.y_width, yuv.y_height); if(!mpi) return NULL; mpi->planes[0]=yuv.y; mpi->stride[0]=yuv.y_stride; mpi->planes[1]=yuv.u; mpi->stride[1]=yuv.uv_stride; mpi->planes[2]=yuv.v; mpi->stride[2]=yuv.uv_stride; return mpi; }
bool CTheoraPlayer::playVideo( //Plays specified OGG Theora file to screen surface. //If screen == NULL, then this method will test that the file is playable //by decoding it as fast as possible but not displaying anything. // //Returns: whether playback was successful CStretchyBuffer& buffer, SDL_Surface *screen, const int x, const int y) //[default=(0,0)] { //init theora_p = vorbis_p = 0; startticks = 0; bool bSkippedLastFrame = false; // 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); if (!screen) ti.quick_p = 1; ti.quality = 63; if (!parseHeaders(buffer)) return false; // force audio off vorbis_p = 0; // initialize decoders if (theora_p) { theora_decode_init(&td,&ti); #if 0 printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n" " Frame content is %dx%d with offset (%d,%d).\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); //report_colorspace(&ti); //we're not using this info for anything dump_comments(&tc); #endif } 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); printf("Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n", vo.serialno,vi.channels,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 SDL_Overlay *yuv_overlay = NULL; if (theora_p && screen) yuv_overlay = open_video(screen); // single frame video buffering ogg_packet op; ogg_int64_t videobuf_granulepos=-1; double videobuf_time=0; double last_frame_time = 0; bool hasdatatobuffer = true; // Main loop bool audiobuf_ready=false; bool videobuf_ready=false; bool playbackdone = (yuv_overlay == NULL); bool isPlaying = false; bool bBreakout = false; while (!playbackdone) { // break out on SDL quit event SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: playbackdone = bBreakout = true; break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) playbackdone = bBreakout = true; break; default: break; } } while (theora_p && !videobuf_ready) { // get one video packet... if (ogg_stream_packetout(&to,&op)>0) { theora_decode_packetin(&td,&op); videobuf_granulepos=td.granulepos; videobuf_time=theora_granule_time(&td,videobuf_granulepos); #if 0 //Without sound channels to synch to, don't need to worry about skipping frames when slow. // update the frame counter //++frameNum; // check if this frame time has not passed yet. // If the frame is late we need to decode additional // ones and keep looping, since theora at this stage // needs to decode all frames. const double now=get_time(); const double delay=videobuf_time-now; if(delay>=0.0){ /// got a good frame, not late, ready to break out videobuf_ready=true; } else if(now-last_frame_time>=1.0) { // display at least one frame per second, regardless videobuf_ready=true; } else { //Need to catch up -- no time to display frame. if (bSkippedLastFrame) //only allow skipping one frame in a row videobuf_ready = true; //show anyway else bSkippedLastFrame = true; //printf("dropping frame %d (%.3fs behind)\n", frameNum, -delay); } #else videobuf_ready = true; //show every frame #endif } else { // need more data break; } } if (!hasdatatobuffer && !videobuf_ready && !audiobuf_ready) { isPlaying = false; playbackdone = true; } //If we're set for the next frame, sleep. //In other words, don't show frames too rapidly. if((!theora_p || videobuf_ready) && (!vorbis_p || audiobuf_ready)) { const int ticks = (int)(1000*(videobuf_time-get_time())); if(ticks>0 && screen) //don't need to sleep if only testing file SDL_Delay(ticks); } if (videobuf_ready) { // time to write our cached frame if (screen) { const bool bRes = video_write(screen, yuv_overlay, x, y); if (!bRes) //couldn't display image playbackdone = bBreakout = true; } videobuf_ready=false; last_frame_time=get_time(); bSkippedLastFrame = false; // if audio has not started (first frame) then start it if ((!isPlaying)&&(vorbis_p)) { start_audio(); isPlaying = true; } } // HACK: always look for more audio data audiobuf_ready=false; // buffer compressed data every loop if (hasdatatobuffer) { hasdatatobuffer = buffer_data(&oy, buffer) > 0; if (!hasdatatobuffer) { //printf("Ogg buffering stopped, end of file reached.\n"); } } if (ogg_sync_pageout(&oy,&og)>0) queue_page(&og); } // playbackdone // show number of video frames decoded //printf("\nFrames decoded: %d\n", frameNum); // deinit if (vorbis_p) { audio_close(); ogg_stream_clear(&vo); vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); vorbis_comment_clear(&vc); vorbis_info_clear(&vi); } if (theora_p) { if (yuv_overlay) SDL_FreeYUVOverlay(yuv_overlay); ogg_stream_clear(&to); theora_clear(&td); theora_comment_clear(&tc); theora_info_clear(&ti); } ogg_sync_clear(&oy); //If broken out of testing, return false since entire file was not verified. return !bBreakout || screen != NULL; }
static GstFlowReturn theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, GstClockTime outtime) { /* normal data packet */ yuv_buffer yuv; GstBuffer *out; guint i; gboolean keyframe; gint out_size; gint stride_y, stride_uv; gint width, height; gint cwidth, cheight; GstFlowReturn result; if (G_UNLIKELY (!dec->have_header)) goto not_initialized; /* the second most significant bit of the first data byte is cleared * for keyframes. We can only check it if it's not a zero-length packet. */ keyframe = packet->bytes && ((packet->packet[0] & 0x40) == 0); if (G_UNLIKELY (keyframe)) { GST_DEBUG_OBJECT (dec, "we have a keyframe"); dec->need_keyframe = FALSE; } else if (G_UNLIKELY (dec->need_keyframe)) { goto dropping; } GST_DEBUG_OBJECT (dec, "parsing data packet"); /* this does the decoding */ if (G_UNLIKELY (theora_decode_packetin (&dec->state, packet))) goto decode_error; if (outtime != -1) { gboolean need_skip; GstClockTime qostime; /* qos needs to be done on running time */ qostime = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME, outtime); GST_OBJECT_LOCK (dec); /* check for QoS, don't perform the last steps of getting and * pushing the buffers that are known to be late. */ /* FIXME, we can also entirely skip decoding if the next valid buffer is * known to be after a keyframe (using the granule_shift) */ need_skip = dec->earliest_time != -1 && qostime <= dec->earliest_time; GST_OBJECT_UNLOCK (dec); if (need_skip) goto dropping_qos; } /* this does postprocessing and set up the decoded frame * pointers in our yuv variable */ if (G_UNLIKELY (theora_decode_YUVout (&dec->state, &yuv) < 0)) goto no_yuv; if (G_UNLIKELY ((yuv.y_width != dec->info.width) || (yuv.y_height != dec->info.height))) goto wrong_dimensions; width = dec->width; height = dec->height; cwidth = width / 2; cheight = height / 2; /* should get the stride from the caps, for now we round up to the nearest * multiple of 4 because some element needs it. chroma needs special * treatment, see videotestsrc. */ stride_y = GST_ROUND_UP_4 (width); stride_uv = GST_ROUND_UP_8 (width) / 2; out_size = stride_y * height + stride_uv * cheight * 2; /* now copy over the area contained in offset_x,offset_y, * frame_width, frame_height */ result = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE, out_size, GST_PAD_CAPS (dec->srcpad), &out); if (G_UNLIKELY (result != GST_FLOW_OK)) goto no_buffer; /* copy the visible region to the destination. This is actually pretty * complicated and gstreamer doesn't support all the needed caps to do this * correctly. For example, when we have an odd offset, we should only combine * 1 row/column of luma samples with one chroma sample in colorspace conversion. * We compensate for this by adding a black border around the image when the * offset or size is odd (see above). */ { guchar *dest_y, *src_y; guchar *dest_u, *src_u; guchar *dest_v, *src_v; gint offset; dest_y = GST_BUFFER_DATA (out); dest_u = dest_y + stride_y * height; dest_v = dest_u + stride_uv * cheight; src_y = yuv.y + dec->offset_x + dec->offset_y * yuv.y_stride; for (i = 0; i < height; i++) { memcpy (dest_y, src_y, width); dest_y += stride_y; src_y += yuv.y_stride; } offset = dec->offset_x / 2 + dec->offset_y / 2 * yuv.uv_stride; src_u = yuv.u + offset; src_v = yuv.v + offset; for (i = 0; i < cheight; i++) { memcpy (dest_u, src_u, cwidth); memcpy (dest_v, src_v, cwidth); dest_u += stride_uv; src_u += yuv.uv_stride; dest_v += stride_uv; src_v += yuv.uv_stride; } } GST_BUFFER_OFFSET (out) = dec->frame_nr; if (dec->frame_nr != -1) dec->frame_nr++; GST_BUFFER_OFFSET_END (out) = dec->frame_nr; if (dec->granulepos != -1) { gint64 cf = _theora_granule_frame (dec, dec->granulepos) + 1; GST_BUFFER_DURATION (out) = gst_util_uint64_scale_int (cf * GST_SECOND, dec->info.fps_denominator, dec->info.fps_numerator) - outtime; } else { GST_BUFFER_DURATION (out) = gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator, dec->info.fps_numerator); } GST_BUFFER_TIMESTAMP (out) = outtime; if (dec->segment.rate >= 0.0) result = theora_dec_push_forward (dec, out); else result = theora_dec_push_reverse (dec, out); return result; /* ERRORS */ not_initialized: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("no header sent yet")); return GST_FLOW_ERROR; } dropping: { GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe"); dec->discont = TRUE; return GST_FLOW_OK; } dropping_qos: { if (dec->frame_nr != -1) dec->frame_nr++; dec->discont = TRUE; GST_WARNING_OBJECT (dec, "dropping frame because of QoS"); return GST_FLOW_OK; } decode_error: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("theora decoder did not decode data packet")); return GST_FLOW_ERROR; } no_yuv: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("couldn't read out YUV image")); return GST_FLOW_ERROR; } wrong_dimensions: { GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, FORMAT, (NULL), ("dimensions of image do not match header")); return GST_FLOW_ERROR; } no_buffer: { GST_DEBUG_OBJECT (dec, "could not get buffer, reason: %s", gst_flow_get_name (result)); return result; } }
static int loadVideoFrameTheora(void) { int r = 0; ogg_packet op; memset(&op,0,sizeof(op)); while( !r && (ogg_stream_packetout(&g_ogm.os_video,&op)) ) { ogg_int64_t th_frame; theora_decode_packetin(&g_ogm.th_state, &op); th_frame = theora_granule_frame(&g_ogm.th_state,g_ogm.th_state.granulepos); if((g_ogm.VFrameCount<th_frame && th_frame>=nextNeededVFrame()) || !g_ogm.outputBuffer) { // int i,j; int yWShift, uvWShift; int yHShift, uvHShift; if( theora_decode_YUVout(&g_ogm.th_state, &g_ogm.th_yuvbuffer) ) continue; if(g_ogm.outputWidht != g_ogm.th_info.width || g_ogm.outputHeight != g_ogm.th_info.height) { g_ogm.outputWidht = g_ogm.th_info.width; g_ogm.outputHeight = g_ogm.th_info.height; Com_DPrintf("[Theora(ogg)]new resolution %dx%d\n",g_ogm.outputWidht,g_ogm.outputHeight); } if(g_ogm.outputBufferSize < g_ogm.th_info.width*g_ogm.th_info.height) { g_ogm.outputBufferSize = g_ogm.th_info.width*g_ogm.th_info.height; /* Free old output buffer*/ if(g_ogm.outputBuffer) free(g_ogm.outputBuffer); /* Allocate the new buffer */ g_ogm.outputBuffer = (unsigned char*)malloc(g_ogm.outputBufferSize*4); if(g_ogm.outputBuffer == NULL) { g_ogm.outputBufferSize = 0; r = -2; break; } } yWShift = findSizeShift(g_ogm.th_yuvbuffer.y_width, g_ogm.th_info.width); uvWShift = findSizeShift(g_ogm.th_yuvbuffer.uv_width, g_ogm.th_info.width); yHShift = findSizeShift(g_ogm.th_yuvbuffer.y_height, g_ogm.th_info.height); uvHShift = findSizeShift(g_ogm.th_yuvbuffer.uv_height,g_ogm.th_info.height); if(yWShift<0 || uvWShift<0 || yHShift<0 || uvHShift<0) { Com_Printf("[Theora] unexpected resolution in a yuv-Frame\n"); r = -1; } else { Frame_yuv_to_rgb24(g_ogm.th_yuvbuffer.y,g_ogm.th_yuvbuffer.u,g_ogm.th_yuvbuffer.v, g_ogm.th_info.width, g_ogm.th_info.height, g_ogm.th_yuvbuffer.y_stride, g_ogm.th_yuvbuffer.uv_stride, yWShift, uvWShift, yHShift, uvHShift, (unsigned int*)g_ogm.outputBuffer ); /* unsigned char* pixelPtr = g_ogm.outputBuffer; unsigned int* pixPtr; pixPtr = (unsigned int*)g_ogm.outputBuffer; //TODO: use one yuv->rgb funktion for the hole frame (the big amout of stack movement(yuv->rgb calls) couldn't be good ;) ) for(j=0;j<g_ogm.th_info.height;++j) { for(i=0;i<g_ogm.th_info.width;++i) { #if 1 // simple grayscale-output ^^ pixelPtr[0] = pixelPtr[1] = pixelPtr[2] = g_ogm.th_yuvbuffer.y[i+j*g_ogm.th_yuvbuffer.y_stride]; pixelPtr+=4; #else // using RoQ yuv->rgb code *pixPtr++ = yuv_to_rgb24( g_ogm.th_yuvbuffer.y[(i>>yWShift)+(j>>yHShift)*g_ogm.th_yuvbuffer.y_stride], g_ogm.th_yuvbuffer.u[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride], g_ogm.th_yuvbuffer.v[(i>>uvWShift)+(j>>uvHShift)*g_ogm.th_yuvbuffer.uv_stride]); #endif } } */ r = 1; g_ogm.VFrameCount=th_frame; } } } return r; }
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
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); }
static void *ucil_theora_worker_thread( ucil_theora_input_file_object_t *vobj ) { unicap_data_buffer_t new_frame_buffer; struct timeval ltime; int eos = 0; unicap_copy_format( &new_frame_buffer.format, &vobj->format ); new_frame_buffer.type = UNICAP_BUFFER_TYPE_SYSTEM; new_frame_buffer.buffer_size = new_frame_buffer.format.buffer_size; new_frame_buffer.data = malloc( new_frame_buffer.format.buffer_size ); gettimeofday( <ime, NULL ); while( !vobj->quit_capture_thread ) { struct timespec abs_timeout; struct timeval ctime; GList *entry; ogg_page og; ogg_packet op; size_t bytes; int buffer_ready = 0; if( !eos && ( ogg_stream_packetout( &vobj->os, &op ) > 0 ) ) { yuv_buffer yuv; theora_decode_packetin( &vobj->th, &op ); theora_decode_YUVout( &vobj->th, &yuv ); copy_yuv( new_frame_buffer.data, &yuv, &vobj->ti ); buffer_ready = 1; } else if( !eos ) { bytes = buffer_data( vobj->f, &vobj->oy ); if( !bytes ) { TRACE( "End of stream\n" ); eos = 1; } while( ogg_sync_pageout( &vobj->oy, &og ) > 0 ) { ogg_stream_pagein( &vobj->os, &og ); } continue; } else { buffer_ready = 1; } gettimeofday( &ctime, NULL ); abs_timeout.tv_sec = ctime.tv_sec + 1; abs_timeout.tv_nsec = ctime.tv_usec * 1000; if( sem_timedwait( &vobj->sema, &abs_timeout ) ) { TRACE( "SEM_WAIT FAILED\n" ); continue; } if( buffer_ready && vobj->event_callback ) { vobj->event_callback( vobj->event_unicap_handle, UNICAP_EVENT_NEW_FRAME, &new_frame_buffer ); TRACE( "New frame\n" ); } unicap_data_buffer_t *data_buffer = g_queue_pop_head( vobj->in_queue ); if( data_buffer ) { unicap_copy_format( &data_buffer->format, &vobj->format ); memcpy( data_buffer->data, new_frame_buffer.data, vobj->format.buffer_size ); g_queue_push_tail( vobj->out_queue, data_buffer ); } sem_post( &vobj->sema ); if( buffer_ready ) { gettimeofday( &ctime, NULL ); if( ctime.tv_usec < ltime.tv_usec ) { ctime.tv_usec += 1000000; ctime.tv_sec -= 1; } ctime.tv_usec -= ltime.tv_usec; ctime.tv_sec -= ltime.tv_sec; if( ( ctime.tv_sec == 0 ) && ( ctime.tv_usec < vobj->frame_intervall ) ) { usleep( vobj->frame_intervall - ctime.tv_usec ); } gettimeofday( <ime, NULL ); } } free( new_frame_buffer.data ); return NULL; }
static int OGV_LoadVideoFrame(cinematic_t *cin) { int r = 0; ogg_packet op; memset(&op, 0, sizeof(op)); while (!r && (ogg_stream_packetout(&g_ogm->os_video, &op))) { ogg_int64_t th_frame; theora_decode_packetin(&g_ogm->th_state, &op); th_frame = theora_granule_frame(&g_ogm->th_state, g_ogm->th_state.granulepos); if ((g_ogm->VFrameCount < th_frame && th_frame >= OGV_NextNeededVFrame(cin)) || !cin->frameBuffer[0]) { if (theora_decode_YUVout(&g_ogm->th_state, &g_ogm->th_yuvbuffer)) { continue; } if (cin->frameWidth != g_ogm->th_info.width || cin->frameHeight != g_ogm->th_info.height) { cin->frameWidth = g_ogm->th_info.width; cin->frameHeight = g_ogm->th_info.height; Com_DPrintf("Theora new resolution %dx%d\n", cin->frameWidth, cin->frameHeight); } if (cin->frameBufferSize < g_ogm->th_info.width * g_ogm->th_info.height) { cin->frameBufferSize = g_ogm->th_info.width * g_ogm->th_info.height; /* Free old output buffer */ if (cin->frameBuffer[0]) { Com_Dealloc(cin->frameBuffer[0]); cin->frameBuffer[0] = NULL; } /* Allocate the new buffer */ cin->frameBuffer[0] = (unsigned char *)Com_Allocate(cin->frameBufferSize * 4); if (cin->frameBuffer[0] == NULL) { cin->frameBufferSize = 0; r = -2; break; } } if (OGV_yuv_to_rgb24(&g_ogm->th_yuvbuffer, &g_ogm->th_info, (unsigned int *) cin->frameBuffer[0])) { r = 1; g_ogm->VFrameCount = th_frame; } else { r = -1; } } } return r; }
int main( int argc, char* argv[] ){ int i,j; ogg_packet op; SDL_Event event; int hasdatatobuffer = 1; int playbackdone = 0; double now, delay, last_frame_time = 0; int frameNum=0; int skipNum=0; /* takes first argument as file to play */ /* this works better on Windows and is more convenient for drag and drop ogg files over the .exe */ if( argc != 2 ) { usage(); exit(0); } infile = fopen( argv[1], "rb" ); /* 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); parseHeaders(); /* force audio off */ /* vorbis_p = 0; */ /* initialize decoders */ if(theora_p){ theora_decode_init(&td,&ti); printf("Ogg logical stream %x is Theora %dx%d %.02f fps video\n" " Frame content is %dx%d with offset (%d,%d).\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); 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); printf("Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n", vo.serialno,vi.channels,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(); /* our main loop */ while(!playbackdone){ /* break out on SDL quit event */ if ( SDL_PollEvent ( &event ) ) { if ( event.type == SDL_QUIT ) break ; } /* get some audio data */ while(vorbis_p && !audiobuf_ready){ int ret; float **pcm; int count = 0; int maxBytesToWrite; /* is there pending audio? does it fit our circular buffer without blocking? */ ret=vorbis_synthesis_pcmout(&vd,&pcm); maxBytesToWrite = GetAudioStreamWriteable(aOutStream); if (maxBytesToWrite<=FRAMES_PER_BUFFER){ /* break out until there is a significant amount of data to avoid a series of small write operations. */ break; } /* if there's pending, decoded audio, grab it */ if((ret>0)&&(maxBytesToWrite>0)){ for(i=0;i<ret && i<(maxBytesToWrite/vi.channels);i++) for(j=0;j<vi.channels;j++){ int val=(int)(pcm[j][i]*32767.f); if(val>32767)val=32767; if(val<-32768)val=-32768; samples[count]=val; count++; } if(WriteAudioStream( aOutStream, samples, i )) { if(count==maxBytesToWrite){ audiobuf_ready=1; } } vorbis_synthesis_read(&vd,i); 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; } } /* end audio cycle */ while(theora_p && !videobuf_ready){ /* get one video packet... */ if(ogg_stream_packetout(&to,&op)>0){ theora_decode_packetin(&td,&op); videobuf_granulepos=td.granulepos; videobuf_time=theora_granule_time(&td,videobuf_granulepos); /* update the frame counter */ frameNum++; /* check if this frame time has not passed yet. If the frame is late we need to decode additonal ones and keep looping, since theora at this stage needs to decode all frames */ now=get_time(); delay=videobuf_time-now; if(delay>=0.0){ /* got a good frame, not late, ready to break out */ videobuf_ready=1; }else if(now-last_frame_time>=1.0){ /* display at least one frame per second, regardless */ videobuf_ready=1; }else{ fprintf(stderr, "dropping frame %d (%.3fs behind)\n", frameNum, -delay); } }else{ /* need more data */ break; } } if(!hasdatatobuffer && !videobuf_ready && !audiobuf_ready){ isPlaying = 0; playbackdone = 1; } /* if we're set for the next frame, sleep */ if((!theora_p || videobuf_ready) && (!vorbis_p || audiobuf_ready)){ int ticks = 1.0e3*(videobuf_time-get_time()); if(ticks>0) SDL_Delay(ticks); } if(videobuf_ready){ /* time to write our cached frame */ video_write(); videobuf_ready=0; last_frame_time=get_time(); /* if audio has not started (first frame) then start it */ if ((!isPlaying)&&(vorbis_p)){ start_audio(); isPlaying = 1; } } /* HACK: always look for more audio data */ audiobuf_ready=0; /* buffer compressed data every loop */ if(hasdatatobuffer){ hasdatatobuffer=buffer_data(&oy); if(hasdatatobuffer==0){ printf("Ogg buffering stopped, end of file reached.\n"); } } if (ogg_sync_pageout(&oy,&og)>0){ queue_page(&og); } } /* playbackdone */ /* show number of video frames decoded */ printf( "\n"); printf( "Frames decoded: %d", frameNum ); if(skipNum) printf( " (only %d shown)", frameNum-skipNum); printf( "\n" ); /* tear it all down */ fclose( infile ); if(vorbis_p){ audio_close(); 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); printf("\r " "\nDone.\n"); SDL_Quit(); return(0); }