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); }
void VideoStreamTheora::clear() { if (file_name == "") return; if(vorbis_p){ ogg_stream_clear(&vo); if (vorbis_p >= 3) { vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); }; vorbis_comment_clear(&vc); vorbis_info_clear(&vi); vorbis_p = 0; } if(theora_p){ ogg_stream_clear(&to); th_decode_free(td); th_comment_clear(&tc); th_info_clear(&ti); theora_p = 0; } ogg_sync_clear(&oy); file_name = ""; theora_p = 0; vorbis_p = 0; videobuf_ready = 0; frames_pending = 0; videobuf_time = 0; playing = false; };
nsTheoraState::~nsTheoraState() { MOZ_COUNT_DTOR(nsTheoraState); th_setup_free(mSetup); th_decode_free(mCtx); th_comment_clear(&mComment); th_info_clear(&mInfo); }
TheoraDecoder::~TheoraDecoder() { th_comment_clear(&mTheoraComment); th_info_clear(&mTheoraInfo); th_decode_free(mTheoraState); th_setup_free(mTheoraSetup); }
void krad_theora_decoder_destroy(krad_theora_decoder_t *krad_theora) { th_decode_free(krad_theora->decoder); th_comment_clear(&krad_theora->comment); th_info_clear(&krad_theora->info); free(krad_theora); }
/* * Theora beginning of stream */ nsresult MediaRecorder::SetupTheoraBOS() { int i; nsresult rv; PRUint32 wr; ogg_uint32_t keyframe; if (ogg_stream_init(&vState->os, rand())) { PR_LOG(log, PR_LOG_NOTICE, ("Failed ogg_stream_init\n")); return NS_ERROR_FAILURE; } th_info_init(&vState->ti); /* Must be multiples of 16 */ vState->ti.frame_width = (params->width + 15) & ~0xF; vState->ti.frame_height = (params->height + 15) & ~0xF; vState->ti.pic_width = params->width; vState->ti.pic_height = params->height; vState->ti.pic_x = (vState->ti.frame_width - params->width) >> 1 & ~1; vState->ti.pic_y = (vState->ti.frame_height - params->height) >> 1 & ~1; vState->ti.fps_numerator = params->fps_n; vState->ti.fps_denominator = params->fps_d; /* Are these the right values? */ keyframe = 64 - 1; for (i = 0; keyframe; i++) keyframe >>= 1; vState->ti.quality = (int)(params->qual * 100); vState->ti.colorspace = TH_CS_ITU_REC_470M; vState->ti.pixel_fmt = TH_PF_420; vState->ti.keyframe_granule_shift = i; vState->th = th_encode_alloc(&vState->ti); th_info_clear(&vState->ti); /* Header init */ th_comment_init(&vState->tc); th_comment_add_tag(&vState->tc, (char *)"ENCODER", (char *)"rainbow"); if (th_encode_flushheader( vState->th, &vState->tc, &vState->op) <= 0) { PR_LOG(log, PR_LOG_NOTICE, ("Internal Theora library error\n")); return NS_ERROR_FAILURE; } th_comment_clear(&vState->tc); ogg_stream_packetin(&vState->os, &vState->op); if (ogg_stream_pageout(&vState->os, &vState->og) != 1) { PR_LOG(log, PR_LOG_NOTICE, ("Internal Ogg library error\n")); return NS_ERROR_FAILURE; } rv = WriteData(vState->og.header, vState->og.header_len, &wr); rv = WriteData(vState->og.body, vState->og.body_len, &wr); return NS_OK; }
static GstStateChangeReturn theora_dec_change_state (GstElement * element, GstStateChange transition) { GstTheoraDec *dec = GST_THEORA_DEC (element); GstStateChangeReturn ret; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: th_info_clear (&dec->info); th_comment_clear (&dec->comment); GST_DEBUG_OBJECT (dec, "Setting have_header to FALSE in READY->PAUSED"); dec->have_header = FALSE; dec->have_par = FALSE; gst_theora_dec_reset (dec); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } ret = parent_class->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: th_info_clear (&dec->info); th_comment_clear (&dec->comment); th_setup_free (dec->setup); dec->setup = NULL; th_decode_free (dec->decoder); dec->decoder = NULL; gst_theora_dec_reset (dec); break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
static int test_comments () { th_comment tc; int n; char * value; INFO ("+ Initializing th_comment"); th_comment_init (&tc); INFO ("+ Adding ARTIST1"); th_comment_add (&tc, "ARTIST=" ARTIST1); INFO ("+ Adding LICENSE by tag"); th_comment_add_tag (&tc, "LICENSE", LICENSE); INFO ("+ Adding ARTIST2 by tag"); th_comment_add_tag (&tc, "ARTIST", ARTIST2); INFO ("+ Querying value of LICENSE"); value = th_comment_query (&tc, "LICENSE", 0); printf("foo %s\n", value); if (strcmp (value, LICENSE)) FAIL ("Incorrect value for LICENSE"); INFO ("+ Querying count of ARTIST comments"); n = th_comment_query_count (&tc, "ARTIST"); if (n != 2) FAIL ("Incorrect count of ARTIST comments"); INFO ("+ Querying value of ARTIST index 0"); value = th_comment_query (&tc, "ARTIST", 0); if (strcmp (value, ARTIST1)) FAIL ("Incorrect value for ARTIST index 0"); INFO ("+ Querying value of ARTIST index 1"); value = th_comment_query (&tc, "ARTIST", 1); if (strcmp (value, ARTIST2)) FAIL ("Incorrect value for ARTIST index 1"); INFO ("+ Querying value of ARTIST index 2 (out of bounds)"); value = th_comment_query (&tc, "ARTIST", 2); if (value != NULL) FAIL ("Non-NULL value for ARTIST index 2 (out of bounds)"); INFO ("+ Querying value of UNDEF index 7 (tag not defined)"); value = th_comment_query (&tc, "UNDEF", 7); if (value != NULL) FAIL ("Non-NULL value for UNDEF index 7 (tag not defined)"); INFO ("+ Clearing th_comment"); th_comment_clear (&tc); return 0; }
/***************************************************************************** * CloseDecoder: theora decoder destruction *****************************************************************************/ static void CloseDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t *)p_this; decoder_sys_t *p_sys = p_dec->p_sys; th_info_clear(&p_sys->ti); th_comment_clear(&p_sys->tc); th_decode_free(p_sys->tcx); free( p_sys ); }
/* * uninit driver */ static void uninit(sh_video_t *sh) { theora_struct_t *context = sh->context; if (context) { th_info_clear(&context->ti); th_comment_clear(&context->tc); th_decode_free(context->tctx); free(context); } }
static int noop_test_comments () { th_comment tc; INFO ("+ Initializing th_comment struct"); th_comment_init (&tc); INFO ("+ Clearing empty th_comment struct") th_comment_clear (&tc); return 0; }
static gboolean theora_dec_start (GstVideoDecoder * decoder) { GstTheoraDec *dec = GST_THEORA_DEC (decoder); GST_DEBUG_OBJECT (dec, "start"); th_info_clear (&dec->info); th_comment_clear (&dec->comment); GST_DEBUG_OBJECT (dec, "Setting have_header to FALSE"); dec->have_header = FALSE; gst_theora_dec_reset (dec); return TRUE; }
void VideoStreamPlaybackTheora::clear() { if (!file) return; if (vorbis_p) { ogg_stream_clear(&vo); if (vorbis_p >= 3) { vorbis_block_clear(&vb); vorbis_dsp_clear(&vd); }; vorbis_comment_clear(&vc); vorbis_info_clear(&vi); vorbis_p = 0; } if (theora_p) { ogg_stream_clear(&to); th_decode_free(td); th_comment_clear(&tc); th_info_clear(&ti); theora_p = 0; } ogg_sync_clear(&oy); #ifdef THEORA_USE_THREAD_STREAMING thread_exit = true; thread_sem->post(); //just in case Thread::wait_to_finish(thread); memdelete(thread); thread = NULL; ring_buffer.clear(); #endif //file_name = ""; theora_p = 0; vorbis_p = 0; videobuf_ready = 0; frames_pending = 0; videobuf_time = 0; theora_eos = false; vorbis_eos = false; if (file) { memdelete(file); } file = NULL; playing = false; };
void theoraenc_delete (TheoraEnc *enc) { if (!enc) return; if (enc->info) { th_info_clear (enc->info); free (enc->info); } if (enc->comment) { th_comment_clear (enc->comment); free (enc->comment); } if (enc->ctx) th_encode_free (enc->ctx); if (enc->postconv_buffer) free (enc->postconv_buffer); free (enc); }
static void theora_enc_finalize (GObject * object) { GstTheoraEnc *enc = GST_THEORA_ENC (object); GST_DEBUG_OBJECT (enc, "Finalizing"); if (enc->encoder) th_encode_free (enc->encoder); th_comment_clear (&enc->comment); th_info_clear (&enc->info); g_free (enc->multipass_cache_file); theora_enc_clear_multipass_cache (enc); G_OBJECT_CLASS (parent_class)->finalize (object); }
static GstStateChangeReturn theora_parse_change_state (GstElement * element, GstStateChange transition) { GstTheoraParse *parse = GST_THEORA_PARSE (element); GstStateChangeReturn ret; gint i; switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: th_info_init (&parse->info); th_comment_init (&parse->comment); parse->send_streamheader = TRUE; parse->buffer_queue = g_queue_new (); parse->event_queue = g_queue_new (); parse->prev_keyframe = -1; parse->prev_frame = -1; parse->granule_offset = 0; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: th_info_clear (&parse->info); th_comment_clear (&parse->comment); theora_parse_clear_queue (parse); g_queue_free (parse->buffer_queue); g_queue_free (parse->event_queue); parse->buffer_queue = NULL; for (i = 0; i < 3; i++) { if (parse->streamheader[i]) { gst_buffer_unref (parse->streamheader[i]); parse->streamheader[i] = NULL; } } parse->streamheader_received = FALSE; break; default: break; } return ret; }
static gboolean theora_enc_stop (GstVideoEncoder * benc) { GstTheoraEnc *enc; GST_DEBUG_OBJECT (benc, "stop: clearing theora state"); enc = GST_THEORA_ENC (benc); if (enc->encoder) { th_encode_free (enc->encoder); enc->encoder = NULL; } th_comment_clear (&enc->comment); th_info_clear (&enc->info); enc->initialised = FALSE; return TRUE; }
static int close_theora(void * data) { int ret = 1; theora_t * theora; theora = data; #ifdef THEORA_1_1 if(theora->stats_file) fclose(theora->stats_file); if(theora->stats_buf) free(theora->stats_buf); #endif th_comment_clear(&theora->tc); th_info_clear(&theora->ti); th_encode_free(theora->ts); free(theora); return ret; }
VideoClip_Theora::~VideoClip_Theora() { if (this->info.TheoraDecoder != NULL) { th_decode_free(this->info.TheoraDecoder); th_setup_free(this->info.TheoraSetup); if (this->audioInterface != NULL) { vorbis_dsp_clear(&this->info.VorbisDSPState); vorbis_block_clear(&this->info.VorbisBlock); } ogg_stream_clear(&this->info.TheoraStreamState); th_comment_clear(&this->info.TheoraComment); th_info_clear(&this->info.TheoraInfo); ogg_stream_clear(&this->info.VorbisStreamState); vorbis_comment_clear(&this->info.VorbisComment); vorbis_info_clear(&this->info.VorbisInfo); ogg_sync_clear(&this->info.OggSyncState); } }
void krad_theora_encoder_destroy (krad_theora_encoder_t *krad_theora) { while (krad_theora->header_count--) { //printf("krad_theora_encoder_destroy freeing header %d\n", // krad_theora->header_count); free (krad_theora->header[krad_theora->header_count]); } th_info_clear (&krad_theora->info); th_comment_clear (&krad_theora->comment); th_encode_free (krad_theora->encoder); free (krad_theora->header_combined); free (krad_theora->ycbcr[0].data); free (krad_theora->ycbcr[1].data); free (krad_theora->ycbcr[2].data); free (krad_theora); }
static gboolean theora_enc_stop (GstVideoEncoder * benc) { GstTheoraEnc *enc; GST_DEBUG_OBJECT (benc, "stop: clearing theora state"); enc = GST_THEORA_ENC (benc); if (enc->encoder) th_encode_free (enc->encoder); enc->encoder = NULL; th_comment_clear (&enc->comment); th_info_clear (&enc->info); if (enc->input_state) gst_video_codec_state_unref (enc->input_state); enc->input_state = NULL; /* Everything else is handled in reset() */ theora_enc_clear_multipass_cache (enc); return TRUE; }
static gboolean theora_dec_stop (GstVideoDecoder * decoder) { GstTheoraDec *dec = GST_THEORA_DEC (decoder); GST_DEBUG_OBJECT (dec, "stop"); th_info_clear (&dec->info); th_comment_clear (&dec->comment); th_setup_free (dec->setup); dec->setup = NULL; th_decode_free (dec->decoder); dec->decoder = NULL; gst_theora_dec_reset (dec); if (dec->input_state) { gst_video_codec_state_unref (dec->input_state); dec->input_state = NULL; } if (dec->output_state) { gst_video_codec_state_unref (dec->output_state); dec->output_state = NULL; } return TRUE; }
void TheoraDecoder::close() { if (_vorbisPacket) { ogg_stream_clear(&_vorbisOut); vorbis_block_clear(&_vorbisBlock); vorbis_dsp_clear(&_vorbisDSP); vorbis_comment_clear(&_vorbisComment); vorbis_info_clear(&_vorbisInfo); g_system->getMixer()->stopHandle(*_audHandle); _audStream = 0; _vorbisPacket = false; } if (_theoraPacket) { ogg_stream_clear(&_theoraOut); th_decode_free(_theoraDecode); th_comment_clear(&_theoraComment); th_info_clear(&_theoraInfo); _theoraDecode = 0; _theoraPacket = false; } if (!_fileStream) return; ogg_sync_clear(&_oggSync); delete _fileStream; _fileStream = 0; _surface.free(); _displaySurface.pixels = 0; _displaySurface.free(); reset(); }
static GstFlowReturn theora_enc_handle_frame (GstVideoEncoder * benc, GstVideoCodecFrame * frame) { GstTheoraEnc *enc; ogg_packet op; GstClockTime timestamp, running_time; GstFlowReturn ret; gboolean force_keyframe; enc = GST_THEORA_ENC (benc); /* we keep track of two timelines. * - The timestamps from the incomming buffers, which we copy to the outgoing * encoded buffers as-is. We need to do this as we simply forward the * newsegment events. * - The running_time of the buffers, which we use to construct the granulepos * in the packets. */ timestamp = frame->pts; /* incoming buffers are clipped, so this should be positive */ running_time = gst_segment_to_running_time (&GST_VIDEO_ENCODER_INPUT_SEGMENT (enc), GST_FORMAT_TIME, timestamp); g_return_val_if_fail (running_time >= 0 || timestamp < 0, GST_FLOW_ERROR); GST_OBJECT_LOCK (enc); if (enc->bitrate_changed) { long int bitrate = enc->video_bitrate; th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate, sizeof (long int)); enc->bitrate_changed = FALSE; } if (enc->quality_changed) { long int quality = enc->video_quality; th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality, sizeof (long int)); enc->quality_changed = FALSE; } /* see if we need to schedule a keyframe */ force_keyframe = GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame); GST_OBJECT_UNLOCK (enc); if (enc->packetno == 0) { /* no packets written yet, setup headers */ GstCaps *caps; GstBuffer *buf; GList *buffers = NULL; int result; GstVideoCodecState *state; enc->granulepos_offset = 0; enc->timestamp_offset = 0; GST_DEBUG_OBJECT (enc, "output headers"); /* Theora streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg bitstream spec. The second header holds any comment fields. The third header holds the bitstream codebook. We merely need to make the headers, then pass them to libtheora one at a time; libtheora handles the additional Ogg bitstream constraints */ /* create the remaining theora headers */ th_comment_clear (&enc->comment); th_comment_init (&enc->comment); while ((result = th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) { buf = theora_enc_buffer_from_header_packet (enc, &op); buffers = g_list_prepend (buffers, buf); } if (result < 0) { g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL); g_list_free (buffers); goto encoder_disabled; } buffers = g_list_reverse (buffers); /* mark buffers and put on caps */ caps = gst_caps_new_empty_simple ("video/x-theora"); caps = theora_set_header_on_caps (caps, buffers); state = gst_video_encoder_set_output_state (benc, caps, enc->input_state); GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, state->caps); gst_video_codec_state_unref (state); gst_video_encoder_negotiate (GST_VIDEO_ENCODER (enc)); gst_video_encoder_set_headers (benc, buffers); theora_enc_reset_ts (enc, running_time, frame->presentation_frame_number); } { th_ycbcr_buffer ycbcr; gint res; GstVideoFrame vframe; if (force_keyframe) { theora_enc_reset (enc); theora_enc_reset_ts (enc, running_time, frame->presentation_frame_number); } if (enc->multipass_cache_fd && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) { if (!theora_enc_read_multipass_cache (enc)) { ret = GST_FLOW_ERROR; goto multipass_read_failed; } } gst_video_frame_map (&vframe, &enc->input_state->info, frame->input_buffer, GST_MAP_READ); theora_enc_init_buffer (ycbcr, &vframe); res = th_encode_ycbcr_in (enc->encoder, ycbcr); gst_video_frame_unmap (&vframe); /* none of the failure cases can happen here */ g_assert (res == 0); if (enc->multipass_cache_fd && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) { if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) { ret = GST_FLOW_ERROR; goto multipass_write_failed; } } ret = GST_FLOW_OK; while (th_encode_packetout (enc->encoder, 0, &op)) { ret = theora_push_packet (enc, &op); if (ret != GST_FLOW_OK) goto beach; } } beach: gst_video_codec_frame_unref (frame); return ret; /* ERRORS */ multipass_read_failed: { gst_video_codec_frame_unref (frame); return ret; } multipass_write_failed: { gst_video_codec_frame_unref (frame); return ret; } encoder_disabled: { gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), ("libtheora has been compiled with the encoder disabled")); return GST_FLOW_ERROR; } }
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; };
void theora_comment_clear(theora_comment *_tc){ th_comment_clear((th_comment *)_tc); }
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; }
void writeTheora(std::vector<std::vector<uint8_t>> vidframes, std::string writeTo, uint16_t vidWidth, uint16_t vidHeight) { // get paddings to nearest multiple of 0x10 uint32_t padW = 16 - vidWidth % 16; uint32_t padH = 16 - vidHeight % 16; uint32_t frmWidth = vidWidth + padW; uint32_t frmHeight = vidHeight + padH; // initialize theora stream th_info vidinfo; th_info_init(&vidinfo); vidinfo.frame_width = frmWidth; vidinfo.frame_height = frmHeight; vidinfo.pic_width = vidWidth; vidinfo.pic_height = vidHeight; vidinfo.pic_x = 0; vidinfo.pic_y = 0; vidinfo.colorspace = TH_CS_ITU_REC_470M; // what our RGB->YCbCr function operates on vidinfo.pixel_fmt = TH_PF_444; // we want the bestest video possible, so no decimation vidinfo.target_bitrate = 0; // prefer VBR with quality level... vidinfo.quality = 63; // ...which we want as high as possible (since we aren't using photographic frames, lossy compression ruins things) vidinfo.fps_numerator = 15; // framerate is 15 fps vidinfo.fps_denominator = 1; // initialize theora encoding context th_enc_ctx * videnc = th_encode_alloc(&vidinfo); // initialize theora comment th_comment vidcomment; th_comment_init(&vidcomment); // initialize ogg container ogg_stream_state vidcont; // serial number chosen by fair dice roll if (ogg_stream_init(&vidcont, 42)) { // returned -1, thus failed std::cerr << "Failed to initialize ogg container :(\n"; throw 42; } // get generic ogg packet & page holders ogg_packet vidpacket; ogg_page vidpage; // generic YCbCr frame, and initial data const int Y = 0; const int Cb = 1; const int Cr = 2; // clarity bonuses th_ycbcr_buffer rawdata; for (auto & i : rawdata) { i.width = i.stride = frmWidth; i.height = frmHeight; i.data = new unsigned char [frmWidth * frmHeight]; } // open file for writing std::ofstream vidfile; // because god forbid this thing supports an unsigned char unit vidfile.open(writeTo, std::ios::binary); // factor out the ogg page writing process a bit auto writePage = [&](){ vidfile.write((char*)vidpage.header, vidpage.header_len); if (!vidfile) { std::cerr << "An error occured in writing Ogg page header to file. Exiting...\n"; vidfile.close(); throw 42; } vidfile.write((char*)vidpage.body, vidpage.body_len); if (!vidfile) { std::cerr << "An error occured in writing Ogg page body to file. Exiting...\n"; vidfile.close(); throw 42; } }; // send header packets to ogg stream bool gotone = false; while (true) { int mkpacket = th_encode_flushheader(videnc, &vidcomment, &vidpacket); if (mkpacket == 0) { if (gotone) { break; } else { std::cerr << "Theora didn't return any header packets.\n"; throw 42; } } if (mkpacket < 0) { std::cerr << "Theora header flushing failed with error code " << mkpacket << ". Exiting...\n"; throw 42; } if (ogg_stream_packetin(&vidcont, &vidpacket)) { std::cerr << "Giving packet to Ogg failed, sorry.\n"; throw 42; } gotone = true; } // write ogg pages (and then the remainder via flush) to file while (ogg_stream_pageout(&vidcont, &vidpage)) { writePage(); } while (ogg_stream_flush(&vidcont, &vidpage)) { writePage(); } ////////////////////// // WRITE THE FRAMES // ////////////////////// for (int FRNO = 0; FRNO < vidframes.size(); FRNO++) { auto * VFR = &vidframes.at(FRNO); // since we set an offset of (0,0) for the picture, we fill up the // top and right edges of the frame with junk. This is us filling // the top part for (int i = 0; i < padH; i++) { for (int j = 0; j < frmWidth; j++) { rawdata[Y].data[i * frmWidth + j] = 0; rawdata[Cb].data[i * frmWidth + j] = 0; rawdata[Cr].data[i * frmWidth + j] = 0; } } // now for the picture itself (every row we add more junk to the right // of the image) int vecAt = 0; // where we are in the VFR vector for (int i = 0; i < vidHeight; i++) { for (int j = 0; j < vidWidth; j++) { rawdata[Y].data[i * frmWidth + j] = VFR->at(vecAt); vecAt++; rawdata[Cb].data[i * frmWidth + j] = VFR->at(vecAt); vecAt++; rawdata[Cr].data[i * frmWidth + j] = VFR->at(vecAt); vecAt++; } // get right-side padding (fill with junk) for (int j = vidWidth; j < frmWidth; j++) { rawdata[Y].data[i * frmWidth + j] = 0; rawdata[Cb].data[i * frmWidth + j] = 0; rawdata[Cr].data[i * frmWidth + j] = 0; } } // frame made, send through theora if (th_encode_ycbcr_in(videnc, rawdata)) { std::cerr << "Error in sending frame " << FRNO + 1 << " of " << vidframes.size() << " to Theora.\n"; throw 42; } // send theora packets into ogg while (true) { int packok = th_encode_packetout(videnc, FRNO + 1 == vidframes.size(), &vidpacket); if (packok == 0) { break; } if (packok < 0) { std::cerr << "Retrieving packet from Theora failed with error code " << packok << ".\n"; throw 42; } if (ogg_stream_packetin(&vidcont, &vidpacket)) { std::cerr << "Giving frame packet to Ogg failed.\n"; throw 42; } } // send complete pages from frame to file (we won't flush until // after all frames are accounted for, to avoid an abundance of // undersized pages) while (ogg_stream_pageout(&vidcont, &vidpage)) { writePage(); } } // take care of any remaining undersized page(s) while (ogg_stream_flush(&vidcont, &vidpage)) { writePage(); } //// Free/close/etc all relevant structures // fstream vidfile.close(); // theora items //th_encode_free(videnc); // causes a corrupted double-linked list, somehow, so you'll have to live with unfree'd memory :( th_info_clear(&vidinfo); th_comment_clear(&vidcomment); for (auto & i : rawdata) { delete[] i.data; } // ogg items ogg_packet_clear(&vidpacket); ogg_stream_clear(&vidcont); }
static int oc_dec_headerin(oc_pack_buf *_opb,th_info *_info, th_comment *_tc,th_setup_info **_setup,ogg_packet *_op){ char buffer[6]; long val; int packtype; int ret; val=oc_pack_read(_opb,8); packtype=(int)val; /*If we're at a data packet and we have received all three headers, we're done.*/ if(!(packtype&0x80)&&_info->frame_width>0&&_tc->vendor!=NULL&&*_setup!=NULL){ return 0; } /*Check the codec string.*/ oc_unpack_octets(_opb,buffer,6); if(memcmp(buffer,"theora",6)!=0)return TH_ENOTFORMAT; switch(packtype){ /*Codec info header.*/ case 0x80:{ /*This should be the first packet, and we should not already be initialized.*/ if(!_op->b_o_s||_info->frame_width>0)return TH_EBADHEADER; ret=oc_info_unpack(_opb,_info); if(ret<0)th_info_clear(_info); else ret=3; }break; /*Comment header.*/ case 0x81:{ if(_tc==NULL)return TH_EFAULT; /*We shoud have already decoded the info header, and should not yet have decoded the comment header.*/ if(_info->frame_width==0||_tc->vendor!=NULL)return TH_EBADHEADER; ret=oc_comment_unpack(_opb,_tc); if(ret<0)th_comment_clear(_tc); else ret=2; }break; /*Codec setup header.*/ case 0x82:{ oc_setup_info *setup; if(_tc==NULL||_setup==NULL)return TH_EFAULT; /*We should have already decoded the info header and the comment header, and should not yet have decoded the setup header.*/ if(_info->frame_width==0||_tc->vendor==NULL||*_setup!=NULL){ return TH_EBADHEADER; } setup=(oc_setup_info *)_ogg_calloc(1,sizeof(*setup)); if(setup==NULL)return TH_EFAULT; ret=oc_setup_unpack(_opb,setup); if(ret<0){ oc_setup_clear(setup); _ogg_free(setup); } else{ *_setup=setup; ret=1; } }break; default:{ /*We don't know what this header is.*/ return TH_EBADHEADER; }break; } return ret; }
static av_cold int encode_init(AVCodecContext* avc_context) { th_info t_info; th_comment t_comment; ogg_packet o_packet; unsigned int offset; TheoraContext *h = avc_context->priv_data; uint32_t gop_size = avc_context->gop_size; /* Set up the theora_info struct */ th_info_init(&t_info); t_info.frame_width = FFALIGN(avc_context->width, 16); t_info.frame_height = FFALIGN(avc_context->height, 16); t_info.pic_width = avc_context->width; t_info.pic_height = avc_context->height; t_info.pic_x = 0; t_info.pic_y = 0; /* Swap numerator and denominator as time_base in AVCodecContext gives the * time period between frames, but theora_info needs the framerate. */ t_info.fps_numerator = avc_context->time_base.den; t_info.fps_denominator = avc_context->time_base.num; if (avc_context->sample_aspect_ratio.num) { t_info.aspect_numerator = avc_context->sample_aspect_ratio.num; t_info.aspect_denominator = avc_context->sample_aspect_ratio.den; } else { t_info.aspect_numerator = 1; t_info.aspect_denominator = 1; } if (avc_context->color_primaries == AVCOL_PRI_BT470M) t_info.colorspace = TH_CS_ITU_REC_470M; else if (avc_context->color_primaries == AVCOL_PRI_BT470BG) t_info.colorspace = TH_CS_ITU_REC_470BG; else t_info.colorspace = TH_CS_UNSPECIFIED; if (avc_context->pix_fmt == AV_PIX_FMT_YUV420P) t_info.pixel_fmt = TH_PF_420; else if (avc_context->pix_fmt == AV_PIX_FMT_YUV422P) t_info.pixel_fmt = TH_PF_422; else if (avc_context->pix_fmt == AV_PIX_FMT_YUV444P) t_info.pixel_fmt = TH_PF_444; else { av_log(avc_context, AV_LOG_ERROR, "Unsupported pix_fmt\n"); return -1; } av_pix_fmt_get_chroma_sub_sample(avc_context->pix_fmt, &h->uv_hshift, &h->uv_vshift); if (avc_context->flags & CODEC_FLAG_QSCALE) { /* to be constant with the libvorbis implementation, clip global_quality to 0 - 10 Theora accepts a quality parameter p, which is: * 0 <= p <=63 * an int value */ t_info.quality = av_clipf(avc_context->global_quality / (float)FF_QP2LAMBDA, 0, 10) * 6.3; t_info.target_bitrate = 0; } else { t_info.target_bitrate = avc_context->bit_rate; t_info.quality = 0; } /* Now initialise libtheora */ h->t_state = th_encode_alloc(&t_info); if (!h->t_state) { av_log(avc_context, AV_LOG_ERROR, "theora_encode_init failed\n"); return -1; } h->keyframe_mask = (1 << t_info.keyframe_granule_shift) - 1; /* Clear up theora_info struct */ th_info_clear(&t_info); if (th_encode_ctl(h->t_state, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, &gop_size, sizeof(gop_size))) { av_log(avc_context, AV_LOG_ERROR, "Error setting GOP size\n"); return -1; } // need to enable 2 pass (via TH_ENCCTL_2PASS_) before encoding headers if (avc_context->flags & CODEC_FLAG_PASS1) { if (get_stats(avc_context, 0)) return -1; } else if (avc_context->flags & CODEC_FLAG_PASS2) { if (submit_stats(avc_context)) return -1; } /* Output first header packet consisting of theora header, comment, and tables. Each one is prefixed with a 16bit size, then they are concatenated together into libavcodec's extradata. */ offset = 0; /* Headers */ th_comment_init(&t_comment); while (th_encode_flushheader(h->t_state, &t_comment, &o_packet)) if (concatenate_packet(&offset, avc_context, &o_packet)) return -1; th_comment_clear(&t_comment); /* Set up the output AVFrame */ avc_context->coded_frame = av_frame_alloc(); return 0; }