Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
/* two tasks to do here: set the streamheader on the caps, and use libtheora to
   parse the headers */
static void
theora_parse_set_streamheader (GstTheoraParse * parse)
{
  GstCaps *caps;
  gint i;
  guint32 bitstream_version;
  th_setup_info *setup = NULL;

  g_assert (!parse->streamheader_received);

  caps = gst_caps_make_writable (gst_pad_query_caps (parse->srcpad, NULL));
  theora_parse_set_header_on_caps (parse, caps);
  GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
  gst_pad_set_caps (parse->srcpad, caps);
  gst_caps_unref (caps);

  for (i = 0; i < 3; i++) {
    ogg_packet packet;
    GstBuffer *buf;
    int ret;
    GstMapInfo map;

    buf = parse->streamheader[i];
    if (buf == NULL)
      continue;

    gst_buffer_map (buf, &map, GST_MAP_READ);
    packet.packet = map.data;
    packet.bytes = map.size;
    packet.granulepos = GST_BUFFER_OFFSET_END (buf);
    packet.packetno = i + 1;
    packet.e_o_s = 0;
    packet.b_o_s = (i == 0);
    ret = th_decode_headerin (&parse->info, &parse->comment, &setup, &packet);
    gst_buffer_unmap (buf, &map);
    if (ret < 0) {
      GST_WARNING_OBJECT (parse, "Failed to decode Theora header %d: %d\n",
          i + 1, ret);
    }
  }
  if (setup) {
    th_setup_free (setup);
  }

  parse->fps_n = parse->info.fps_numerator;
  parse->fps_d = parse->info.fps_denominator;
  parse->shift = parse->info.keyframe_granule_shift;

  /* With libtheora-1.0beta1 the granulepos scheme was changed:
   * where earlier the granulepos referred to the index/beginning
   * of a frame, it now refers to the end, which matches the use
   * in vorbis/speex. We check the bitstream version from the header so
   * we know which way to interpret the incoming granuepos
   */
  bitstream_version = (parse->info.version_major << 16) |
      (parse->info.version_minor << 8) | parse->info.version_subminor;
  parse->is_old_bitstream = (bitstream_version <= 0x00030200);

  parse->streamheader_received = TRUE;
}
Exemplo n.º 3
0
PRBool
nsTheoraState::DecodeHeader(ogg_packet* aPacket)
{
  mPacketCount++;
  int ret = th_decode_headerin(&mInfo,
                               &mComment,
                               &mSetup,
                               aPacket);
 
  // We must determine when we've read the last header packet.
  // th_decode_headerin() does not tell us when it's read the last header, so
  // we must keep track of the headers externally.
  //
  // There are 3 header packets, the Identification, Comment, and Setup
  // headers, which must be in that order. If they're out of order, the file
  // is invalid. If we've successfully read a header, and it's the setup
  // header, then we're done reading headers. The first byte of each packet
  // determines it's type as follows:
  //    0x80 -> Identification header
  //    0x81 -> Comment header
  //    0x82 -> Setup header
  // See http://www.theora.org/doc/Theora.pdf Chapter 6, "Bitstream Headers",
  // for more details of the Ogg/Theora containment scheme.
  PRBool isSetupHeader = aPacket->bytes > 0 && aPacket->packet[0] == 0x82;
  if (ret < 0 || mPacketCount > 3) {
    // We've received an error, or the first three packets weren't valid
    // header packets, assume bad input, and don't activate the bitstream.
    mDoneReadingHeaders = PR_TRUE;
  } else if (ret > 0 && isSetupHeader && mPacketCount == 3) {
    // Successfully read the three header packets.
    mDoneReadingHeaders = PR_TRUE;
    mActive = PR_TRUE;
  }
  return mDoneReadingHeaders;
}
Exemplo n.º 4
0
int theora_decode_header(theora_info *_ci,theora_comment *_cc,ogg_packet *_op){
  th_api_wrapper *api;
  th_info         info;
  int             ret;
  api=(th_api_wrapper *)_ci->codec_setup;
  /*Allocate an API wrapper struct on demand, since it will not also include a
     theora_info struct like the ones that are used in a theora_state struct.*/
  if(api==NULL){
    _ci->codec_setup=_ogg_calloc(1,sizeof(*api));
    if(_ci->codec_setup==NULL)return OC_FAULT;
    api=(th_api_wrapper *)_ci->codec_setup;
    api->clear=(oc_setup_clear_func)th_dec_api_clear;
  }
  /*Convert from the theora_info struct instead of saving our own th_info
     struct between calls.
    The user might be doing something "clever" with the header packets if they
     are not using an Ogg encapsulation, and we don't want to break this.*/
  oc_theora_info2th_info(&info,_ci);
  /*We rely on the fact that theora_comment and th_comment structures are
     actually identical.
    Take care not to change this fact unless you change the code here as
     well!*/
  ret=th_decode_headerin(&info,(th_comment *)_cc,&api->setup,_op);
  /*We also rely on the fact that the error return code values are the same,
    and that the implementations of these two functions return the same set of
    them.
   Note that theora_decode_header() really can return OC_NOTFORMAT, even
    though it is not currently documented to do so.*/
  if(ret<0)return ret;
  th_info2theora_info(_ci,&info);
  return 0;
}
Exemplo n.º 5
0
krad_theora_decoder_t *krad_theora_decoder_create(unsigned char *header1, int header1len, unsigned char *header2, int header2len, unsigned char *header3, int header3len) {

	krad_theora_decoder_t *krad_theora;
	
	krad_theora = calloc(1, sizeof(krad_theora_decoder_t));

	krad_theora->granulepos = -1;

	th_comment_init(&krad_theora->comment);
	th_info_init(&krad_theora->info);

	krad_theora->packet.packet = header1;
	krad_theora->packet.bytes = header1len;
	krad_theora->packet.b_o_s = 1;
	krad_theora->packet.packetno = 1;
	th_decode_headerin(&krad_theora->info, &krad_theora->comment, &krad_theora->setup_info, &krad_theora->packet);
	//printf("x is %d len is %d\n", x, header1len);

	krad_theora->packet.packet = header2;
	krad_theora->packet.bytes = header2len;
	krad_theora->packet.b_o_s = 0;
	krad_theora->packet.packetno = 2;
	th_decode_headerin(&krad_theora->info, &krad_theora->comment, &krad_theora->setup_info, &krad_theora->packet);
	//printf("x is %d len is %d\n", x, header2len);

	krad_theora->packet.packet = header3;
	krad_theora->packet.bytes = header3len;
	krad_theora->packet.packetno = 3;
	th_decode_headerin(&krad_theora->info, &krad_theora->comment, &krad_theora->setup_info, &krad_theora->packet);

	printf("Theora %dx%d %.02f fps video\n Encoded frame content is %dx%d with %dx%d offset\n",
		   krad_theora->info.frame_width, krad_theora->info.frame_height, 
		   (double)krad_theora->info.fps_numerator/krad_theora->info.fps_denominator,
		   krad_theora->info.pic_width, krad_theora->info.pic_height, krad_theora->info.pic_x, krad_theora->info.pic_y);


	krad_theora->decoder = th_decode_alloc(&krad_theora->info, krad_theora->setup_info);

	th_setup_free(krad_theora->setup_info);

	return krad_theora;

}
Exemplo n.º 6
0
bool TheoraDecoder::decodeHeader(StampedOggPacket* inHeaderPacket) 
{		
    //inHeaderPacket is accepted and deleted.
	//TODO::: Error handling

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

    th_decode_headerin(&mTheoraInfo, &mTheoraComment, &mTheoraSetup, locOldPack);

	delete locOldPack->packet;
	delete locOldPack;

    mPacketCount++;
	return true;
}
Exemplo n.º 7
0
IoObject *IoTheoraDecodeContext_headerin(IoTheoraDecodeContext *self, IoObject *locals, IoMessage *m)
{
	/*doc TheoraDecodecontext headerin(info, comment, setup, packet)
	Try to decode a theora header from the packet.
	*/
        IoTheoraInfo *info = IoMessage_locals_theoraInfoArgAt_(m, locals, 0);
        IoTheoraComment *comment = IoMessage_locals_theoraCommentArgAt_(m, locals, 1);
        IoTheoraSetupInfo *setup = IoMessage_locals_theoraSetupInfoArgAt_(m, locals, 2);
        IoOggPacket *packet = IoMessage_locals_oggPacketArgAt_(m, locals, 3);
	int ret = th_decode_headerin(((th_info*)(IoObject_dataPointer(info))),
				     ((th_comment*)(IoObject_dataPointer(comment))),
				     ((th_setup_info**)(IoObject_dataPointer(setup))),
				     ((ogg_packet*)(IoObject_dataPointer(packet))));

	return IONUMBER(ret);
}
Exemplo n.º 8
0
static GstFlowReturn
theora_handle_header_packet (GstTheoraDec * dec, ogg_packet * packet)
{
  GstFlowReturn res;
  int ret;

  GST_DEBUG_OBJECT (dec, "parsing header packet");

  ret = th_decode_headerin (&dec->info, &dec->comment, &dec->setup, packet);
  if (ret < 0)
    goto header_read_error;

  switch (packet->packet[0]) {
    case 0x81:
      res = theora_handle_comment_packet (dec, packet);
      break;
    case 0x82:
      res = theora_handle_type_packet (dec);
      break;
    default:
      /* ignore */
      g_warning ("unknown theora header packet found");
    case 0x80:
      /* nothing special, this is the identification header */
      res = GST_FLOW_OK;
      break;
  }
  return res;

  /* ERRORS */
header_read_error:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
        (NULL), ("couldn't read header packet"));
    return GST_FLOW_ERROR;
  }
}
Exemplo n.º 9
0
Arquivo: theora.c Projeto: repstd/vlc
/*****************************************************************************
 * ProcessHeaders: process Theora headers.
 *****************************************************************************/
static int ProcessHeaders( decoder_t *p_dec )
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    ogg_packet oggpacket;
    th_setup_info *ts = NULL; /* theora setup information */
    int i_max_pp, i_pp;

    unsigned pi_size[XIPH_MAX_HEADER_COUNT];
    void     *pp_data[XIPH_MAX_HEADER_COUNT];
    unsigned i_count;
    if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
                           p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
        return VLC_EGENERIC;
    if( i_count < 3 )
        goto error;

    oggpacket.granulepos = -1;
    oggpacket.e_o_s = 0;
    oggpacket.packetno = 0;

    /* Take care of the initial Vorbis header */
    oggpacket.b_o_s  = 1; /* yes this actually is a b_o_s packet :) */
    oggpacket.bytes  = pi_size[0];
    oggpacket.packet = pp_data[0];
    if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 )
    {
        msg_Err( p_dec, "this bitstream does not contain Theora video data" );
        goto error;
    }

    /* Set output properties */
    if( !p_sys->b_packetizer )

    switch( p_sys->ti.pixel_fmt )
    {
      case TH_PF_420:
        p_dec->fmt_out.i_codec = VLC_CODEC_I420;
        break;
      case TH_PF_422:
        p_dec->fmt_out.i_codec = VLC_CODEC_I422;
        break;
      case TH_PF_444:
        p_dec->fmt_out.i_codec = VLC_CODEC_I444;
        break;
      case TH_PF_RSVD:
      default:
        msg_Err( p_dec, "unknown chroma in theora sample" );
        break;
    }

    p_dec->fmt_out.video.i_width = p_sys->ti.frame_width;
    p_dec->fmt_out.video.i_height = p_sys->ti.frame_height;
    if( p_sys->ti.pic_width && p_sys->ti.pic_height )
    {
        p_dec->fmt_out.video.i_visible_width = p_sys->ti.pic_width;
        p_dec->fmt_out.video.i_visible_height = p_sys->ti.pic_height;

        if( p_sys->ti.pic_x || p_sys->ti.pic_y )
        {
            p_dec->fmt_out.video.i_x_offset = p_sys->ti.pic_x;
            p_dec->fmt_out.video.i_y_offset = p_sys->ti.pic_y;
        }
    }

    if( p_sys->ti.aspect_denominator && p_sys->ti.aspect_numerator )
    {
        p_dec->fmt_out.video.i_sar_num = p_sys->ti.aspect_numerator;
        p_dec->fmt_out.video.i_sar_den = p_sys->ti.aspect_denominator;
    }
    else
    {
        p_dec->fmt_out.video.i_sar_num = 1;
        p_dec->fmt_out.video.i_sar_den = 1;
    }

    if( p_sys->ti.fps_numerator > 0 && p_sys->ti.fps_denominator > 0 )
    {
        p_dec->fmt_out.video.i_frame_rate = p_sys->ti.fps_numerator;
        p_dec->fmt_out.video.i_frame_rate_base = p_sys->ti.fps_denominator;
    }

    msg_Dbg( p_dec, "%dx%d %.02f fps video, frame content "
             "is %dx%d with offset (%d,%d)",
             p_sys->ti.frame_width, p_sys->ti.frame_height,
             (double)p_sys->ti.fps_numerator/p_sys->ti.fps_denominator,
             p_sys->ti.pic_width, p_sys->ti.pic_height,
             p_sys->ti.pic_x, p_sys->ti.pic_y );

    /* Some assertions based on the documentation.  These are mandatory restrictions. */
    assert( p_sys->ti.frame_height % 16 == 0 && p_sys->ti.frame_height < 1048576 );
    assert( p_sys->ti.frame_width % 16 == 0 && p_sys->ti.frame_width < 1048576 );
    assert( p_sys->ti.keyframe_granule_shift >= 0 && p_sys->ti.keyframe_granule_shift <= 31 );
    assert( p_sys->ti.pic_x <= __MIN( p_sys->ti.frame_width - p_sys->ti.pic_width, 255 ) );
    assert( p_sys->ti.pic_y <= p_sys->ti.frame_height - p_sys->ti.pic_height);
    assert( p_sys->ti.frame_height - p_sys->ti.pic_height - p_sys->ti.pic_y <= 255 );

    /* Sanity check that seems necessary for some corrupted files */
    if( p_sys->ti.frame_width < p_sys->ti.pic_width ||
        p_sys->ti.frame_height < p_sys->ti.pic_height )
    {
        msg_Warn( p_dec, "trying to correct invalid theora header "
                  "(frame size (%dx%d) is smaller than frame content (%d,%d))",
                  p_sys->ti.frame_width, p_sys->ti.frame_height,
                  p_sys->ti.pic_width, p_sys->ti.pic_height );

        if( p_sys->ti.frame_width < p_sys->ti.pic_width )
          p_sys->ti.frame_width = p_sys->ti.pic_width;
        if( p_sys->ti.frame_height < p_sys->ti.pic_height )
            p_sys->ti.frame_height = p_sys->ti.pic_height;
    }

    /* The next packet in order is the comments header */
    oggpacket.b_o_s  = 0;
    oggpacket.bytes  = pi_size[1];
    oggpacket.packet = pp_data[1];

    if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 )
    {
        msg_Err( p_dec, "2nd Theora header is corrupted" );
        goto error;
    }

    ParseTheoraComments( p_dec );

    /* The next packet in order is the codebooks header
     * We need to watch out that this packet is not missing as a
     * missing or corrupted header is fatal. */
    oggpacket.b_o_s  = 0;
    oggpacket.bytes  = pi_size[2];
    oggpacket.packet = pp_data[2];
    if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 )
    {
        msg_Err( p_dec, "3rd Theora header is corrupted" );
        goto error;
    }

    if( !p_sys->b_packetizer )
    {
        /* We have all the headers, initialize decoder */
        if ( ( p_sys->tcx = th_decode_alloc( &p_sys->ti, ts ) ) == NULL )
        {
            msg_Err( p_dec, "Could not allocate Theora decoder" );
            goto error;
        }

        i_pp = var_InheritInteger( p_dec, DEC_CFG_PREFIX "postproc" );
        if ( i_pp >= 0 && !th_decode_ctl( p_sys->tcx,
                    TH_DECCTL_GET_PPLEVEL_MAX, &i_max_pp, sizeof(int) ) )
        {
            i_pp = __MIN( i_pp, i_max_pp );
            if ( th_decode_ctl( p_sys->tcx, TH_DECCTL_SET_PPLEVEL,
                                &i_pp, sizeof(int) ) )
                msg_Err( p_dec, "Failed to set post processing level to %d",
                         i_pp );
            else
                msg_Dbg( p_dec, "Set post processing level to %d / %d",
                         i_pp, i_max_pp );
        }

    }
    else
    {
        p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra;
        p_dec->fmt_out.p_extra = xrealloc( p_dec->fmt_out.p_extra,
                                                  p_dec->fmt_out.i_extra );
        memcpy( p_dec->fmt_out.p_extra,
                p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
    }

    for( unsigned i = 0; i < i_count; i++ )
        free( pp_data[i] );
    /* Clean up the decoder setup info... we're done with it */
    th_setup_free( ts );
    return VLC_SUCCESS;

error:
    for( unsigned i = 0; i < i_count; i++ )
        free( pp_data[i] );
    /* Clean up the decoder setup info... we're done with it */
    th_setup_free( ts );
    return VLC_EGENERIC;
}
Exemplo n.º 10
0
bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
	close();

	_fileStream = stream;

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

	// init supporting Vorbis structures needed in header parsing
	vorbis_info_init(&_vorbisInfo);
	vorbis_comment vorbisComment;
	vorbis_comment_init(&vorbisComment);

	// init supporting Theora structures needed in header parsing
	th_info theoraInfo;
	th_info_init(&theoraInfo);
	th_comment theoraComment;
	th_comment_init(&theoraComment);
	th_setup_info *theoraSetup = 0;

	uint theoraPackets = 0, vorbisPackets = 0;

	// Ogg file open; parse the headers
	// Only interested in Vorbis/Theora streams
	bool foundHeader = false;
	while (!foundHeader) {
		int ret = bufferData();

		if (ret == 0)
			break; // FIXME: Shouldn't this error out?

		while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
			ogg_stream_state test;

			// is this a mandated initial header? If not, stop parsing
			if (!ogg_page_bos(&_oggPage)) {
				// don't leak the page; get it into the appropriate stream
				queuePage(&_oggPage);
				foundHeader = true;
				break;
			}

			ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
			ogg_stream_pagein(&test, &_oggPage);
			ogg_stream_packetout(&test, &_oggPacket);

			// identify the codec: try theora
			if (theoraPackets == 0 && th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket) >= 0) {
				// it is theora
				memcpy(&_theoraOut, &test, sizeof(test));
				theoraPackets = 1;
				_hasVideo = true;
			} else if (vorbisPackets == 0 && vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket) >= 0) {
				// it is vorbis
				memcpy(&_vorbisOut, &test, sizeof(test));
				vorbisPackets = 1;
				_hasAudio = true;
			} 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 ((theoraPackets && theoraPackets < 3) || (vorbisPackets && vorbisPackets < 3)) {
		int ret;

		// look for further theora headers
		while (theoraPackets && (theoraPackets < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
			if (ret < 0)
				error("Error parsing Theora stream headers; corrupt stream?");

			if (!th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket))
				error("Error parsing Theora stream headers; corrupt stream?");

			theoraPackets++;
		}

		// look for more vorbis header packets
		while (vorbisPackets && (vorbisPackets < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
			if (ret < 0)
				error("Error parsing Vorbis stream headers; corrupt stream?");

			if (vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket))
				error("Error parsing Vorbis stream headers; corrupt stream?");

			vorbisPackets++;

			if (vorbisPackets == 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(&_oggSync, &_oggPage) > 0) {
			queuePage(&_oggPage); // demux into the appropriate stream
		} else {
			ret = bufferData(); // someone needs more data

			if (ret == 0)
				error("End of file while searching for codec headers.");
		}
	}

	// And now we have it all. Initialize decoders next
	if (_hasVideo) {
		_videoTrack = new TheoraVideoTrack(getDefaultHighColorFormat(), theoraInfo, theoraSetup);
		addTrack(_videoTrack);
	}

	th_info_clear(&theoraInfo);
	th_comment_clear(&theoraComment);
	th_setup_free(theoraSetup);

	if (_hasAudio) {
		_audioTrack = new VorbisAudioTrack(_soundType, _vorbisInfo);

		// Get enough audio data to start us off
		while (!_audioTrack->hasAudio()) {
			// Queue more data
			bufferData();
			while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
				queuePage(&_oggPage);

			queueAudio();
		}

		addTrack(_audioTrack);
	}

	vorbis_comment_clear(&vorbisComment);

	return true;
}
Exemplo n.º 11
0
	void VideoClip_Theora::_readTheoraVorbisHeaders()
	{
		ogg_packet tempOggPacket;
		//init Vorbis/Theora Layer
		//Ensure all structures get cleared out.
		memset(&this->info.OggSyncState, 0, sizeof(ogg_sync_state));
		memset(&this->info.OggPage, 0, sizeof(ogg_page));
		memset(&this->info.VorbisStreamState, 0, sizeof(ogg_stream_state));
		memset(&this->info.TheoraStreamState, 0, sizeof(ogg_stream_state));
		memset(&this->info.TheoraInfo, 0, sizeof(th_info));
		memset(&this->info.TheoraComment, 0, sizeof(th_comment));
		memset(&this->info.VorbisInfo, 0, sizeof(vorbis_info));
		memset(&this->info.VorbisDSPState, 0, sizeof(vorbis_dsp_state));
		memset(&this->info.VorbisBlock, 0, sizeof(vorbis_block));
		memset(&this->info.VorbisComment, 0, sizeof(vorbis_comment));
		// init all structures
		ogg_sync_init(&this->info.OggSyncState);
		th_comment_init(&this->info.TheoraComment);
		th_info_init(&this->info.TheoraInfo);
		vorbis_info_init(&this->info.VorbisInfo);
		vorbis_comment_init(&this->info.VorbisComment);
		// start
		ogg_stream_state oggStateTest;
		bool decodeAudio = (theoraplayer::manager->getAudioInterfaceFactory() != NULL);
		char* buffer = NULL;
		int bytesRead = 0;
		bool done = false;
		while (!done)
		{
			buffer = ogg_sync_buffer(&this->info.OggSyncState, BUFFER_SIZE);
			bytesRead = this->stream->read(buffer, BUFFER_SIZE);
			ogg_sync_wrote(&this->info.OggSyncState, bytesRead);
			if (bytesRead == 0)
			{
				break;
			}
			while (ogg_sync_pageout(&this->info.OggSyncState, &this->info.OggPage) > 0)
			{
				memset(&oggStateTest, 0, sizeof(oggStateTest));
				//is this an initial header? If not, stop
				if (ogg_page_bos(&this->info.OggPage) == 0)
				{
					//This is done blindly, because stream only accept themselves
					if (this->theoraStreams > 0)
					{
						ogg_stream_pagein(&this->info.TheoraStreamState, &this->info.OggPage);
					}
					if (this->vorbisStreams > 0)
					{
						ogg_stream_pagein(&this->info.VorbisStreamState, &this->info.OggPage);
					}
					done = true;
					break;
				}
				ogg_stream_init(&oggStateTest, ogg_page_serialno(&this->info.OggPage));
				ogg_stream_pagein(&oggStateTest, &this->info.OggPage);
				ogg_stream_packetout(&oggStateTest, &tempOggPacket);
				// identify the codec
				if (this->theoraStreams == 0 && th_decode_headerin(&this->info.TheoraInfo, &this->info.TheoraComment, &this->info.TheoraSetup, &tempOggPacket) > 0)
				{
					// This is the Theora Header
					memcpy(&this->info.TheoraStreamState, &oggStateTest, sizeof(oggStateTest));
					this->theoraStreams = 1;
				}
				else if (decodeAudio && this->vorbisStreams == 0 && vorbis_synthesis_headerin(&this->info.VorbisInfo, &this->info.VorbisComment, &tempOggPacket) >= 0)
				{
					// This is vorbis header
					memcpy(&this->info.VorbisStreamState, &oggStateTest, sizeof(oggStateTest));
					this->vorbisStreams = 1;
				}
				else // Hm, guess it's not a header we support, so erase it
				{
					ogg_stream_clear(&oggStateTest);
				}
			}
		}
		int result = 0;
		while ((this->theoraStreams > 0 && this->theoraStreams < 3) || (this->vorbisStreams && this->vorbisStreams < 3))
		{
			// Check 2nd'dary headers... Theora First
			while (this->theoraStreams > 0 && this->theoraStreams < 3 && (result = ogg_stream_packetout(&this->info.TheoraStreamState, &tempOggPacket)))
			{
				if (result < 0)
				{
					throw TheoraplayerException("Error parsing Theora stream headers!");
				}
				if (th_decode_headerin(&this->info.TheoraInfo, &this->info.TheoraComment, &this->info.TheoraSetup, &tempOggPacket) == 0)
				{
					throw TheoraplayerException("Invalid theora stream!");
				}
				++this->theoraStreams;
			} // end while looking for more theora headers
			  // look 2nd vorbis header packets
			while (this->vorbisStreams < 3 && (result = ogg_stream_packetout(&this->info.VorbisStreamState, &tempOggPacket)))
			{
				if (result < 0)
				{
					throw TheoraplayerException("Error parsing vorbis stream headers!");
				}
				if (vorbis_synthesis_headerin(&this->info.VorbisInfo, &this->info.VorbisComment, &tempOggPacket) != 0)
				{
					throw TheoraplayerException("Invalid stream!");
				}
				++this->vorbisStreams;
			} // end while looking for more vorbis headers
			  // Not finished with Headers, get some more file data
			if (ogg_sync_pageout(&this->info.OggSyncState, &this->info.OggPage) > 0)
			{
				if (this->theoraStreams > 0)
				{
					ogg_stream_pagein(&this->info.TheoraStreamState, &this->info.OggPage);
				}
				if (this->vorbisStreams > 0)
				{
					ogg_stream_pagein(&this->info.VorbisStreamState, &this->info.OggPage);
				}
			}
			else
			{
				buffer = ogg_sync_buffer(&this->info.OggSyncState, BUFFER_SIZE);
				bytesRead = this->stream->read(buffer, BUFFER_SIZE);
				ogg_sync_wrote(&this->info.OggSyncState, bytesRead);
				if (bytesRead == 0)
				{
					throw TheoraplayerException("End of file found prematurely!");
				}
			}
		} // end while looking for all headers
		  //log("Vorbis Headers: " + str(mVorbisHeaders) + " Theora Headers : " + str(mTheoraHeaders));
	}
Exemplo n.º 12
0
void TheoraPlayer::OpenFile(const String &path)
{
    ReleaseData();
    
    if(path == "")
        return;
    
    filePath = path;
    
    file = File::Create(path, File::OPEN | File::READ);
    ogg_sync_init(&theoraData->syncState);
    th_info_init(&theoraData->thInfo);
    th_comment_init(&theoraData->thComment);
    
    int32 stateflag = 0;
    while(!stateflag)
    {
        if(!BufferData())
            break;
        
        while(ogg_sync_pageout(&theoraData->syncState, &theoraData->page) > 0)
        {
            ogg_stream_state test;
            
            /* is this a mandated initial header? If not, stop parsing */
            if(!ogg_page_bos(&theoraData->page))
            {
                /* don't leak the page; get it into the appropriate stream */
                ogg_stream_pagein(&theoraData->state, &theoraData->page);
                stateflag = 1;
                break;
            }
            
            ogg_stream_init(&test, ogg_page_serialno(&theoraData->page));
            ogg_stream_pagein(&test, &theoraData->page);
            ogg_stream_packetout(&test, &theoraData->packet);
            
            /* identify the codec: try theora */
            if(!theora_p && th_decode_headerin(&theoraData->thInfo, &theoraData->thComment, &theoraData->thSetup, &theoraData->packet) >= 0)
            {
                /* it is theora */
                memcpy(&theoraData->state, &test, sizeof(test));
                theora_p = 1;
            }
            else
            {
                /* whatever it is, we don't care about it */
                ogg_stream_clear(&test);
            }
        }
        /* fall through to non-bos page parsing */
    }
    
    while(theora_p && theora_p < 3)
    {
        int ret;
        
        /* look for further theora headers */
        while(theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&theoraData->state, &theoraData->packet)))
        {
            if(ret < 0)
            {
                Logger::Error("TheoraPlayer: Error parsing Theora stream headers; corrupt stream?\n");
                return;
            }
            if(!th_decode_headerin(&theoraData->thInfo, &theoraData->thComment, &theoraData->thSetup, &theoraData->packet))
            {
                Logger::Error("TheoraPlayer: Error parsing Theora stream headers; corrupt stream?\n");
                return;
            }
            theora_p++;
        }
        
        /* The header pages/packets will arrive before anything else we
         care about, or the stream is not obeying spec */
        
        if(ogg_sync_pageout(&theoraData->syncState, &theoraData->page) > 0)
        {
            ogg_stream_pagein(&theoraData->state, &theoraData->page); /* demux into the appropriate stream */
        }
        else
        {
            int ret = BufferData(); /* someone needs more data */
            if(ret == 0)
            {
                Logger::Error("TheoraPlayer: End of file while searching for codec headers.\n");
                return;
            }
        }
    }
    if(theora_p)
    {
        theoraData->thCtx = th_decode_alloc(&theoraData->thInfo, theoraData->thSetup);
        
        th_decode_ctl(theoraData->thCtx, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max, sizeof(pp_level_max));
        pp_level=pp_level_max;
        th_decode_ctl(theoraData->thCtx, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
        pp_inc=0;
    }
    else
    {
        /* tear down the partial theora setup */
        th_info_clear(&theoraData->thInfo);
        th_comment_clear(&theoraData->thComment);
    }
    
    if(theoraData->thSetup)
        th_setup_free(theoraData->thSetup);
    theoraData->thSetup = 0;

    frameBufferW = binCeil(theoraData->thInfo.pic_width);
    frameBufferH = binCeil(theoraData->thInfo.pic_height);
    
    frameBuffer = new unsigned char[frameBufferW * frameBufferH * 4];
    
    repeatFilePos = file->GetPos();
    
    frameTime = (float32)(theoraData->thInfo.fps_denominator)/(float32)(theoraData->thInfo.fps_numerator);
    
    isPlaying = true;
}
Exemplo n.º 13
0
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);

}
Exemplo n.º 14
0
bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
	close();

	_endOfAudio = false;
	_endOfVideo = false;
	_fileStream = stream;

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

	// init supporting Vorbis structures needed in header parsing
	vorbis_info_init(&_vorbisInfo);
	vorbis_comment_init(&_vorbisComment);

	// init supporting Theora structures needed in header parsing
	th_comment_init(&_theoraComment);
	th_info_init(&_theoraInfo);

	// Ogg file open; parse the headers
	// Only interested in Vorbis/Theora streams
	bool foundHeader = false;
	while (!foundHeader) {
		int ret = bufferData();

		if (ret == 0)
			break;

		while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
			ogg_stream_state test;

			// is this a mandated initial header? If not, stop parsing
			if (!ogg_page_bos(&_oggPage)) {
				// don't leak the page; get it into the appropriate stream
				queuePage(&_oggPage);
				foundHeader = true;
				break;
			}

			ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
			ogg_stream_pagein(&test, &_oggPage);
			ogg_stream_packetout(&test, &_oggPacket);

			// identify the codec: try theora
			if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) {
				// it is theora
				memcpy(&_theoraOut, &test, sizeof(test));
				_theoraPacket = 1;
			} else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) {
				// it is vorbis
				memcpy(&_vorbisOut, &test, sizeof(test));
				_vorbisPacket = 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 ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) {
		int ret;

		// look for further theora headers
		while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
			if (ret < 0)
				error("Error parsing Theora stream headers; corrupt stream?");

			if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket))
				error("Error parsing Theora stream headers; corrupt stream?");

			_theoraPacket++;
		}

		// look for more vorbis header packets
		while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
			if (ret < 0)
				error("Error parsing Vorbis stream headers; corrupt stream?");

			if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket))
				error("Error parsing Vorbis stream headers; corrupt stream?");

			_vorbisPacket++;

			if (_vorbisPacket == 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(&_oggSync, &_oggPage) > 0) {
			queuePage(&_oggPage); // demux into the appropriate stream
		} else {
			ret = bufferData(); // someone needs more data

			if (ret == 0)
				error("End of file while searching for codec headers.");
		}
	}

	// and now we have it all.  initialize decoders
	if (_theoraPacket) {
		_theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup);
		debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps",
		       _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height,
		       (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator);

		switch (_theoraInfo.pixel_fmt) {
		case TH_PF_420:
			debug(1, " 4:2:0 video");
			break;
		case TH_PF_422:
			debug(1, " 4:2:2 video");
			break;
		case TH_PF_444:
			debug(1, " 4:4:4 video");
			break;
		case TH_PF_RSVD:
		default:
			debug(1, " video\n  (UNKNOWN Chroma sampling!)");
			break;
		}

		if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height)
			debug(1, "  Frame content is %dx%d with offset (%d,%d).",
			      _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y);

		switch (_theoraInfo.colorspace){
		case TH_CS_UNSPECIFIED:
			/* nothing to report */
			break;
		case TH_CS_ITU_REC_470M:
			debug(1, "  encoder specified ITU Rec 470M (NTSC) color.");
			break;
		case TH_CS_ITU_REC_470BG:
			debug(1, "  encoder specified ITU Rec 470BG (PAL) color.");
			break;
		default:
			debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace);
			break;
		}

		debug(1, "Encoded by %s", _theoraComment.vendor);
		if (_theoraComment.comments) {
			debug(1, "theora comment header:");
			for (int i = 0; i < _theoraComment.comments; i++) {
				if (_theoraComment.user_comments[i]) {
					int len = _theoraComment.comment_lengths[i];
					char *value = (char *)malloc(len + 1);
					if (value) {
						memcpy(value, _theoraComment.user_comments[i], len);
						value[len] = '\0';
						debug(1, "\t%s", value);
						free(value);
					}
				}
			}
		}

		th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax));
		_ppLevel = _ppLevelMax;
		th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
		_ppInc = 0;
	} else {
		// tear down the partial theora setup
		th_info_clear(&_theoraInfo);
		th_comment_clear(&_theoraComment);
	}

	th_setup_free(_theoraSetup);
	_theoraSetup = 0;

	if (_vorbisPacket) {
		vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo);
		vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
		debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.",
		      _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate);

		_audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels);

		// Get enough audio data to start us off
		while (_audStream->numQueuedStreams() == 0) {
			// Queue more data
			bufferData();
			while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
				queuePage(&_oggPage);

			queueAudio();
		}

		if (_audStream)
			g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream, -1, getVolume(), getBalance());
	} else {
		// tear down the partial vorbis setup
		vorbis_info_clear(&_vorbisInfo);
		vorbis_comment_clear(&_vorbisComment);
		_endOfAudio = true;
	}

	_surface.create(_theoraInfo.frame_width, _theoraInfo.frame_height, g_system->getScreenFormat());

	// Set up a display surface
	_displaySurface.pixels = _surface.getBasePtr(_theoraInfo.pic_x, _theoraInfo.pic_y);
	_displaySurface.w = _theoraInfo.pic_width;
	_displaySurface.h = _theoraInfo.pic_height;
	_displaySurface.format = _surface.format;
	_displaySurface.pitch = _surface.pitch;

	// Set the frame rate
	_frameRate = Common::Rational(_theoraInfo.fps_numerator, _theoraInfo.fps_denominator);

	return true;
}
Exemplo n.º 15
0
OSErr init_theora_decoder(Theora_Globals glob, CodecDecompressParams *p)
{
    OSErr err = noErr;
    Handle ext;
    //OggSerialNoAtom *atom;
    Byte *ptrheader, *mCookie, *cend;
    UInt32 mCookieSize;
    CookieAtomHeader *aheader;
    th_comment tc;
    ogg_packet header, header_tc, header_cb;

    if (glob->info_initialised) {
        dbg_printf("--:Theora:- Decoder already initialised, skipping...\n");
        return err;
    }

    err = GetImageDescriptionExtension(p->imageDescription, &ext, kSampleDescriptionExtensionTheora, 1);
    if (err != noErr) {
        dbg_printf("XXX GetImageDescriptionExtension() failed! ('%4.4s')\n", &(*p->imageDescription)->cType);
        err = codecBadDataErr;
        return err;
    }

    mCookie = (UInt8 *) *ext;
    mCookieSize = GetHandleSize(ext);

    ptrheader = mCookie;
    cend = mCookie + mCookieSize;

    aheader = (CookieAtomHeader*)ptrheader;


    header.bytes = header_tc.bytes = header_cb.bytes = 0;

    while (ptrheader < cend) {
        aheader = (CookieAtomHeader *) ptrheader;
        ptrheader += EndianU32_BtoN(aheader->size);
        if (ptrheader > cend || EndianU32_BtoN(aheader->size) <= 0)
            break;

        switch(EndianS32_BtoN(aheader->type)) {
        case kCookieTypeTheoraHeader:
            header.b_o_s = 1;
            header.e_o_s = 0;
            header.granulepos = 0;
            header.packetno = 0;
            header.bytes = EndianS32_BtoN(aheader->size) - 2 * sizeof(long);
            header.packet = aheader->data;
            break;

        case kCookieTypeTheoraComments:
            header_tc.b_o_s = 0;
            header_tc.e_o_s = 0;
            header_tc.granulepos = 0;
            header_tc.packetno = 1;
            header_tc.bytes = EndianS32_BtoN(aheader->size) - 2 * sizeof(long);
            header_tc.packet = aheader->data;
            break;

        case kCookieTypeTheoraCodebooks:
            header_cb.b_o_s = 0;
            header_cb.e_o_s = 0;
            header_cb.granulepos = 0;
            header_cb.packetno = 2;
            header_cb.bytes = EndianS32_BtoN(aheader->size) - 2 * sizeof(long);
            header_cb.packet = aheader->data;
            break;

        default:
            break;
        }
    }

    err = codecBadDataErr;

    if (header.bytes == 0 || header_tc.bytes == 0 || header_cb.bytes == 0)
        return err;

    th_info_init(&glob->ti);
    th_comment_init(&tc);
    glob->ts = NULL;

    if (th_decode_headerin(&glob->ti, &tc, &glob->ts, &header) < 0) {

        if (glob->ts != NULL)
            th_setup_free (glob->ts);
        th_comment_clear(&tc);
        th_info_clear(&glob->ti);

        return err;
    }

    th_decode_headerin(&glob->ti, &tc, &glob->ts, &header_tc);
    th_decode_headerin(&glob->ti, &tc, &glob->ts, &header_cb);

    err = noErr;

    th_comment_clear(&tc);

    dbg_printf("--:Theora:- OK, managed to initialize the decoder somehow...\n");
    glob->info_initialised = true;

    return err;
}
Exemplo n.º 16
0
void VideoStreamPlaybackTheora::set_file(const String &p_file) {

	ERR_FAIL_COND(playing);
	ogg_packet op;
	th_setup_info *ts = NULL;

	file_name = p_file;
	if (file) {
		memdelete(file);
	}
	file = FileAccess::open(p_file, FileAccess::READ);
	ERR_FAIL_COND(!file);

#ifdef THEORA_USE_THREAD_STREAMING
	thread_exit = false;
	thread_eof = false;
	//pre-fill buffer
	int to_read = ring_buffer.space_left();
	int read = file->get_buffer(read_buffer.ptr(), to_read);
	ring_buffer.write(read_buffer.ptr(), read);

	thread = Thread::create(_streaming_thread, this);

#endif

	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);

	theora_eos = false;
	vorbis_eos = false;

	/* Ogg file open; parse the headers */
	/* Only interested in Vorbis/Theora streams */
	int stateflag = 0;

	int audio_track_skip = audio_track;

	while (!stateflag) {
		int ret = buffer_data();
		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 */
				copymem(&to, &test, sizeof(test));
				theora_p = 1;
			} else if (!vorbis_p && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) {

				/* it is vorbis */
				if (audio_track_skip) {
					vorbis_info_clear(&vi);
					vorbis_comment_clear(&vc);
					ogg_stream_clear(&test);
					vorbis_info_init(&vi);
					vorbis_comment_init(&vc);

					audio_track_skip--;
				} else {
					copymem(&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");
				clear();
				return;
			}
			if (!th_decode_headerin(&ti, &tc, &ts, &op)) {
				fprintf(stderr, "Error parsing Theora stream headers; "
								"corrupt stream?\n");
				clear();
				return;
			}
			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");
				clear();
				return;
			}
			ret = vorbis_synthesis_headerin(&vi, &vc, &op);
			if (ret) {
				fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n");
				clear();
				return;
			}
			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(); /* someone needs more data */
			if (ret == 0) {
				fprintf(stderr, "End of file while searching for codec headers.\n");
				clear();
				return;
			}
		}
	}

	/* 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);
		th_decode_ctl(td, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max,
				sizeof(pp_level_max));
		pp_level = 0;
		th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
		pp_inc = 0;

		int w;
		int h;
		w = (ti.pic_x + ti.frame_width + 1 & ~1) - (ti.pic_x & ~1);
		h = (ti.pic_y + ti.frame_height + 1 & ~1) - (ti.pic_y & ~1);
		size.x = w;
		size.y = h;

		texture->create(w, h, Image::FORMAT_RGBA8, Texture::FLAG_FILTER | Texture::FLAG_VIDEO_SURFACE);

	} 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);
		//_setup(vi.channels, vi.rate);

	} else {
		/* tear down the partial vorbis setup */
		vorbis_info_clear(&vi);
		vorbis_comment_clear(&vc);
	}

	playing = false;
	buffering = true;
	time = 0;
	audio_frames_wrote = 0;
};
Exemplo n.º 17
0
/*
 * init driver
 */
static int init(sh_video_t *sh){
    theora_struct_t *context = NULL;
    uint8_t *extradata = (uint8_t *)(sh->bih + 1);
    int extradata_size = sh->bih->biSize - sizeof(*sh->bih);
    int errorCode = 0;
    ogg_packet op;
    int i;

    context = calloc (sizeof (theora_struct_t), 1);
    sh->context = context;
    if (!context)
        goto err_out;

    th_info_init(&context->ti);
    th_comment_init(&context->tc);
    context->tsi = NULL;
	

    /* Read all header packets, pass them to theora_decode_header. */
    for (i = 0; i < THEORA_NUM_HEADER_PACKETS; i++)
    {
        if (extradata_size > 2) {
            op.bytes  = AV_RB16(extradata);
            op.packet = extradata + 2;
            op.b_o_s  = 1;
            if (extradata_size < op.bytes + 2) {
                mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Theora header too small\n");
                goto err_out;
            }
            extradata      += op.bytes + 2;
            extradata_size -= op.bytes + 2;
        } else {
            op.bytes = ds_get_packet (sh->ds, &op.packet);
            op.b_o_s = 1;
        }

        if ( (errorCode = th_decode_headerin (&context->ti, &context->tc, &context->tsi, &op)) < 0)
        {
            mp_msg(MSGT_DECVIDEO, MSGL_ERR, "Broken Theora header; errorCode=%i!\n", errorCode);
            goto err_out;
        }
    }

    /* now init codec */
    context->tctx = th_decode_alloc (&context->ti, &context->tsi);
    if (!context->tctx)
    {
        mp_msg(MSGT_DECVIDEO,MSGL_ERR,"Theora decode init failed\n");
        goto err_out;
    }
    /* free memory used for decoder setup information */
    th_setup_free(context->tsi);		

    if(sh->aspect==0.0 && context->ti.aspect_denominator!=0)
    {
       sh->aspect = ((double)context->ti.aspect_numerator * context->ti.frame_width)/
          ((double)context->ti.aspect_denominator * context->ti.frame_height);
    }

    mp_msg(MSGT_DECVIDEO,MSGL_V,"INFO: Theora video init ok!\n");
    mp_msg(MSGT_DECVIDEO,MSGL_INFO,"Frame: %dx%d, Picture %dx%d, Offset [%d,%d]\n", context->ti.frame_width, context->ti.frame_height, context->ti.pic_width, context->ti.pic_height, context->ti.pic_x, context->ti.pic_y);

    return mpcodecs_config_vo (sh,context->ti.frame_width,context->ti.frame_height,theora_pixelformat2imgfmt(context->ti.pixel_fmt));

err_out:
    free(context);
    sh->context = NULL;
    return 0;
}
Exemplo n.º 18
0
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);

}