bool TheoraDecoder::TheoraVideoTrack::decodePacket(ogg_packet &oggPacket) {
	if (th_decode_packetin(_theoraDecode, &oggPacket, 0) == 0) {
		_curFrame++;

		// Convert YUV data to RGB data
		th_ycbcr_buffer yuv;
		th_decode_ycbcr_out(_theoraDecode, yuv);
		translateYUVtoRGBA(yuv);

		double time = th_granule_time(_theoraDecode, oggPacket.granulepos);

		// We need to calculate when the next frame should be shown
		// This is all in floating point because that's what the Ogg code gives us
		// Ogg is a lossy container format, so it doesn't always list the time to the
		// next frame. In such cases, we need to calculate it ourselves.
		if (time == -1.0)
			_nextFrameStartTime += _frameRate.getInverse().toDouble();
		else
			_nextFrameStartTime = time;

		return true;
	}

	return false;
}
void TheoraVideoStream::parseHeader()
{
	if (headerParsed)
		return;

	th_comment comment;
	th_setup_info *setupInfo = nullptr;
	th_comment_init(&comment);
	int ret;

	demuxer.readPacket(packet);
	ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);

	if (ret < 0)
	{
		th_comment_clear(&comment);
		throw love::Exception("Could not find header");
	}

	while (ret > 0)
	{
		demuxer.readPacket(packet);
		ret = th_decode_headerin(&videoInfo, &comment, &setupInfo, &packet);
	}

	th_comment_clear(&comment);

	decoder = th_decode_alloc(&videoInfo, setupInfo);
	th_setup_free(setupInfo);

	Frame *buffers[2] = {backBuffer, frontBuffer};

	yPlaneXOffset = cPlaneXOffset = videoInfo.pic_x;
	yPlaneYOffset = cPlaneYOffset = videoInfo.pic_y;

	scaleFormat(videoInfo.pixel_fmt, cPlaneXOffset, cPlaneYOffset);

	for (int i = 0; i < 2; i++)
	{
		buffers[i]->cw = buffers[i]->yw = videoInfo.pic_width;
		buffers[i]->ch = buffers[i]->yh = videoInfo.pic_height;

		scaleFormat(videoInfo.pixel_fmt, buffers[i]->cw, buffers[i]->ch);

		buffers[i]->yplane = new unsigned char[buffers[i]->yw * buffers[i]->yh];
		buffers[i]->cbplane = new unsigned char[buffers[i]->cw * buffers[i]->ch];
		buffers[i]->crplane = new unsigned char[buffers[i]->cw * buffers[i]->ch];

		memset(buffers[i]->yplane, 16, buffers[i]->yw * buffers[i]->yh);
		memset(buffers[i]->cbplane, 128, buffers[i]->cw * buffers[i]->ch);
		memset(buffers[i]->crplane, 128, buffers[i]->cw * buffers[i]->ch);
	}

	headerParsed = true;
	th_decode_packetin(decoder, &packet, nullptr);
}
Exemple #3
0
int theora_decode_packetin(theora_state *_td,ogg_packet *_op){
  th_api_wrapper *api;
  ogg_int64_t     gp;
  int             ret;
  if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT;
  api=(th_api_wrapper *)_td->i->codec_setup;
  ret=th_decode_packetin(api->decode,_op,&gp);
  if(ret<0)return OC_BADPACKET;
  _td->granulepos=gp;
  return 0;
}
Exemple #4
0
IoObject *IoTheoraDecodeContext_packetin(IoTheoraDecodeContext *self, IoObject *locals, IoMessage *m)
{
	/*doc TheoraDecodecontext packetin(packet)
	Try to decode a theora frame from the packet.
	*/
        IoOggPacket *packet = IoMessage_locals_oggPacketArgAt_(m, locals, 3);
	int ret = th_decode_packetin(DATA(self)->ctx,
				     ((ogg_packet*)(IoObject_dataPointer(packet))),
				     &(DATA(self)->granulepos));

	return IONUMBER(ret);
}
Exemple #5
0
void krad_theora_decoder_decode(krad_theora_decoder_t *krad_theora, void *buffer, int len) {


	//printf("theora decode with %d\n", len);
	
	krad_theora->packet.packet = buffer;
	krad_theora->packet.bytes = len;
	krad_theora->packet.packetno++;
	
	th_decode_packetin(krad_theora->decoder, &krad_theora->packet, &krad_theora->granulepos);
	
	th_decode_ycbcr_out(krad_theora->decoder, krad_theora->ycbcr);

}
int theora_decode_packetin(theora_state *_td,ogg_packet *_op){
  th_api_wrapper *api;
  ogg_int64_t     gp;
  int             ret;
  api=(th_api_wrapper *)_td->i->codec_setup;
  ret=th_decode_packetin(api->decode,_op,&gp);

#ifdef _TH_DEBUG_
  dframe++;
#endif 

  if(ret<0)return OC_BADPACKET;
  _td->granulepos=gp;
  return 0;
}
Exemple #7
0
/*
 * decode frame
 */
static mp_image_t *decode(sh_video_t *sh, void *data, int len, int flags)
{
    theora_struct_t *context = sh->context;
    int errorCode = 0;
    ogg_packet op;
    mp_image_t *mpi;

    // no delayed frames
    if (!data || !len)
        return NULL;

    memset(&op, 0, sizeof(op));
    op.bytes      = len;
    op.packet     = data;
    op.granulepos = -1;

    errorCode = th_decode_packetin(context->tctx, &op, NULL);
    if (errorCode < 0) {
        mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Theora decode packetin failed: %i \n",
               errorCode);
        return NULL;
    }

    if (errorCode != TH_DUPFRAME) {
        errorCode = th_decode_ycbcr_out(context->tctx, context->ycbcrbuf);
        if (errorCode != 0) {
            mp_msg(MSGT_DECVIDEO, MSGL_ERR,
                   "Theora decode YUVout failed: %i \n", errorCode);
            return NULL;
        }
    }

    mpi = mpcodecs_get_image(sh, MP_IMGTYPE_EXPORT, 0,
                             context->ycbcrbuf[0].width,
                             context->ycbcrbuf[0].height);
    if (!mpi)
        return NULL;

    mpi->planes[0] = context->ycbcrbuf[0].data;
    mpi->stride[0] = context->ycbcrbuf[0].stride;
    mpi->planes[1] = context->ycbcrbuf[1].data;
    mpi->stride[1] = context->ycbcrbuf[1].stride;
    mpi->planes[2] = context->ycbcrbuf[2].data;
    mpi->stride[2] = context->ycbcrbuf[2].stride;

    return mpi;
}
Exemple #8
0
/*****************************************************************************
 * 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;
    th_ycbcr_buffer ycbcr;

    /* TODO: Implement _granpos (3rd parameter here) and add the
     * call to TH_DECCTL_SET_GRANDPOS after seek */
    /* TODO: If the return is TH_DUPFRAME, we don't need to display a new
     * frame, but we do need to keep displaying the previous one. */
    if (th_decode_packetin( p_sys->tcx, p_oggpacket, NULL ) < 0)
        return NULL; /* bad packet */

    /* 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 )
        return NULL; /* Wait until we've decoded the first keyframe */

    if( th_decode_ycbcr_out( p_sys->tcx, ycbcr ) ) /* returns 0 on success */
        return NULL;

    /* Get a new picture */
    if( decoder_UpdateVideoFormat( p_dec ) )
        return NULL;
    p_pic = decoder_NewPicture( p_dec );
    if( !p_pic ) return NULL;

    theora_CopyPicture( p_pic, ycbcr );

    p_pic->date = p_sys->i_pts;
    p_pic->b_progressive = true;

    return p_pic;
}
Exemple #9
0
void krad_theora_decoder_decode (krad_theora_decoder_t *krad_theora,
                                 void *buffer,
                                 int len) {

    int ret;

    krad_theora->packet.packet = buffer;
    krad_theora->packet.bytes = len;
    krad_theora->packet.packetno++;

    ret = th_decode_packetin (krad_theora->decoder,
                              &krad_theora->packet,
                              &krad_theora->granulepos);

    if (ret == 0) {
        th_decode_ycbcr_out (krad_theora->decoder, krad_theora->ycbcr);
    } else {

        printke ("theora decoder err! %d", ret);

        if (ret == TH_DUPFRAME) {
            printke ("theora decoder DUPFRAME!");
        }

        if (ret == TH_EFAULT) {
            printke ("theora decoder EFAULT!");
        }

        if (ret == TH_EBADPACKET) {
            printke ("theora decoder EBADPACKET!");
        }

        if (ret == TH_EIMPL) {
            printke ("theora decoder EIMPL!");
        }
    }
}
static GstFlowReturn
theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
    GstVideoCodecFrame * frame)
{
  /* normal data packet */
  th_ycbcr_buffer buf;
  gboolean keyframe;
  GstFlowReturn result;
  ogg_int64_t gp;

  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 (th_decode_packetin (dec->decoder, packet, &gp) < 0))
    goto decode_error;

  if (frame &&
      (gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (dec),
              frame) < 0))
    goto dropping_qos;

  /* this does postprocessing and set up the decoded frame
   * pointers in our yuv variable */
  if (G_UNLIKELY (th_decode_ycbcr_out (dec->decoder, buf) < 0))
    goto no_yuv;

  if (G_UNLIKELY ((buf[0].width != dec->info.frame_width)
          || (buf[0].height != dec->info.frame_height)))
    goto wrong_dimensions;

  result = theora_handle_image (dec, buf, frame);

  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");
    return GST_CUSTOM_FLOW_DROP;
  }
dropping_qos:
  {
    GST_WARNING_OBJECT (dec, "dropping frame because of QoS");
    return GST_CUSTOM_FLOW_DROP;
  }
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;
  }
}
Exemple #11
0
void VideoStreamPlaybackTheora::update(float p_delta) {

	if (!file)
		return;

	if (!playing || paused) {
		//printf("not playing\n");
		return;
	};

#ifdef THEORA_USE_THREAD_STREAMING
	thread_sem->post();
#endif

	//print_line("play "+rtos(p_delta));
	time += p_delta;

	if (videobuf_time > get_time()) {
		return; //no new frames need to be produced
	}

	bool frame_done = false;
	bool audio_done = !vorbis_p;

	while (!frame_done || (!audio_done && !vorbis_eos)) {
		//a frame needs to be produced

		ogg_packet op;
		bool no_theora = false;

		while (vorbis_p) {
			int ret;
			float **pcm;

			bool buffer_full = false;

			/* if there's pending, decoded audio, grab it */
			ret = vorbis_synthesis_pcmout(&vd, &pcm);
			if (ret > 0) {

				const int AUXBUF_LEN = 4096;
				int to_read = ret;
				int16_t aux_buffer[AUXBUF_LEN];

				while (to_read) {

					int m = MIN(AUXBUF_LEN / vi.channels, to_read);

					int count = 0;

					for (int j = 0; j < m; j++) {
						for (int i = 0; i < vi.channels; i++) {

							int val = Math::fast_ftoi(pcm[i][j] * 32767.f);
							if (val > 32767) val = 32767;
							if (val < -32768) val = -32768;
							aux_buffer[count++] = val;
						}
					}

					if (mix_callback) {
						int mixed = mix_callback(mix_udata, aux_buffer, m);
						to_read -= mixed;
						if (mixed != m) { //could mix no more
							buffer_full = true;
							break;
						}
					} else {
						to_read -= m; //just pretend we sent the audio
					}
				}

				int tr = vorbis_synthesis_read(&vd, ret - to_read);

				if (vd.granulepos >= 0) {
					//print_line("wrote: "+itos(audio_frames_wrote)+" gpos: "+itos(vd.granulepos));
				}

				//print_line("mix audio!");

				audio_frames_wrote += ret - to_read;

				//print_line("AGP: "+itos(vd.granulepos)+" added "+itos(ret-to_read));

			} 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 */
					//printf("need moar data\n");
					break;
				};
			}

			audio_done = videobuf_time < (audio_frames_wrote / float(vi.rate));

			if (buffer_full)
				break;
		}

		while (theora_p && !frame_done) {
			/* theora is one in, one out... */
			if (ogg_stream_packetout(&to, &op) > 0) {

				if (false && pp_inc) {
					pp_level += pp_inc;
					th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level,
							sizeof(pp_level));
					pp_inc = 0;
				}
				/*HACK: This should be set after a seek or a gap, but we might not have
				a granulepos for the first packet (we only have them for the last
				packet on a page), so we just set it as often as we get it.
				To do this right, we should back-track from the last packet on the
				page and compute the correct granulepos for the first packet after
				a seek or a gap.*/
				if (op.granulepos >= 0) {
					th_decode_ctl(td, TH_DECCTL_SET_GRANPOS, &op.granulepos,
							sizeof(op.granulepos));
				}
				ogg_int64_t videobuf_granulepos;
				if (th_decode_packetin(td, &op, &videobuf_granulepos) == 0) {
					videobuf_time = th_granule_time(td, videobuf_granulepos);

					//printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);

					/* 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()) {
						frame_done = true;
					} else {
						/*If we are too slow, reduce the pp level.*/
						pp_inc = pp_level > 0 ? -1 : 0;
					}
				} else {
				}

			} else {
				no_theora = true;
				break;
			}
		}

#ifdef THEORA_USE_THREAD_STREAMING
		if (file && thread_eof && no_theora && theora_eos && ring_buffer.data_left() == 0) {
#else
		if (file && /*!videobuf_ready && */ no_theora && theora_eos) {
#endif
			printf("video done, stopping\n");
			stop();
			return;
		};

		if (!frame_done || !audio_done) {
			//what's the point of waiting for audio to grab a page?

			buffer_data();
			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 (videobuf_ready && videobuf_time <= get_time()) {

			//video_write();
			//videobuf_ready=0;
		} else {
			//printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
		}

		float tdiff = videobuf_time - get_time();
		/*If we have lots of extra time, increase the post-processing level.*/
		if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) {
			pp_inc = pp_level < pp_level_max ? 1 : 0;
		} else if (tdiff < ti.fps_denominator * 0.05 / ti.fps_numerator) {
			pp_inc = pp_level > 0 ? -1 : 0;
		}
	}

	video_write();
};

void VideoStreamPlaybackTheora::play() {

	if (!playing)
		time = 0;
	else {
		stop();
	}

	playing = true;
	delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms");
	delay_compensation /= 1000.0;
};

void VideoStreamPlaybackTheora::stop() {

	if (playing) {

		clear();
		set_file(file_name); //reset
	}
	playing = false;
	time = 0;
};

bool VideoStreamPlaybackTheora::is_playing() const {

	return playing;
};

void VideoStreamPlaybackTheora::set_paused(bool p_paused) {

	paused = p_paused;
	//pau = !p_paused;
};

bool VideoStreamPlaybackTheora::is_paused(bool p_paused) const {

	return paused;
};

void VideoStreamPlaybackTheora::set_loop(bool p_enable){

};

bool VideoStreamPlaybackTheora::has_loop() const {

	return false;
};

float VideoStreamPlaybackTheora::get_length() const {

	return 0;
};

String VideoStreamPlaybackTheora::get_stream_name() const {

	return "";
};

int VideoStreamPlaybackTheora::get_loop_count() const {

	return 0;
};

float VideoStreamPlaybackTheora::get_playback_position() const {

	return get_time();
};

void VideoStreamPlaybackTheora::seek(float p_time){

	// no
};

void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {

	mix_callback = p_callback;
	mix_udata = p_userdata;
}
int main(int argc,char *argv[]){

  ogg_packet op;

  int long_option_index;
  int c;

  struct timeb start;
  struct timeb after;
  struct timeb last;
  int fps_only=0;
  int frames = 0;

  FILE *infile = stdin;
  outfile = stdout;

#ifdef _WIN32 /* We need to set stdin/stdout to binary mode on windows. */
  /* Beware the evil ifdef. We avoid these where we can, but this one we
     cannot. Don't add any more, you'll probably go to hell if you do. */
  _setmode( _fileno( stdin ), _O_BINARY );
  _setmode( _fileno( stdout ), _O_BINARY );
#endif

  /* Process option arguments. */
  while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
    switch(c){
    case 'o':
      if(strcmp(optarg,"-")!=0){
        outfile=fopen(optarg,"wb");
        if(outfile==NULL){
          fprintf(stderr,"Unable to open output file '%s'\n", optarg);
          exit(1);
        }
      }else{
        outfile=stdout;
      }
      break;

    case 'c':
      crop=1;
      break;

    case 'r':
      raw=1;
      break;

    case 'f':
      fps_only = 1;
      outfile = NULL;
      break;

    default:
      usage();
    }
  }
  if(optind<argc){
    infile=fopen(argv[optind],"rb");
    if(infile==NULL){
      fprintf(stderr,"Unable to open '%s' for extraction.\n", argv[optind]);
      exit(1);
    }
    if(++optind<argc){
      usage();
      exit(1);
    }
  }
  /*Ok, Ogg parsing.
    The idea here is we have a bitstream that is made up of Ogg pages.
    The libogg sync layer will find them for us.
    There may be pages from several logical streams interleaved; we find the
     first theora stream and ignore any others.
    Then we pass the pages for our stream to the libogg stream layer which
     assembles our original set of packets out of them.
    It's the packets that libtheora actually knows how to handle.*/

  /* start up Ogg stream synchronization layer */
  ogg_sync_init(&oy);

  /* init supporting Theora structures needed in header parsing */
  th_comment_init(&tc);
  th_info_init(&ti);

  /*Ogg file open; parse the headers.
    Theora (like Vorbis) depends on some initial header packets for decoder
     setup and initialization.
    We retrieve these first before entering the main decode loop.*/

  /* Only interested in Theora streams */
  while(!stateflag){
    int ret=buffer_data(infile,&oy);
    if(ret==0)break;
    while(ogg_sync_pageout(&oy,&og)>0){
      int got_packet;
      ogg_stream_state test;

      /* is this a mandated initial header? If not, stop parsing */
      if(!ogg_page_bos(&og)){
        /* don't leak the page; get it into the appropriate stream */
        queue_page(&og);
        stateflag=1;
        break;
      }

      ogg_stream_init(&test,ogg_page_serialno(&og));
      ogg_stream_pagein(&test,&og);
      got_packet = ogg_stream_packetpeek(&test,&op);

      /* identify the codec: try theora */
      if((got_packet==1) && !theora_p && (theora_processing_headers=
       th_decode_headerin(&ti,&tc,&ts,&op))>=0){
        /* it is theora -- save this stream state */
        memcpy(&to,&test,sizeof(test));
        theora_p=1;
        /*Advance past the successfully processed header.*/
        if(theora_processing_headers)ogg_stream_packetout(&to,NULL);
      }else{
        /* whatever it is, we don't care about it */
        ogg_stream_clear(&test);
      }
    }
    /* fall through to non-bos page parsing */
  }

  /* we're expecting more header packets. */
  while(theora_p && theora_processing_headers){
    int ret;

    /* look for further theora headers */
    while(theora_processing_headers&&(ret=ogg_stream_packetpeek(&to,&op))){
      if(ret<0)continue;
      theora_processing_headers=th_decode_headerin(&ti,&tc,&ts,&op);
      if(theora_processing_headers<0){
        fprintf(stderr,"Error parsing Theora stream headers; "
         "corrupt stream?\n");
        exit(1);
      }
      else if(theora_processing_headers>0){
        /*Advance past the successfully processed header.*/
        ogg_stream_packetout(&to,NULL);
      }
      theora_p++;
    }

    /*Stop now so we don't fail if there aren't enough pages in a short
       stream.*/
    if(!(theora_p && theora_processing_headers))break;

    /* The header pages/packets will arrive before anything else we
       care about, or the stream is not obeying spec */

    if(ogg_sync_pageout(&oy,&og)>0){
      queue_page(&og); /* demux into the appropriate stream */
    }else{
      int ret=buffer_data(infile,&oy); /* someone needs more data */
      if(ret==0){
        fprintf(stderr,"End of file while searching for codec headers.\n");
        exit(1);
      }
    }
  }

  /* and now we have it all.  initialize decoders */
  if(theora_p){
    dump_comments(&tc);
    td=th_decode_alloc(&ti,ts);
    fprintf(stderr,"Ogg logical stream %lx is Theora %dx%d %.02f fps video\n"
     "Encoded frame content is %dx%d with %dx%d offset\n",
     to.serialno,ti.frame_width,ti.frame_height,
     (double)ti.fps_numerator/ti.fps_denominator,
     ti.pic_width,ti.pic_height,ti.pic_x,ti.pic_y);

    /*{
      int arg = 0xffff;
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
      arg=10;
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
    }*/
  }else{
    /* tear down the partial theora setup */
    th_info_clear(&ti);
    th_comment_clear(&tc);
  }
  /*Either way, we're done with the codec setup data.*/
  th_setup_free(ts);

  /* open video */
  if(theora_p)open_video();

  if(!raw && outfile){
    static const char *CHROMA_TYPES[4]={"420jpeg",NULL,"422jpeg","444"};
    int width;
    int height;
    if(ti.pixel_fmt>=4||ti.pixel_fmt==TH_PF_RSVD){
      fprintf(stderr,"Unknown pixel format: %i\n",ti.pixel_fmt);
      exit(1);
    }
    if(crop){
      int hdec;
      int vdec;
      hdec=!(ti.pixel_fmt&1);
      vdec=!(ti.pixel_fmt&2);
      if((ti.pic_x&hdec)||(ti.pic_width&hdec)
       ||(ti.pic_y&vdec)||(ti.pic_height&vdec)){
        fprintf(stderr,
         "Error: Cropped images with odd offsets/sizes and chroma subsampling\n"
         "cannot be output to YUV4MPEG2. Remove the --crop flag or add the\n"
         "--raw flag.\n");
        exit(1);
      }
      width=ti.pic_width;
      height=ti.pic_height;
    }
    else{
      width=ti.frame_width;
      height=ti.frame_height;
    }
    fprintf(outfile,"YUV4MPEG2 C%s W%d H%d F%d:%d I%c A%d:%d\n",
     CHROMA_TYPES[ti.pixel_fmt],width,height,
     ti.fps_numerator,ti.fps_denominator,'p',
     ti.aspect_numerator,ti.aspect_denominator);
  }

  /* install signal handler */
  signal (SIGINT, sigint_handler);

  /*Finally the main decode loop.

    It's one Theora packet per frame, so this is pretty straightforward if
     we're not trying to maintain sync with other multiplexed streams.

    The videobuf_ready flag is used to maintain the input buffer in the libogg
     stream state.
    If there's no output frame available at the end of the decode step, we must
     need more input data.
    We could simplify this by just using the return code on
     ogg_page_packetout(), but the flag system extends easily to the case where
     you care about more than one multiplexed stream (like with audio
     playback).
    In that case, just maintain a flag for each decoder you care about, and
     pull data when any one of them stalls.

    videobuf_time holds the presentation time of the currently buffered video
     frame.
    We ignore this value.*/

  stateflag=0; /* playback has not begun */
  /* queue any remaining pages from data we buffered but that did not
      contain headers */
  while(ogg_sync_pageout(&oy,&og)>0){
    queue_page(&og);
  }

  if(fps_only){
    ftime(&start);
    ftime(&last);
  }

  while(!got_sigint){

    while(theora_p && !videobuf_ready){
      /* theora is one in, one out... */
      if(ogg_stream_packetout(&to,&op)>0){

        if(th_decode_packetin(td,&op,&videobuf_granulepos)>=0){
          videobuf_time=th_granule_time(td,videobuf_granulepos);
          videobuf_ready=1;
          frames++;
          if(fps_only)
            ftime(&after);
        }

      }else
        break;
    }

    if(fps_only && (videobuf_ready || fps_only==2)){
      long ms =
        after.time*1000.+after.millitm-
        (last.time*1000.+last.millitm);

      if(ms>500 || fps_only==1 ||
         (feof(infile) && !videobuf_ready)){
        float file_fps = (float)ti.fps_numerator/ti.fps_denominator;
        fps_only=2;

        ms = after.time*1000.+after.millitm-
          (start.time*1000.+start.millitm);

        fprintf(stderr,"\rframe:%d rate:%.2fx           ",
                frames,
                frames*1000./(ms*file_fps));
        memcpy(&last,&after,sizeof(last));
      }
    }

    if(!videobuf_ready && feof(infile))break;

    if(!videobuf_ready){
      /* no data yet for somebody.  Grab another page */
      buffer_data(infile,&oy);
      while(ogg_sync_pageout(&oy,&og)>0){
        queue_page(&og);
      }
    }
    /* dumpvideo frame, and get new one */
    else if(outfile)video_write();

    videobuf_ready=0;
  }

  /* end of decoder loop -- close everything */

  if(theora_p){
    ogg_stream_clear(&to);
    th_decode_free(td);
    th_comment_clear(&tc);
    th_info_clear(&ti);
  }
  ogg_sync_clear(&oy);

  if(infile && infile!=stdin)fclose(infile);
  if(outfile && outfile!=stdout)fclose(outfile);

  fprintf(stderr, "\n\n%d frames\n", frames);
  fprintf(stderr, "\nDone.\n");

  return(0);

}
const Graphics::Surface *TheoraDecoder::decodeNextFrame() {
	// First, let's get our frame
	while (_theoraPacket) {
		// theora is one in, one out...
		if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {

			if (_ppInc) {
				_ppLevel += _ppInc;
				th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
				_ppInc = 0;
			}

			if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) {
				_curFrame++;

				// Convert YUV data to RGB data
				th_ycbcr_buffer yuv;
				th_decode_ycbcr_out(_theoraDecode, yuv);
				translateYUVtoRGBA(yuv);

				if (_curFrame == 0)
					_startTime = g_system->getMillis();

				double time = th_granule_time(_theoraDecode, _oggPacket.granulepos);

				// We need to calculate when the next frame should be shown
				// This is all in floating point because that's what the Ogg code gives us
				// Ogg is a lossy container format, so it doesn't always list the time to the
				// next frame. In such cases, we need to calculate it ourselves.
				if (time == -1.0)
					_nextFrameStartTime += _frameRate.getInverse().toDouble();
				else
					_nextFrameStartTime = time;

				// break out
				break;
			}
		} else {
			// If we can't get any more frames, we're done.
			if (_theoraOut.e_o_s || _fileStream->eos()) {
				_endOfVideo = true;
				break;
			}

			// Queue more data
			bufferData();
			while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
				queuePage(&_oggPage);
		}

		// Update audio if we can
		queueAudio();
	}

	// Force at least some audio to be buffered
	// TODO: 5 is very arbitrary. We probably should do something like QuickTime does.
	while (!_endOfAudio && _audStream->numQueuedStreams() < 5) {
		bufferData();
		while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
			queuePage(&_oggPage);

		bool queuedAudio = queueAudio();
		if ((_vorbisOut.e_o_s  || _fileStream->eos()) && !queuedAudio) {
			_endOfAudio = true;
			break;
		}
	}

	return &_displaySurface;
}
Exemple #14
0
yuv_buffer* TheoraDecoder::decodeTheora(StampedOggPacket* inPacket) 
{		
    //Accepts packet and deletes it.
    LOG(logDEBUG3) << __FUNCTIONW__;

	if (mPacketCount < 3) 
    {
		decodeHeader(inPacket);		//Accepts header and deletes it.

		if (mPacketCount == 3) 
        {
			mTheoraState = th_decode_alloc(&mTheoraInfo, mTheoraSetup);
			//TODO::: Post processing http://people.xiph.org/~tterribe/doc/libtheora-exp/theoradec_8h.html#a1
		}
		
        LOG(logDEBUG3) << __FUNCTIONW__ << " PacketCount under 3: " << mPacketCount;
		return NULL;
	} 
    else 
    {
		//if (mFirstPacket) 
        //{
		//	theora_decode_init(&mTheoraState, &mTheoraInfo);
		//	mFirstPacket = false;
		//}
		if (inPacket->packetSize() > 0 && (inPacket->packetData()[0] & 128) != 0) 
        {
			//Ignore header packets
			delete inPacket;

            LOG(logDEBUG3) << __FUNCTIONW__ << " Ignoring header packets";
			return NULL;
		}

		ogg_packet* locOldPack = simulateOldOggPacket(inPacket);		//Accepts the packet and deletes it.

		th_decode_packetin(mTheoraState, locOldPack, NULL);

        delete locOldPack->packet;
		delete locOldPack;
		
		th_decode_ycbcr_out(mTheoraState, mYCbCrBuffer);

		//TODO:::
		//This is slightly nasty for now... since changing the return type
		// will screw with other stuff
		//
		//Need to probably use the theora-exp buffer type and change all the
		// uses of yuv_buffer to handle this, and avoid assumptions about
		// the relative size of the Y and U and V buffer

		mYUVBuffer.y_width = mYCbCrBuffer[0].width;
		mYUVBuffer.y_height = mYCbCrBuffer[0].height;
		mYUVBuffer.y_stride = mYCbCrBuffer[0].stride;
		mYUVBuffer.y = mYCbCrBuffer[0].data;
		mYUVBuffer.uv_width = mYCbCrBuffer[1].width;
		mYUVBuffer.uv_height = mYCbCrBuffer[1].height;
		mYUVBuffer.uv_stride = mYCbCrBuffer[1].stride;
		mYUVBuffer.u = mYCbCrBuffer[1].data;
		mYUVBuffer.v = mYCbCrBuffer[2].data;
		
		return &mYUVBuffer;
	}
}
int main(int argc,char *const *argv){

  int pp_level_max;
  int pp_level;
  int pp_inc;
  int i,j;
  ogg_packet op;

  FILE *infile = stdin;

  int frames = 0;
  int dropped = 0;

#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 */
  th_comment_init(&tc);
  th_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 && th_decode_headerin(&ti,&tc,&ts,&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(!th_decode_headerin(&ti,&tc,&ts,&op)){
        fprintf(stderr,"Error parsing Theora stream headers; "
         "corrupt stream?\n");
        exit(1);
      }
      theora_p++;
    }

    /* 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){
    td=th_decode_alloc(&ti,ts);
    printf("Ogg logical stream %lx is Theora %dx%d %.02f fps",
           to.serialno,ti.pic_width,ti.pic_height,
           (double)ti.fps_numerator/ti.fps_denominator);
    px_fmt=ti.pixel_fmt;
    switch(ti.pixel_fmt){
      case TH_PF_420: printf(" 4:2:0 video\n"); break;
      case TH_PF_422: printf(" 4:2:2 video\n"); break;
      case TH_PF_444: printf(" 4:4:4 video\n"); break;
      case TH_PF_RSVD:
      default:
       printf(" video\n  (UNKNOWN Chroma sampling!)\n");
       break;
    }
    if(ti.pic_width!=ti.frame_width || ti.pic_height!=ti.frame_height)
      printf("  Frame content is %dx%d with offset (%d,%d).\n",
           ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y);
    report_colorspace(&ti);
    dump_comments(&tc);
    th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,
     sizeof(pp_level_max));
    pp_level=pp_level_max;
    th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
    pp_inc=0;

    /*{
      int arg = 0xffff;
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
      arg=10;
      th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
    }*/
  }else{
    /* tear down the partial theora setup */
    th_info_clear(&ti);
    th_comment_clear(&tc);
  }
  
  th_setup_free(ts);
  
  if(vorbis_p){
    vorbis_synthesis_init(&vd,&vi);
    vorbis_block_init(&vd,&vb);
    fprintf(stderr,"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 */
  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){

        if(pp_inc){
          pp_level+=pp_inc;
          th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,
           sizeof(pp_level));
          pp_inc=0;
        }
        /*HACK: This should be set after a seek or a gap, but we might not have
           a granulepos for the first packet (we only have them for the last
           packet on a page), so we just set it as often as we get it.
          To do this right, we should back-track from the last packet on the
           page and compute the correct granulepos for the first packet after
           a seek or a gap.*/
        if(op.granulepos>=0){
          th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos,
           sizeof(op.granulepos));
        }
        if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){
          videobuf_time=th_granule_time(td,videobuf_granulepos);
          frames++;

          /* 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{
            /*If we are too slow, reduce the pp level.*/
            pp_inc=pp_level>0?-1:0;
            dropped++;
          }
        }

      }else
        break;
    }

    if(!videobuf_ready && !audiobuf_ready && feof(infile))break;

    if(!videobuf_ready || !audiobuf_ready){
      /* no data yet for somebody.  Grab another page */
      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){
        double tdiff;
        long milliseconds;
        tdiff=videobuf_time-get_time();
        /*If we have lots of extra time, increase the post-processing level.*/
        if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){
          pp_inc=pp_level<pp_level_max?1:0;
        }
        else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){
          pp_inc=pp_level>0?-1:0;
        }
        milliseconds=tdiff*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);
    th_decode_free(td);
    th_comment_clear(&tc);
    th_info_clear(&ti);
  }
  ogg_sync_clear(&oy);

  if(infile && infile!=stdin)fclose(infile);

  fprintf(stderr,
          "\r                                                             \r");
  fprintf(stderr, "%d frames", frames);
  if (dropped) fprintf(stderr, " (%d dropped)", dropped);
  fprintf(stderr, "\n");
  fprintf(stderr, "\nDone.\n");

  return(0);

}
void VideoStreamTheora::update() {

	if (!playing) {
		//printf("not playing\n");
		return;
	};

	double ctime =AudioServer::get_singleton()->get_mix_time();

	if (last_update_time) {
		double delta = (ctime-last_update_time);
		time+=delta;
		//print_line("delta: "+rtos(delta));
	}
	last_update_time=ctime;


	int audio_todo = get_todo();
	ogg_packet op;
	int audio_pending = 0;


	while (vorbis_p && audio_todo) {
		int ret;
		float **pcm;

		/* if there's pending, decoded audio, grab it */
		if ((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0) {

			audio_pending = ret;
			int16_t* out = get_write_buffer();
			int count = 0;
			int to_read = MIN(ret, audio_todo);
			for (int i=0; i<to_read; i++) {

				for(int j=0;j<vi.channels;j++){
					int val=Math::fast_ftoi(pcm[j][i]*32767.f);
					if(val>32767)val=32767;
					if(val<-32768)val=-32768;
					out[count++] = val;
				};
			};
			int tr = vorbis_synthesis_read(&vd, to_read);
			audio_todo -= to_read;
			audio_frames_wrote += to_read;
			write(to_read);
			audio_pending -= to_read;
			if (audio_todo==0)
				buffering=false;


		} 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 */
				//printf("need moar data\n");
				break;
			};
		}
	}

	while(theora_p && !videobuf_ready){
		/* theora is one in, one out... */
		if(ogg_stream_packetout(&to,&op)>0){


			if(pp_inc){
				pp_level+=pp_inc;
				th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,
							  sizeof(pp_level));
				pp_inc=0;
			}
			/*HACK: This should be set after a seek or a gap, but we might not have
			a granulepos for the first packet (we only have them for the last
			packet on a page), so we just set it as often as we get it.
			To do this right, we should back-track from the last packet on the
			page and compute the correct granulepos for the first packet after
			a seek or a gap.*/
			if(op.granulepos>=0){
				th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos,
							  sizeof(op.granulepos));
			}
			ogg_int64_t videobuf_granulepos;
			if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){
				videobuf_time=th_granule_time(td,videobuf_granulepos);
				//printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);

				/* 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{
					/*If we are too slow, reduce the pp level.*/
					pp_inc=pp_level>0?-1:0;
				}
			}

		} else
			break;
	}

	if (/*!videobuf_ready && */ audio_pending == 0 && file->eof_reached()) {
		printf("video done, stopping\n");
		stop();
		return;
	};

	if (!videobuf_ready || audio_todo > 0){
		/* no data yet for somebody.  Grab another page */

		buffer_data();
		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(videobuf_ready && videobuf_time<=get_time()){

		video_write();
		videobuf_ready=0;
	} else {
		//printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
	}

	float tdiff=videobuf_time-get_time();
	/*If we have lots of extra time, increase the post-processing level.*/
	if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){
		pp_inc=pp_level<pp_level_max?1:0;
	}
	else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){
		pp_inc=pp_level>0?-1:0;
	}
};
void TheoraVideoStream::threadedFillBackBuffer(double dt)
{
	// Synchronize
	frameSync->update(dt);
	double position = frameSync->getPosition();

	// Seeking backwards
	if (position < lastFrame)
		seekDecoder(position);

	// Until we are at the end of the stream, or we are displaying the right frame
	unsigned int lagCounter = 0;
	while (!demuxer.isEos() && position >= nextFrame)
	{
		// If we can't catch up, seek
		if (lagCounter++ > 5)
		{
			seekDecoder(position);
			lagCounter = 0;
		}

		th_ycbcr_buffer bufferinfo;
		th_decode_ycbcr_out(decoder, bufferinfo);

		ogg_int64_t granulePosition;
		do
		{
			if (demuxer.readPacket(packet))
				return;
		} while (th_decode_packetin(decoder, &packet, &granulePosition) != 0);
		lastFrame = nextFrame;
		nextFrame = th_granule_time(decoder, granulePosition);

		// Don't swap whilst we're writing to the backbuffer
		{
			love::thread::Lock l(bufferMutex);
			frameReady = false;
		}

		for (int y = 0; y < backBuffer->yh; ++y)
		{
			memcpy(backBuffer->yplane+backBuffer->yw*y,
					bufferinfo[0].data+
						bufferinfo[0].stride*(y+yPlaneYOffset)+yPlaneXOffset,
					backBuffer->yw);
		}

		for (int y = 0; y < backBuffer->ch; ++y)
		{
			memcpy(backBuffer->cbplane+backBuffer->cw*y,
					bufferinfo[1].data+
						bufferinfo[1].stride*(y+cPlaneYOffset)+cPlaneXOffset,
					backBuffer->cw);
		}

		for (int y = 0; y < backBuffer->ch; ++y)
		{
			memcpy(backBuffer->crplane+backBuffer->cw*y,
					bufferinfo[2].data+
						bufferinfo[2].stride*(y+cPlaneYOffset)+cPlaneXOffset,
					backBuffer->cw);
		}

		// Re-enable swapping
		{
			love::thread::Lock l(bufferMutex);
			frameReady = true;
		}
	}
}
void TheoraPlayer::Update(float32 timeElapsed)
{
    if(!isPlaying)
        return;
        
    videoTime += timeElapsed;
    
    currFrameTime += timeElapsed;
    if(currFrameTime < frameTime)
    {
        return;
    }
    else
    {
        currFrameTime -= frameTime;
    }
    
    int ret;
    
    while(theora_p && !isVideoBufReady)
    {
        ret = ogg_stream_packetout(&theoraData->state, &theoraData->packet);
        if(ret > 0)
        {
            if(pp_inc)
            {
                pp_level += pp_inc;
                th_decode_ctl(theoraData->thCtx, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
                pp_inc = 0;
            }
            if(theoraData->packet.granulepos >= 0)
                th_decode_ctl(theoraData->thCtx, TH_DECCTL_SET_GRANPOS, &theoraData->packet.granulepos, sizeof(theoraData->packet.granulepos));

            if(th_decode_packetin(theoraData->thCtx, &theoraData->packet, &theoraData->videoBufGranulePos) == 0)
            {
                if((videoBufTime = th_granule_time(theoraData->thCtx, theoraData->videoBufGranulePos)) >= videoTime)
                    isVideoBufReady = true;
                else
                    pp_inc = (pp_level > 0)? -1 : 0;
            }
        }
        else
        {
            isVideoBufReady = false;
            break;
        }
    }
    
    if(!isVideoBufReady)
    {
        BufferData();
        while(ogg_sync_pageout(&theoraData->syncState, &theoraData->page) > 0)
            ogg_stream_pagein(&theoraData->state, &theoraData->page);
    }
    
    if(isVideoBufReady)
    {
        isVideoBufReady = false;
        ret = th_decode_ycbcr_out(theoraData->thCtx, theoraData->yuvBuffer);
    
        for(int i = 0; i < frameBufferH; i++) //Y
        {
            int yShift = 0, uShift = 0, vShift = 0;
            const bool inBuffer = (i <= theoraData->yuvBuffer[0].height);
            if(inBuffer)
            {
                yShift = theoraData->yuvBuffer[0].stride * i;
                uShift = theoraData->yuvBuffer[1].stride * (i / 2);
                vShift = theoraData->yuvBuffer[2].stride * (i / 2);
            }
            
            for(int j = 0; j < frameBufferW; j++) //X
            {
                const int index = (i * frameBufferW + j) * 4;
                
                if(inBuffer && j <= theoraData->yuvBuffer[0].width)
                {
                    const unsigned char Y = *(theoraData->yuvBuffer[0].data + yShift + j);
                    const unsigned char U = *(theoraData->yuvBuffer[1].data + uShift + j / 2);
                    const unsigned char V = *(theoraData->yuvBuffer[2].data + vShift + j / 2);
                
                    frameBuffer[index]   = ClampFloatToByte(Y + 1.371f * (V - 128));
                    frameBuffer[index+1] = ClampFloatToByte(Y - 0.698f * (V - 128) - 0.336f * (U - 128));
                    frameBuffer[index+2] = ClampFloatToByte(Y + 1.732f * (U - 128));
                    frameBuffer[index+3] = 255;
                }
                else
                {
                    memset(&frameBuffer[index], 0, 4 * sizeof(unsigned char));
                }
            }
        }
    
        if(!ret)
        {
            Texture * tex = Texture::CreateFromData(FORMAT_RGBA8888, frameBuffer, frameBufferW, frameBufferH, false);
            Sprite * spr = Sprite::CreateFromTexture(tex, 0, 0, tex->width, tex->height);
            spr->ConvertToVirtualSize();

            SafeRelease(tex);
            SetSprite(spr, 0);
            SafeRelease(spr);
        }
    }
    
    if(theora_p)
    {
        double tdiff = videoBufTime - videoTime;
        /*If we have lots of extra time, increase the post-processing level.*/
        if(tdiff > theoraData->thInfo.fps_denominator * 0.25f / theoraData->thInfo.fps_numerator)
        {
            pp_inc = (pp_level < pp_level_max) ? 1 : 0;
        }
        else if(tdiff < theoraData->thInfo.fps_denominator * 0.05 / theoraData->thInfo.fps_numerator)
        {
            pp_inc = (pp_level > 0)? -1 : 0;
        }
    }
    if(isRepeat && file->GetPos() == file->GetSize())
    {
        ReleaseData();
        OpenFile(filePath);
    }
}
Exemple #19
0
static GstFlowReturn
theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet,
    GstClockTime outtime, GstClockTime outdur)
{
  /* normal data packet */
  th_ycbcr_buffer buf;
  GstBuffer *out;
  gboolean keyframe;
  GstFlowReturn result;
  ogg_int64_t gp;

  if (G_UNLIKELY (!dec->have_header))
    goto not_initialized;

  /* get timestamp and durations */
  if (outtime == -1)
    outtime = dec->last_timestamp;
  if (outdur == -1)
    outdur = gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator,
        dec->info.fps_numerator);

  /* calculate expected next timestamp */
  if (outtime != -1 && outdur != -1)
    dec->last_timestamp = outtime + outdur;

  /* 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 (th_decode_packetin (dec->decoder, packet, &gp) < 0))
    goto decode_error;

  if (outtime != -1) {
    gboolean need_skip;
    GstClockTime running_time;
    GstClockTime earliest_time;
    gdouble proportion;

    /* qos needs to be done on running time */
    running_time = gst_segment_to_running_time (&dec->segment, GST_FORMAT_TIME,
        outtime);

    GST_OBJECT_LOCK (dec);
    proportion = dec->proportion;
    earliest_time = dec->earliest_time;
    /* check for QoS, don't perform the last steps of getting and
     * pushing the buffers that are known to be late. */
    need_skip = earliest_time != -1 && running_time <= earliest_time;
    GST_OBJECT_UNLOCK (dec);

    if (need_skip) {
      GstMessage *qos_msg;
      guint64 stream_time;
      gint64 jitter;

      GST_DEBUG_OBJECT (dec, "skipping decoding: qostime %"
          GST_TIME_FORMAT " <= %" GST_TIME_FORMAT,
          GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time));

      dec->dropped++;

      stream_time =
          gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, outtime);
      jitter = GST_CLOCK_DIFF (running_time, earliest_time);

      qos_msg =
          gst_message_new_qos (GST_OBJECT_CAST (dec), FALSE, running_time,
          stream_time, outtime, outdur);
      gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
      gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
          dec->processed, dec->dropped);
      gst_element_post_message (GST_ELEMENT_CAST (dec), qos_msg);

      goto dropping_qos;
    }
  }

  /* this does postprocessing and set up the decoded frame
   * pointers in our yuv variable */
  if (G_UNLIKELY (th_decode_ycbcr_out (dec->decoder, buf) < 0))
    goto no_yuv;

  if (G_UNLIKELY ((buf[0].width != dec->info.frame_width)
          || (buf[0].height != dec->info.frame_height)))
    goto wrong_dimensions;

  result = theora_handle_image (dec, buf, &out);
  if (result != GST_FLOW_OK)
    return result;

  GST_BUFFER_OFFSET (out) = dec->frame_nr;
  if (dec->frame_nr != -1)
    dec->frame_nr++;
  GST_BUFFER_OFFSET_END (out) = dec->frame_nr;

  GST_BUFFER_TIMESTAMP (out) = outtime;
  GST_BUFFER_DURATION (out) = outdur;

  dec->processed++;

  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;
  }
}
	void VideoClip_Theora::_executeSeek()
	{
#if _DEBUG
		log(this->name + " [seek]: seeking to frame " + str(this->seekFrame));
#endif
		int frame = 0;
		float time = this->seekFrame / getFps();
		this->timer->seek(time);
		bool paused = this->timer->isPaused();
		if (!paused)
		{
			this->timer->pause(); // pause until seeking is done
		}
		this->endOfFile = false;
		this->restarted = false;
		this->_resetFrameQueue();
		// reset the video decoder.
		ogg_stream_reset(&this->info.TheoraStreamState);
		th_decode_free(this->info.TheoraDecoder);
		this->info.TheoraDecoder = th_decode_alloc(&this->info.TheoraInfo, this->info.TheoraSetup);
		Mutex::ScopeLock audioMutexLock;
		if (this->audioInterface != NULL)
		{
			audioMutexLock.acquire(this->audioMutex);
			ogg_stream_reset(&this->info.VorbisStreamState);
			vorbis_synthesis_restart(&this->info.VorbisDSPState);
			this->destroyAllAudioPackets();
		}
		// first seek to desired frame, then figure out the location of the
		// previous key frame and seek to it.
		// then by setting the correct time, the decoder will skip N frames untill
		// we get the frame we want.
		frame = (int)this->_seekPage(this->seekFrame, 1); // find the key frame nearest to the target frame
#ifdef _DEBUG
	//		log(mName + " [seek]: nearest key frame for frame " + str(mSeekFrame) + " is frame: " + str(frame));
#endif
		this->_seekPage(std::max(0, frame - 1), 0);

		ogg_packet opTheora;
		ogg_int64_t granulePos;
		bool granuleSet = false;
		if (frame <= 1)
		{
			if (this->info.TheoraInfo.version_major == 3 && this->info.TheoraInfo.version_minor == 2 && this->info.TheoraInfo.version_subminor == 0)
			{
				granulePos = 0;
			}
			else
			{
				granulePos = 1; // because of difference in granule interpretation in theora streams 3.2.0 and newer ones
			}
			th_decode_ctl(this->info.TheoraDecoder, TH_DECCTL_SET_GRANPOS, &granulePos, sizeof(granulePos));
			granuleSet = true;
		}
		// now that we've found the key frame that preceeds our desired frame, lets keep on decoding frames until we
		// reach our target frame.
		int status = 0;
		while (this->seekFrame != 0)
		{
			if (ogg_stream_packetout(&this->info.TheoraStreamState, &opTheora) > 0)
			{
				if (!granuleSet)
				{
					// theora decoder requires to set the granule pos after seek to be able to determine the current frame
					if (opTheora.granulepos < 0)
					{
						continue; // ignore prev delta frames until we hit a key frame
					}
					th_decode_ctl(this->info.TheoraDecoder, TH_DECCTL_SET_GRANPOS, &opTheora.granulepos, sizeof(opTheora.granulepos));
					granuleSet = true;
				}
				status = th_decode_packetin(this->info.TheoraDecoder, &opTheora, &granulePos);
				if (status != 0 && status != TH_DUPFRAME)
				{
					continue;
				}
				frame = (int)th_granule_frame(this->info.TheoraDecoder, granulePos);
				if (frame >= this->seekFrame - 1)
				{
					break;
				}
			}
			else if (!this->_readData())
			{
				log(this->name + " [seek]: fineseeking failed, _readData failed!");
				return;
			}
		}
#ifdef _DEBUG
		//	log(mName + " [seek]: fineseeked to frame " + str(frame + 1) + ", requested: " + str(mSeekFrame));
#endif
		if (this->audioInterface != NULL)
		{
			// read audio data until we reach a timeStamp. this usually takes only one iteration, but just in case let's
			// wrap it in a loop
			float timeStamp = 0.0f;
			while (true)
			{
				timeStamp = this->_decodeAudio();
				if (timeStamp >= 0)
				{
					break;
				}
				this->_readData();
			}
			float rate = (float)this->audioFrequency * this->audioChannelsCount;
			float queuedTime = this->getAudioPacketQueueLength();
			int trimmedCount = 0;
			// at this point there are only 2 possibilities: either we have too much packets and we have to delete
			// the first N ones, or we don't have enough, so let's fill the gap with silence.
			if (time > timeStamp - queuedTime)
			{
				while (this->audioPacketQueue != NULL)
				{
					if (time <= timeStamp - queuedTime + this->audioPacketQueue->samplesCount / rate)
					{
						trimmedCount = (int)((timeStamp - queuedTime + this->audioPacketQueue->samplesCount / rate - time) * rate);
						if (this->audioPacketQueue->samplesCount - trimmedCount <= 0)
						{
							this->destroyAudioPacket(this->popAudioPacket()); // if there's no data to be left, just destroy it
						}
						else
						{
							for (int i = trimmedCount, j = 0; i < this->audioPacketQueue->samplesCount; ++i, ++j)
							{
								this->audioPacketQueue->pcmData[j] = this->audioPacketQueue->pcmData[i];
							}
							this->audioPacketQueue->samplesCount -= trimmedCount;
						}
						break;
					}
					queuedTime -= this->audioPacketQueue->samplesCount / rate;
					this->destroyAudioPacket(this->popAudioPacket());
				}
			}
			// expand the first packet with silence.
			else if (this->audioPacketQueue != NULL)
			{
				int i = 0;
				int j = 0;
				int missingCount = (int)((timeStamp - queuedTime - time) * rate);
				if (missingCount > 0)
				{
					float* samples = new float[missingCount + this->audioPacketQueue->samplesCount];
					if (missingCount > 0)
					{
						memset(samples, 0, missingCount * sizeof(float));
					}
					for (j = 0; i < missingCount + this->audioPacketQueue->samplesCount; ++i, ++j)
					{
						samples[i] = this->audioPacketQueue->pcmData[j];
					}
					delete[] this->audioPacketQueue->pcmData;
					this->audioPacketQueue->pcmData = samples;
				}
			}
			this->lastDecodedFrameNumber = this->seekFrame;
			this->readAudioSamples = (unsigned int)(timeStamp * this->audioFrequency);
			audioMutexLock.release();
		}
		if (!paused)
		{
			this->timer->play();
		}
		this->seekFrame = -1;
	}
	bool VideoClip_Theora::_decodeNextFrame()
	{
		if (this->endOfFile)
		{
			return false;
		}
		VideoFrame* frame = this->frameQueue->requestEmptyFrame();
		if (frame == NULL)
		{
			return false; // max number of precached frames reached
		}
		bool shouldRestart = false;
		ogg_packet opTheora;
		ogg_int64_t granulePos;
		th_ycbcr_buffer buff;
		int result = 0;
		int attempts = 0;
		int status = 0;
		float time = 0.0f;
		unsigned long frameNumber = 0;
		while (true)
		{
			// ogg_stream_packetout can return -1 and the official docs suggest to do subsequent calls until it succeeds
			// because the data is out of sync. still will limit the number of attempts just in case
			for (result = -1, attempts = 0; result < 0 && attempts < 100; ++attempts)
			{
				result = ogg_stream_packetout(&this->info.TheoraStreamState, &opTheora);
			}
			if (result > 0)
			{
				status = th_decode_packetin(this->info.TheoraDecoder, &opTheora, &granulePos);
				if (status != 0 && status != TH_DUPFRAME) // 0 means success
				{
					continue;
				}
				time = (float)th_granule_time(this->info.TheoraDecoder, granulePos);
				frameNumber = (unsigned long)th_granule_frame(this->info.TheoraDecoder, granulePos);
				// %16 operation is here to prevent a playback halt during video playback if the decoder can't keep up with demand.
				if (time < this->timer->getTime() && !this->restarted && frameNumber % 16 != 0)
				{
#ifdef _DEBUG_FRAMEDROP
					log(mName + ": pre-dropped frame " + str((int)frameNumber));
#endif
					++this->droppedFramesCount;
					continue; // drop frame
				}
				this->_setVideoFrameTimeToDisplay(frame, time - this->frameDuration);
				this->_setVideoFrameIteration(frame, this->iteration);
				this->_setVideoFrameFrameNumber(frame, (int)frameNumber);
				this->lastDecodedFrameNumber = frameNumber;
				th_decode_ycbcr_out(this->info.TheoraDecoder, buff);
				Theoraplayer_PixelTransform pixelTransform;
				memset(&pixelTransform, 0, sizeof(Theoraplayer_PixelTransform));
				pixelTransform.y = buff[0].data;	pixelTransform.yStride = buff[0].stride;
				pixelTransform.u = buff[1].data;	pixelTransform.uStride = buff[1].stride;
				pixelTransform.v = buff[2].data;	pixelTransform.vStride = buff[2].stride;
				frame->decode(&pixelTransform);
				break;
			}
			if (!this->_readData())
			{
				this->_setVideoFrameInUse(frame, false);
				shouldRestart = this->autoRestart;
				break;
			}
		}
		if (this->audioInterface != NULL)
		{
			Mutex::ScopeLock lock(this->audioMutex);
			this->_decodeAudio();
		}
		if (shouldRestart)
		{
			++this->iteration;
			this->_executeRestart();
		}
		return true;
	}