/* * CINELERRA * Copyright (C) 2008 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "asset.h" #include "bcsignals.h" #include "byteorder.h" #include "clip.h" #include "edit.h" #include "file.h" #include "fileogg.h" #include "guicast.h" #include "interlacemodes.h" #include "language.h" #include "mainerror.h" #include "mutex.h" #include "mwindow.inc" #include "preferences.h" #include "render.h" #include "vframe.h" #include "versioninfo.h" #include "videodevice.inc" #include #include #include #include #include #include #define READ_SIZE 3*66000 /* This code was aspired by ffmpeg2theora */ /* Special thanks for help on this code goes out to j@v2v.cc */ int ogg_ret0, ogg_ret1, ogg_ret2; FileOGG::FileOGG(Asset *asset, File *file) : FileBase(asset, file) { if(asset->format == FILE_UNKNOWN) asset->format = FILE_OGG; asset->byte_order = 0; reset_parameters(); final_write = 1; } FileOGG::~FileOGG() { if (tf) { if (tf->videosync) { ogg_sync_clear(&tf->videosync->sync); delete tf->videosync; theora_info_clear(&tf->ti); theora_comment_clear(&tf->tc); } if (tf->audiosync) { ogg_sync_clear(&tf->audiosync->sync); delete tf->audiosync; vorbis_info_clear(&tf->vi); vorbis_comment_clear(&tf->vc); } if (tf->vpage) free(tf->vpage); if (tf->apage) free(tf->apage); delete tf; } if (temp_frame) delete temp_frame; if (stream) close_file(); if(pcm_history) { for(int i = 0; i < asset->channels; i++) delete [] pcm_history[i]; delete [] pcm_history; } if (flush_lock) delete flush_lock; } void FileOGG::get_parameters(BC_WindowBase *parent_window, Asset *asset, BC_WindowBase* &format_window, int audio_options, int video_options, EDL *edl) { if(audio_options) { OGGConfigAudio *window = new OGGConfigAudio(parent_window, asset); format_window = window; window->create_objects(); window->run_window(); delete window; } else if(video_options) { OGGConfigVideo *window = new OGGConfigVideo(parent_window, asset); format_window = window; window->create_objects(); window->run_window(); delete window; } } int FileOGG::reset_parameters_derived() { tf = 0; temp_frame = 0; stream = 0; flush_lock = 0; pcm_history = 0; start_frame = 0; return 0; } static int read_buffer(FILE *in, sync_window_t *sw, int buflen) { char *buffer = ogg_sync_buffer(&sw->sync, buflen); // printf("reading range: %lli - %lli\n", sw->file_bufpos, sw->file_bufpos + buflen); sw->wlen = fread(buffer, 1, buflen, in); ogg_sync_wrote(&sw->sync, sw->wlen); // printf("XX data: %c %c %c %c\n", sw->sync.data[0], sw->sync.data[1], sw->sync.data[2], sw->sync.data[3]); sw->file_bufpos += sw->wlen; // printf("sb: %i\n",buffer); return (sw->wlen); } static int read_buffer_at(FILE *in, sync_window_t *sw, int buflen, off_t filepos) { // printf("seeking to %lli %lli\n", filepos, sw->file_bufpos); fseeko(in, filepos, SEEK_SET); // if (sw->file_bufpos != filepos) // { sw->file_bufpos = filepos; sw->file_pagepos = filepos; // this one is not valid until sync_pageseek! ogg_sync_reset(&sw->sync); // } return read_buffer(in, sw, buflen); } static int take_page_out_autoadvance(FILE *in, sync_window_t *sw, ogg_page *og) { while (1) { int ret = ogg_sync_pageout(&sw->sync, og); if (ret > 0) { // printf("fpa: %lli\n", sw->file_pagepos); // advance 'virtual' position sw->file_pagepos += og->header_len + og->body_len; // printf("ret2: %i %i\n",ret, og->header_len + og->body_len); return ret; } else if (ret < 0) { eprintf(_("FileOGG: Taking page out on nonsynced stream!\n")); return ret; } else { // need more data for page if ((ret = read_buffer(in, sw, READ_SIZE)) == 0) { printf(_("FileOGG: There is no more data in the file we are reading from\n")); return 0; // No more data } } } return 1; } // we never need to autoadvance when syncing, since our read chunks are larger than // maximum page size static int sync_and_take_page_out(sync_window_t *sw, ogg_page *page) { page->header_len = 0; page->body_len = 0; page->header = 0; page->body = 0; int ret = ogg_sync_pageseek(&sw->sync, page); if (ret < 0) { sw->file_pagepos -= ret; } else if (ret > 0) { sw->file_pagepos += ret; // printf("ret: %i %i\n",ret, page->header_len + page->body_len); } return ret; } int FileOGG::open_file(int rd, int wr) { if (!tf) { tf = new theoraframes_info_t; memset(tf, 0, sizeof(*tf)); } if(wr) { if((stream = fopen(asset->path, "w+b")) == 0) { eprintf(_("Error while opening \"%s\" for writing. %m\n"), asset->path); return 1; } tf->audio_bytesout = 0; tf->video_bytesout = 0; tf->videotime = 0; tf->audiotime = 0; tf->vpage_valid = 0; tf->apage_valid = 0; tf->apage_buffer_length = 0; tf->vpage_buffer_length = 0; tf->apage = NULL; tf->vpage = NULL; tf->v_pkg=0; tf->a_pkg=0; /* yayness. Set up Ogg output stream */ srand (time (NULL)); if(asset->video_data) { ogg_stream_init (&tf->to, rand ()); /* oops, add one ot the above */ theora_info_init (&tf->ti); tf->ti.frame_width = asset->width; tf->ti.frame_height = asset->height; tf->ti.width = ((asset->width + 15) >>4)<<4; // round up to the nearest multiple of 16 tf->ti.height = ((asset->height + 15) >>4)<<4; // round up to the nearest multiple of 16 if (tf->ti.width != tf->ti.frame_width || tf->ti.height != tf->ti.frame_height) { eprintf(_("WARNING: Encoding theora when width or height are not dividable by 16 is suboptimal\n")); } tf->ti.offset_x = 0; tf->ti.offset_y = tf->ti.height - tf->ti.frame_height; tf->ti.fps_numerator = (unsigned int)(asset->frame_rate * 1000000); tf->ti.fps_denominator = 1000000; if (asset->aspect_ratio > 0) { // Cinelerra uses frame aspect ratio, theora uses pixel aspect ratio float pixel_aspect = asset->aspect_ratio / asset->width * asset->height; tf->ti.aspect_numerator = (unsigned int)(pixel_aspect * 1000000); tf->ti.aspect_denominator = 1000000; } else { tf->ti.aspect_numerator = 1000000; tf->ti.aspect_denominator = 1000000; } if(EQUIV(asset->frame_rate, 25) || EQUIV(asset->frame_rate, 50)) tf->ti.colorspace = OC_CS_ITU_REC_470BG; else if((asset->frame_rate > 29 && asset->frame_rate < 31) || (asset->frame_rate > 59 && asset->frame_rate < 61) ) tf->ti.colorspace = OC_CS_ITU_REC_470M; else tf->ti.colorspace = OC_CS_UNSPECIFIED; if (asset->theora_fix_bitrate) { tf->ti.target_bitrate = asset->theora_bitrate; tf->ti.quality = 0; } else { tf->ti.target_bitrate = 0; tf->ti.quality = asset->theora_quality; // video quality 0-63 } tf->ti.dropframes_p = 0; tf->ti.quick_p = 1; tf->ti.keyframe_auto_p = 1; tf->ti.keyframe_frequency = asset->theora_keyframe_frequency; tf->ti.keyframe_frequency_force = asset->theora_keyframe_force_frequency; tf->ti.keyframe_data_target_bitrate = (unsigned int) (tf->ti.target_bitrate * 1.5) ; tf->ti.keyframe_auto_threshold = 80; tf->ti.keyframe_mindistance = 8; tf->ti.noise_sensitivity = 1; tf->ti.sharpness = 2; if (theora_encode_init (&tf->td, &tf->ti)) { eprintf(_("(FileOGG:file_open) initialization of theora codec failed\n")); } } /* init theora done */ /* initialize Vorbis too, if we have audio. */ if(asset->audio_data) { ogg_stream_init (&tf->vo, rand ()); vorbis_info_init (&tf->vi); /* Encoding using a VBR quality mode. */ int ret; if(!asset->vorbis_vbr) { ret = vorbis_encode_init(&tf->vi, asset->channels, asset->sample_rate, asset->vorbis_max_bitrate, asset->vorbis_bitrate, asset->vorbis_min_bitrate); } else { // Set true VBR as demonstrated by http://svn.xiph.org/trunk/vorbis/doc/vorbisenc/examples.html ret = vorbis_encode_setup_managed(&tf->vi, asset->channels, asset->sample_rate, -1, asset->vorbis_bitrate, -1); ret |= vorbis_encode_ctl(&tf->vi, OV_ECTL_RATEMANAGE_AVG, NULL); ret |= vorbis_encode_setup_init(&tf->vi); } if (ret) { eprintf(_("The Vorbis encoder could not set up a mode according to\n" "the requested quality or bitrate.\n\n")); fclose (stream); stream = 0; return 1; } vorbis_comment_init (&tf->vc); // comment is cleared lateron vorbis_comment_add_tag (&tf->vc, (char*)"ENCODER", (char*)PROGRAM_NAME " " CINELERRA_VERSION); /* set up the analysis state and auxiliary encoding storage */ vorbis_analysis_init (&tf->vd, &tf->vi); vorbis_block_init (&tf->vd, &tf->vb); } /* audio init done */ /* write the bitstream header packets with proper page interleave */ /* first packet will get its own page automatically */ if(asset->video_data) { theora_encode_header (&tf->td, &tf->op); ogg_stream_packetin (&tf->to, &tf->op); if (ogg_stream_pageout (&tf->to, &tf->og) != 1) { eprintf(_("Internal Ogg library error.\n")); return 1; } fwrite (tf->og.header, 1, tf->og.header_len, stream); fwrite (tf->og.body, 1, tf->og.body_len, stream); /* create the remaining theora headers */ theora_comment_init (&tf->tc); theora_comment_add_tag (&tf->tc, (char*)"ENCODER", (char*)PROGRAM_NAME " " CINELERRA_VERSION); theora_encode_comment (&tf->tc, &tf->op); ogg_stream_packetin (&tf->to, &tf->op); theora_comment_clear(&tf->tc); theora_encode_tables (&tf->td, &tf->op); ogg_stream_packetin (&tf->to, &tf->op); } if(asset->audio_data) { ogg_packet header; ogg_packet header_comm; ogg_packet header_code; vorbis_analysis_headerout (&tf->vd, &tf->vc, &header, &header_comm, &header_code); ogg_stream_packetin (&tf->vo, &header); /* automatically placed in its own page */ vorbis_comment_clear(&tf->vc); if (ogg_stream_pageout (&tf->vo, &tf->og) != 1) { eprintf(_("Internal Ogg library error.\n")); return 1; } fwrite (tf->og.header, 1, tf->og.header_len, stream); fwrite (tf->og.body, 1, tf->og.body_len, stream); /* remaining vorbis header packets */ ogg_stream_packetin (&tf->vo, &header_comm); ogg_stream_packetin (&tf->vo, &header_code); } /* Flush the rest of our headers. This ensures * the actual data in each stream will start * on a new page, as per spec. */ while (1 && asset->video_data) { int result = ogg_stream_flush (&tf->to, &tf->og); if (result < 0) { /* can't get here */ eprintf(_("Internal Ogg library error.\n")); return 1; } if (result == 0) break; fwrite (tf->og.header, 1, tf->og.header_len, stream); fwrite (tf->og.body, 1, tf->og.body_len, stream); } while (1 && asset->audio_data) { int result = ogg_stream_flush (&tf->vo, &tf->og); if (result < 0) { /* can't get here */ eprintf(_("Internal Ogg library error.\n")); return 1; } if (result == 0) break; fwrite (tf->og.header, 1, tf->og.header_len, stream); fwrite (tf->og.body, 1, tf->og.body_len, stream); } flush_lock = new Mutex("OGGFile::Flush lock"); // printf("End of headers at position: %lli\n", ftello(stream)); } else if (rd) { if((stream = fopen(asset->path, "rb")) == 0) { eprintf(_("Error while opening %s for reading. %m\n"), asset->path); return 1; } /* get file length */ struct stat file_stat; stat(asset->path, &file_stat); file_length = file_stat.st_size; /* start up Ogg stream synchronization layer */ /* oy is used just here to parse header, we use separate syncs for video and audio*/ sync_window_t oy; ogg_sync_init(&oy.sync); // make sure we init the position structures to zero read_buffer_at(stream, &oy, READ_SIZE, 0); /* init supporting Vorbis structures needed in header parsing */ vorbis_info_init(&tf->vi); vorbis_comment_init(&tf->vc); /* init supporting Theora structures needed in header parsing */ theora_comment_init(&tf->tc); theora_info_init(&tf->ti); /* Ogg file open; parse the headers */ /* Only interested in Vorbis/Theora streams */ int stateflag = 0; int theora_p = 0; int vorbis_p = 0; while(!stateflag) { // int ret = read_buffer(stream, &oy, 4096); TRACE("FileOGG::open_file 60") // if(ret == 0) // break; while(take_page_out_autoadvance(stream, &oy, &tf->og) > 0) { ogg_stream_state test; /* is this a mandated initial header? If not, stop parsing */ if(!ogg_page_bos(&tf->og)) { /* don't leak the page; get it into the appropriate stream */ // queue_page(&tf->og); if(theora_p)ogg_stream_pagein(&tf->to, &tf->og); if(vorbis_p)ogg_stream_pagein(&tf->vo, &tf->og); stateflag = 1; break; } ogg_stream_init(&test, ogg_page_serialno(&tf->og)); ogg_stream_pagein(&test, &tf->og); ogg_stream_packetout(&test, &tf->op); /* identify the codec: try theora */ if(!theora_p && theora_decode_header(&tf->ti, &tf->tc, &tf->op)>=0) { /* it is theora */ memcpy(&tf->to, &test, sizeof(test)); theora_p = 1; // find out granule shift - from liboggz's oggz_auto.c unsigned char * header = tf->op.packet; theora_keyframe_granule_shift = (char) ((header[40] & 0x03) << 3); theora_keyframe_granule_shift |= (header[41] & 0xe0) >> 5; } else if(!vorbis_p && vorbis_synthesis_headerin(&tf->vi, &tf->vc, &tf->op)>=0) { /* it is vorbis */ memcpy(&tf->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(&tf->to, &tf->op))) { if(ret < 0) { eprintf(_("FileOGG: Error parsing Theora stream headers; corrupt stream?\n")); return 1; } if(theora_decode_header(&tf->ti, &tf->tc, &tf->op)) { eprintf(_("FileOGG: Error parsing Theora stream headers; corrupt stream?\n")); return 1; } theora_p++; if(theora_p == 3) break; } /* look for more vorbis header packets */ while(vorbis_p && (vorbis_p < 3) && (ret = ogg_stream_packetout(&tf->vo, &tf->op))) { if(ret<0) { eprintf(_("FileOGG: Error parsing Vorbis stream headers; corrupt stream?\n")); return 1; } if (vorbis_synthesis_headerin(&tf->vi, &tf->vc, &tf->op)) { eprintf(_("FileOGG: Error parsing Vorbis stream headers; corrupt stream?\n")); return 1; } vorbis_p++; if (vorbis_p == 3) break; } if ((!vorbis_p || vorbis_p == 3) && (!theora_p || theora_p == 3)) break; /* The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec */ if(take_page_out_autoadvance(stream, &oy, &tf->og) > 0) { // queue_page(&tf->og); /* demux into the appropriate stream */ if(theora_p) ogg_stream_pagein(&tf->to, &tf->og); if(vorbis_p) ogg_stream_pagein(&tf->vo, &tf->og); } else { eprintf(_("FileOGG: End of file while searching for codec headers.\n")); return 1; } } // Remember where the real data begins for later seeking purposes filedata_begin = oy.file_pagepos; /* and now we have it all. initialize decoders */ if(theora_p) { int ret; // WORKAROUND for bug in alpha4 version of theora: tf->td.internal_encode = 0; ret = theora_decode_init(&tf->td, &tf->ti); if( ret ) printf("theora_decode_init ret=%d\n", ret); double fps = (double)tf->ti.fps_numerator/tf->ti.fps_denominator; /* printf("FileOGG: Ogg logical stream %x is Theora %dx%d %.02f fps\n", (unsigned int)tf->to.serialno, tf->ti.width, tf->ti.height, fps); */ /* Not yet available in alpha4, we assume 420 for now switch(tf->ti.pixelformat) { case OC_PF_420: printf(" 4:2:0 video\n"); break; case OC_PF_422: printf(" 4:2:2 video\n"); break; case OC_PF_444: printf(" 4:4:4 video\n"); break; case OC_PF_RSVD: default: printf(" video\n (UNKNOWN Chroma sampling!)\n"); break; } */ theora_cmodel = BC_YUV420P; if(tf->ti.width!=tf->ti.frame_width || tf->ti.height!=tf->ti.frame_height) { eprintf(_("Frame content is %dx%d with offset (%d,%d), We do not support this yet. You will get black border.\n"), tf->ti.frame_width, tf->ti.frame_height, tf->ti.offset_x, tf->ti.offset_y); } tf->videosync = new sync_window_t; ogg_sync_init(&tf->videosync->sync); tf->videosync->wlen = 0; ret = ogg_get_first_page(tf->videosync, tf->to.serialno, &tf->videopage); if( !ret ) printf("ogg_get_first_page ret=%d\n", ret); ogg_packet op; // we have headers already decoded, so just skip them ogg_stream_reset(&tf->to); ogg_stream_pagein(&tf->to, &tf->videopage); while (1) { while (ogg_stream_packetpeek(&tf->to, NULL) != 1) { if (!ogg_get_next_page(tf->videosync, tf->to.serialno, &tf->videopage)) { printf(_("FileOGG: Cannot find next page while looking for first non-header packet\n")); return 1; } ogg_stream_pagein(&tf->to, &tf->videopage); } ogg_stream_packetout(&tf->to, &op); if (!theora_packet_isheader(&op)) break; } // now get to the page of the finish of the first packet while (ogg_page_packets(&tf->videopage) == 0) { if (ogg_page_granulepos(&tf->videopage) != -1) { printf(_("FileOGG: Broken ogg file - broken page: ogg_page_packets == 0 and granulepos != -1\n")); return 1; } ogg_get_next_page(tf->videosync, tf->to.serialno, &tf->videopage); } // FIXME - LOW PRIORITY - start counting at first decodable keyframe! // but then we have to fix a/v sync somehow start_frame = (int64_t) (theora_granule_frame (&tf->td, ogg_page_granulepos(&tf->videopage)) - ogg_page_packets(&tf->videopage)) + 1; ret = ogg_get_last_page(tf->videosync, tf->to.serialno, &tf->videopage); last_frame = (int64_t) (theora_granule_frame (&tf->td, ogg_page_granulepos(&tf->videopage))); asset->video_length = last_frame - start_frame + 1; // printf("FileOGG:: first frame: %lli, last frame: %lli, video length: %lli\n", start_frame, last_frame, asset->video_length); asset->layers = 1; // FIXME - LOW PRIORITY Cinelerra does not honor the frame_width and frame_height asset->width = tf->ti.width; asset->height = tf->ti.height; // Don't want a user configured frame rate to get destroyed if(!asset->frame_rate) asset->frame_rate = fps; // All theora material is noninterlaced by definition if(!asset->interlace_mode) asset->interlace_mode = ILACE_MODE_NOTINTERLACED; /* ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 0 +start_frame); ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 1 +start_frame); ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 5 +start_frame); ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 206 +start_frame); ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 207 +start_frame); ogg_get_page_of_frame(tf->videosync, tf->to.serialno, &og, 208 +start_frame); int64_t kf; ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, 0 + start_frame, &kf); ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, 1 + start_frame, &kf); ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, 5 + start_frame, &kf); ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, 66 + start_frame, &kf); //printf("Keyframe: %lli\n", kf); ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, 1274 + start_frame, &kf); */ set_video_position(0); // make sure seeking is done to the first sample ogg_frame_position = -10; asset->video_data = 1; strncpy(asset->vcodec, "theo", 4); // report_colorspace(&ti); // dump_comments(&tc); } else { /* tear down the partial theora setup */ theora_info_clear(&tf->ti); theora_comment_clear(&tf->tc); } if(vorbis_p) { vorbis_synthesis_init(&tf->vd, &tf->vi); vorbis_block_init(&tf->vd, &tf->vb); /* eprintf(_("FileOGG: Ogg logical stream %x is Vorbis %d channel %d Hz audio.\n"), (unsigned int)tf->vo.serialno, tf->vi.channels, (int)tf->vi.rate); */ /* init audio_sync structure */ tf->audiosync = new sync_window_t; ogg_sync_init(&tf->audiosync->sync); tf->audiosync->wlen = 0; ogg_get_first_page(tf->audiosync, tf->vo.serialno, &tf->audiopage); ogg_packet op; ogg_stream_reset(&tf->vo); // FIXME - LOW PRIORITY should be getting pages until one has granulepos, // probably never happens in pracitce ogg_ret2 = ogg_stream_pagein(&tf->vo, &tf->audiopage); ogg_int64_t accumulated = 0; long lastblock = -1; int result; while((result = ogg_stream_packetout(&tf->vo, &op))) { if(result > 0) { // ignore holes long thisblock = vorbis_packet_blocksize(&tf->vi, &op); if(lastblock != -1) accumulated += (lastblock + thisblock) >> 2; lastblock = thisblock; } } start_sample = ogg_page_granulepos(&tf->audiopage) - accumulated; /* printf("Begin: %lli, To byte: %lli, granule: %lli, serialno: %x\n", tf->audiosync->file_pagepos_found, tf->audiosync->file_pagepos_found + tf->audiopage.body_len + tf->audiopage.header_len, ogg_page_granulepos(&tf->audiopage), ogg_page_serialno(&tf->audiopage)); while (ogg_get_next_page(tf->audiosync, tf->vo.serialno, &tf->audiopage)) printf("At byte: %lli, To byte: %lli, granule: %lli, serialno: %x\n", tf->audiosync->file_pagepos_found, tf->audiosync->file_pagepos_found + tf->audiopage.body_len + tf->audiopage.header_len, ogg_page_granulepos(&tf->audiopage), ogg_page_serialno(&tf->audiopage)); */ ogg_ret1 = ogg_get_last_page(tf->audiosync, tf->vo.serialno, &tf->audiopage); last_sample = ogg_page_granulepos(&tf->audiopage); asset->audio_length = last_sample - start_sample; /* printf("FileOGG:: First sample: %lli, last_sample: %lli\n", start_sample, last_sample); printf("FileOGG:: audio length: samples: %lli, playing time: %f\n", asset->audio_length, 1.0 * asset->audio_length / tf->vi.rate); */ /* printf("End: %lli, To byte: %lli, granule: %lli, serialno: %x\n", tf->audiosync->file_pagepos_found, tf->audiosync->file_pagepos_found + tf->audiopage.body_len + tf->audiopage.header_len, ogg_page_granulepos(&tf->audiopage), ogg_page_serialno(&tf->audiopage)); while (ogg_get_prev_page(tf->audiosync, tf->vo.serialno, &tf->audiopage)) printf("At byte: %lli, To byte: %lli, granule: %lli, serialno: %x\n", tf->audiosync->file_pagepos_found, tf->audiosync->file_pagepos_found + tf->audiopage.body_len + tf->audiopage.header_len, ogg_page_granulepos(&tf->audiopage), ogg_page_serialno(&tf->audiopage)); */ /* ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 0); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 1); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 50); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 5000); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 30000); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 50000); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 90000); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 95999); ogg_get_page_of_sample(tf->audiosync, tf->vo.serialno, &tf->audiopage, 96000); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 0); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 1); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 5000); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 30000); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 50000); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 90000); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 95999); ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, 96000); */ asset->channels = tf->vi.channels; if(!asset->sample_rate) asset->sample_rate = tf->vi.rate; asset->audio_data = 1; ogg_sample_position = -10; set_audio_position(0); // make sure seeking is done to the first sample strncpy(asset->acodec, "vorb", 4); } else { /* tear down the partial vorbis setup */ vorbis_info_clear(&tf->vi); vorbis_comment_clear(&tf->vc); } ogg_sync_clear(&oy.sync); } return 0; } int FileOGG::ogg_get_prev_page(sync_window_t *sw, long serialno, ogg_page *og) { ogg_page page; off_t filepos = sw->file_pagepos_found - READ_SIZE; int first_page_offset = 0; int done = 0; int read_len = READ_SIZE; // printf("fp: %lli pagepos found: %lli\n", filepos, sw->file_pagepos_found); while (!done) { if (filepos < 0) { // read_len = read_len - (filedata_begin - filepos); read_len = read_len + filepos; filepos = 0; } if (read_len <= 0) return 0; int have_read = read_buffer_at(stream, sw, read_len, filepos); // printf("reading at %lli, len: %i, read: %i, pagepos: %lli, pageposfound: %lli\n", filepos, read_len, have_read, sw->file_pagepos, sw->file_pagepos_found); // printf("Buffer position: %lli\n", sw->file_bufpos); // printf("SS: storage: %i, fill: %i, returned: %i\n", sw->sync.storage, sw->sync.fill, sw->sync.returned); // printf("US: unsynced%i, headrebytes: %i, bodybyes: %i\n", sw->sync.unsynced, sw->sync.headerbytes, sw->sync.bodybytes); // printf("data: %c %c %c %c\n", sw->sync.data[0], sw->sync.data[1], sw->sync.data[2], sw->sync.data[3]); if (!have_read) return 0; // read all pages in the buffer int page_offset = 0; int page_length = 0; int first_page = 1; while (first_page || page_length) { // if negative, skip bytes while ((page_length = sync_and_take_page_out(sw, &page)) < 0) { page_offset -= page_length; // if (filepos == 0) // printf("BBBb page_len: %i\n", page_length); } // if (filepos == 0 && page_length) // { // printf("AAAAAAAAAAAAAAAAAAAAAAAAAaaa\n"); // printf("pp: %lli %x\n", sw->file_pagepos, ogg_page_serialno(&page)); // } if (first_page) first_page_offset = page_offset; first_page = 0; // printf("pl: %i, serial: %x iscem: %x\n", page_length, ogg_page_serialno(&page), serialno); if (page_length && ogg_page_serialno(&page) == serialno) { // we will copy every page until last page in this buffer done = 1; sw->file_pagepos_found = sw->file_pagepos - page.header_len - page.body_len; // printf("got it : %lli %i %i\n", sw->file_pagepos, page.header_len, page.body_len); memcpy(og, &page, sizeof(page)); } } // printf("fpo: %i\n", first_page_offset); filepos += first_page_offset - READ_SIZE; } // printf("finished\n"); if (done) return 1; else return 0; } int FileOGG::ogg_get_last_page(sync_window_t *sw, long serialno, ogg_page *og) { ogg_page page; off_t filepos = file_length - READ_SIZE; if (filepos < 0) filepos = 0; int first_page_offset = 0; int done = 0; while (!done && filepos >= 0) { //int readlen = read_buffer_at(stream, sw, READ_SIZE, filepos); // read all pages in the buffer int page_offset = 0; int page_length = 0; int first_page = 1; while (first_page || page_length) { // if negative, skip bytes while ((page_length = sync_and_take_page_out(sw, &page)) < 0) page_offset -= page_length; if (first_page) first_page_offset = page_offset; first_page = 0; if (page_length && ogg_page_serialno(&page) == serialno) { // we will copy every page until last page in this buffer done = 1; sw->file_pagepos_found = sw->file_pagepos - page.header_len - page.body_len; memcpy(og, &page, sizeof(page)); } } filepos = filepos + first_page_offset - READ_SIZE; } if (done) return 1; else return 0; } int FileOGG::ogg_get_first_page(sync_window_t *sw, long serialno, ogg_page *og) { // printf("FileOGG:: Seeking to first page at %lli\n", filedata_begin); read_buffer_at(stream, sw, READ_SIZE, 0); // we don't even need to sync since we _know_ it is right return (ogg_get_next_page(sw, serialno, og)); } int FileOGG::ogg_seek_to_databegin(sync_window_t *sw, long serialno) { // printf("FileOGG:: Seeking to first page at %lli\n", filedata_begin); read_buffer_at(stream, sw, READ_SIZE, filedata_begin); // we don't even need to sync since we _know_ it is right return (0); } int FileOGG::ogg_get_next_page(sync_window_t *sw, long serialno, ogg_page *og) { while (take_page_out_autoadvance(stream, sw, og) > 0) { if (ogg_page_serialno(og) == serialno) { sw->file_pagepos_found = sw->file_pagepos - og->header_len - og->body_len; return 1; } } return 0; } int FileOGG::ogg_sync_and_get_next_page(sync_window_t *sw, long serialno, ogg_page *og) { // TODO: Put better error reporting inhere int ret; while ((ret = sync_and_take_page_out(sw, og)) < 0) { // do nothing; } if (ret == 0) return 0; if (ogg_page_serialno(og) == serialno) { sw->file_pagepos_found = sw->file_pagepos - og->header_len - og->body_len; return 1; } while (ogg_get_next_page(sw, serialno, og)) if (ogg_page_serialno(og) == serialno) { sw->file_pagepos_found = sw->file_pagepos - og->header_len - og->body_len; return 1; } return 0; } // Returns: // >= 0, number of sample within a page // < 0 error int FileOGG::ogg_get_page_of_sample(sync_window_t *sw, long serialno, ogg_page *og, int64_t sample) { // First make an educated guess about position if (sample >= asset->audio_length + start_sample) { printf(_("FileOGG: Illegal seek beyond end of samples\n")); return 0; } off_t educated_guess = filedata_begin + (file_length - filedata_begin) * (sample - start_sample) / asset->audio_length - READ_SIZE; if (educated_guess < 0) educated_guess = 0; // printf("My educated guess: %lli\n", educated_guess); // now see if we won read_buffer_at(stream, sw, READ_SIZE, educated_guess); ogg_sync_and_get_next_page(sw, serialno, og); int64_t end_sample = ogg_page_granulepos(og); // linear seek to the sample int64_t start_sample = 0; // TODO: Use bisection also // printf("Next page granulepos: %lli, pagepos: %lli\n", end_sample, sw->file_pagepos_found); if (end_sample <= sample) { // scan forward while (end_sample <= sample) { ogg_get_next_page(sw, serialno, og); start_sample = end_sample; end_sample = ogg_page_granulepos(og); } ogg_get_prev_page(sw, serialno, og); while (ogg_page_continued(og) && ogg_page_packets(og) == 1) ogg_get_prev_page(sw, serialno, og); } else { // scan backward start_sample = end_sample; while (start_sample > sample || (ogg_page_continued(og) && ogg_page_packets(og) == 1)) { // printf("get prev page: %lli pagepos:%lli\n", ogg_page_granulepos(og), sw->file_pagepos_found); ogg_get_prev_page(sw, serialno, og); end_sample = start_sample; start_sample = ogg_page_granulepos(og); } // go forward one page at the end } // printf("For sample %lli we need to start decoding on page with granulepos: %lli\n", sample, ogg_page_granulepos(og)); return 1; } // seeks, so next sample returned will be the correct one // sample here is still the vorbis sample number (= cinelerra sample number + start_sample) // reinits the decoding engine int FileOGG::ogg_seek_to_sample(sync_window_t *sw, long serialno, int64_t sample) { // MAYBE FIXME - find out if we really are decoding previous two packets or not // get to the sample ogg_page og; ogg_packet op; // printf("Calling get page of sample\n"); if (!ogg_get_page_of_sample(sw, serialno, &og, sample)) { eprintf(_("FileOGG: Seeking to sample's page failed\n")); return 0; } // printf("Pagepos: %lli\n", sw->file_pagepos); vorbis_synthesis_restart(&tf->vd); ogg_stream_reset(&tf->vo); ogg_stream_pagein(&tf->vo, &og); int sync = 0; // printf("seeking to sample : %lli , starting at page with gpos: %lli\n", sample, ogg_page_granulepos(&og)); int64_t current_comming_sample = -1; while (1) { // make sure we have a packet ready while (ogg_stream_packetpeek(&tf->vo, NULL) != 1) { if (!ogg_get_next_page(sw, serialno, &og)) { eprintf(_("FileOGG: Cannot find next page while seeking\n")); return 0; } ogg_stream_pagein(&tf->vo, &og); } ogg_stream_packetout(&tf->vo, &op); if (sync) { if(!vorbis_synthesis(&tf->vb, &op)) { ogg_ret0 = vorbis_synthesis_blockin(&tf->vd, &tf->vb); int64_t previous_comming_sample = current_comming_sample; current_comming_sample += vorbis_synthesis_pcmout(&tf->vd, NULL); if (current_comming_sample > sample) { if (previous_comming_sample > sample) { eprintf(_("Ogg decoding error while seeking sample\n")); } vorbis_synthesis_read(&tf->vd, (sample - previous_comming_sample)); // printf("WE GOT IT, samples already decoded: %jd\n", vorbis_synthesis_pcmout(&tf->vd,NULL)); return 1; // YAY next sample read is going to be ours, sexy! } else { // discard decoded data before current sample vorbis_synthesis_read(&tf->vd, (current_comming_sample - previous_comming_sample)); } } } if (!sync && op.granulepos >= 0) { sync = 1; current_comming_sample = op.granulepos; if(!vorbis_synthesis(&tf->vb, &op)) { vorbis_synthesis_blockin(&tf->vd, &tf->vb); if (vorbis_synthesis_pcmout(&tf->vd, NULL) != 0) { eprintf(_("FileOGG: Something wrong while trying to seek\n")); return 0; } } } } return 0; } int FileOGG::ogg_get_page_of_frame(sync_window_t *sw, long serialno, ogg_page *og, int64_t frame) { if (frame >= asset->video_length + start_frame) { eprintf(_("FileOGG: Illegal seek beyond end of frames\n")); return 0; } // printf("frame: %lli start frame: %lli\n", frame, start_frame); // printf("file_length: %lli filedata_begin: %lli\n", file_length, filedata_begin); off_t educated_guess = filedata_begin + (file_length - filedata_begin) * (frame - start_frame) / asset->video_length - READ_SIZE/2; // educated_guess += 100000; if (educated_guess > file_length - READ_SIZE) educated_guess = file_length - READ_SIZE; if (educated_guess < filedata_begin) educated_guess = filedata_begin; // printf("My educated guess: %lli\n", educated_guess); // now see if we won read_buffer_at(stream, sw, READ_SIZE, educated_guess); if( !ogg_sync_and_get_next_page(sw, serialno, og) ) { printf(_("FileOGG: ogg_sync_and_get_next_page failed\n")); return 0; } int64_t pageend_frame; //int read_back = 0; // find the page with "real" ending while ((pageend_frame = ogg_page_granulepos(og)) == -1) { if (ogg_get_next_page(sw, serialno, og) == 0) { //read_back = 1; break; } } pageend_frame = theora_granule_frame(&tf->td, ogg_page_granulepos(og)); // FIXME - MEDIUM PRIORITY: read back if we've gone too far and no page of our serialno at all can be found // linear seek to the sample // TODO: Use bisection also // printf("Next page granulepos: %lli, pagepos: %lli\n", end_sample, sw->file_pagepos_found); //int discard_packets = 0; int missp = 0; int missm = 0; if (pageend_frame <= frame) { // scan forward while (pageend_frame < frame) { do { ogg_get_next_page(sw, serialno, og); } while (ogg_page_packets(og) == 0); pageend_frame = theora_granule_frame(&tf->td, ogg_page_granulepos(og)); missp++; } // go back if this frame starts on previous page if (ogg_page_continued(og) && pageend_frame - ogg_page_packets(og) == frame - 1) { do { ogg_get_prev_page(sw, serialno, og); } while (ogg_page_packets(og) == 0 && ogg_page_continued(og)); } pageend_frame = theora_granule_frame(&tf->td, ogg_page_granulepos(og)); } else { // scan backward int64_t first_frame_on_page = theora_granule_frame(&tf->td, ogg_page_granulepos(og)) - ogg_page_packets(og) + 2; if (!ogg_page_continued(og)) first_frame_on_page--; while (first_frame_on_page > frame) { // printf("get prev page: %lli pagepos:%lli\n", ogg_page_granulepos(og), sw->file_pagepos_found); do { ogg_get_prev_page(sw, serialno, og); } while (ogg_page_packets(og) == 0 && ogg_page_continued(og)); missm++; // pageend_frame = theora_granule_frame(&tf->td, ogg_page_granulepos(og)); first_frame_on_page = theora_granule_frame(&tf->td, ogg_page_granulepos(og)) - ogg_page_packets(og) + 2; if (!ogg_page_continued(og)) first_frame_on_page--; } } // printf("Miss plus: %i, miss minus: %i\n", missp, missm); // printf("last frame of page with frame : %lli\n", pageend_frame); return 1; } int FileOGG::ogg_seek_to_keyframe(sync_window_t *sw, long serialno, int64_t frame, int64_t *position) { //printf("seek %jd\n", frame); ogg_page og; ogg_packet op; if( !ogg_get_page_of_frame(sw, serialno, &og, frame) ) { eprintf(_("FileOGG: seek to frame failed\n")); return 0; } // find a page with packets while( ogg_page_packets(&og) == 0 ) { ogg_get_prev_page(sw, serialno, &og); } int64_t granulepos = ogg_page_granulepos(&og); granulepos &= ~((1<td, granulepos); // iframe based on granulepos if( frame < iframe || !ogg_get_page_of_frame(sw, serialno, &og, iframe) ) { eprintf(_("FileOGG: seek to iframe failed\n")); return 0; } while( ogg_page_packets(&og) == 0 ) { ogg_get_prev_page(sw, serialno, &og); } int64_t pageend_frame = theora_granule_frame(&tf->td, ogg_page_granulepos(&og)); int64_t frames_on_page = ogg_page_packets(&og); if( ogg_page_continued(&og) ) --frames_on_page; // get frame before page with with iframe int64_t page_frame = pageend_frame - frames_on_page; if( page_frame < 0 ) page_frame = 0; //printf("iframe %jd, page_frame %jd, frames_on_page %jd\n", iframe, page_frame, frames_on_page); ogg_stream_reset(&tf->to); ogg_stream_pagein(&tf->to, &og); while( ++page_frame < iframe ) { while( ogg_stream_packetpeek(&tf->to, NULL) != 1 ) { if( !ogg_get_next_page(sw, serialno, &og) ) { eprintf(_("FileOGG: Cannot find next page while seeking\n")); return 0; } ogg_stream_pagein(&tf->to, &og); } ogg_stream_packetout(&tf->to, &op); } *position = iframe - 1; return 1; } int FileOGG::check_sig(Asset *asset) { FILE *fd = fopen(asset->path, "rb"); // Test for "OggS" fseek(fd, 0, SEEK_SET); char data[4]; (void)fread(data, 4, 1, fd); if(data[0] == 'O' && data[1] == 'g' && data[2] == 'g' && data[3] == 'S') { fclose(fd); // printf("Yay, we have an ogg file\n"); return 1; } fclose(fd); return 0; } int FileOGG::close_file() { if (file->wr) { if (final_write) { if (asset->audio_data) write_samples_vorbis(0, 0, 1); // set eos if (asset->video_data) write_frames_theora(0, 1, 1); // set eos } flush_ogg(1); // flush all if (asset->audio_data) { vorbis_block_clear (&tf->vb); vorbis_dsp_clear (&tf->vd); vorbis_info_clear (&tf->vi); ogg_stream_clear (&tf->vo); } if (asset->video_data) { theora_info_clear (&tf->ti); ogg_stream_clear (&tf->to); theora_clear (&tf->td); } if (stream) fclose(stream); stream = 0; } else if (file->rd) { if (asset->audio_data) { vorbis_block_clear (&tf->vb); vorbis_dsp_clear (&tf->vd); vorbis_comment_clear (&tf->vc); vorbis_info_clear (&tf->vi); ogg_stream_clear (&tf->vo); } theora_comment_clear(&tf->tc); if (asset->video_data) { theora_info_clear (&tf->ti); theora_comment_clear (&tf->tc); theora_clear (&tf->td); ogg_stream_clear (&tf->to); } if (stream) fclose(stream); stream = 0; } return 0; } int FileOGG::close_file_derived() { //printf("FileOGG::close_file_derived(): 1\n"); if (stream) fclose(stream); stream = 0; return 0; } int64_t FileOGG::get_video_position() { // printf("GVP\n"); return next_frame_position - start_frame; } int64_t FileOGG::get_audio_position() { return next_sample_position - start_sample; } int FileOGG::set_video_position(int64_t x) { // x=0; // printf("SVP: %lli\n", x); next_frame_position = x + start_frame; return 1; } int FileOGG::colormodel_supported(int colormodel) { // printf("CMS\n"); if (colormodel == BC_YUV420P) return BC_YUV420P; else return colormodel; } int FileOGG::get_best_colormodel(Asset *asset, int driver) { return BC_YUV420P; } int FileOGG::read_frame(VFrame *frame) { if(!stream) return 1; // skip is cheaper than seek, do it... int decode_frames = 0; int expect_keyframe = 0; if (ogg_frame_position >= 0 && next_frame_position >= ogg_frame_position && next_frame_position - ogg_frame_position < 32) { decode_frames = next_frame_position - ogg_frame_position; } else if (next_frame_position != ogg_frame_position) { if (!ogg_seek_to_keyframe(tf->videosync, tf->to.serialno, next_frame_position, &ogg_frame_position)) { eprintf(_("FileOGG:: Error while seeking to frame's keyframe" " (frame: %jd, keyframe: %jd)\n"), next_frame_position, ogg_frame_position); return 1; } // printf("For frame: %lli, keyframe is: %lli\n", next_frame_position,ogg_frame_position); // skip frames must be > 0 here decode_frames = next_frame_position - ogg_frame_position; if (decode_frames <= 0) { eprintf(_("FileOGG:: Error while seeking to keyframe," " wrong keyframe number (frame: %jd, keyframe: %jd)\n"), next_frame_position, ogg_frame_position); return 1; } expect_keyframe = 1; } // printf("Frames to decode: %i\n", decode_frames); // THIS IS WHAT CAUSES SLOWNESS OF SEEKING, but we can do nothing about it. int ret = -1; while (decode_frames > 0) { ogg_page og; ogg_packet op; while (ogg_stream_packetpeek(&tf->to, NULL) != 1) { if (!ogg_get_next_page(tf->videosync, tf->to.serialno, &og)) { eprintf(_("FileOGG: Cannot find next page while seeking\n")); return 1; } ogg_stream_pagein(&tf->to, &og); } ogg_stream_packetout(&tf->to, &op); if( theora_packet_isheader(&op) ) continue; //printf("frame %jd, key %d\n", ogg_frame_position, theora_packet_iskeyframe(&op)); if (expect_keyframe && !theora_packet_iskeyframe(&op)) { eprintf(_("FileOGG: Expecting keyframe, but didn't get it\n")); // return 1; this is generally not a fatal error } expect_keyframe = 0; // decode ret = theora_decode_packetin(&tf->td, &op); //if(ret < 0 )printf("ret = %d\n", ret); decode_frames --; ogg_frame_position ++; } if( ret >= 0 ) { yuv_buffer yuv; int ret = theora_decode_YUVout (&tf->td, &yuv); if (ret) { eprintf(_("FileOGG: theora_decode_YUVout failed with code %i\n"), ret); } // Dirty magic /* yuv.y += yuv.y_stride * (yuv.y_height - 1); yuv.u += yuv.uv_stride * (yuv.uv_height - 1); yuv.v += yuv.uv_stride * (yuv.uv_height - 1); yuv.y_stride = - yuv.y_stride; yuv.uv_stride = - yuv.uv_stride;*/ VFrame *temp_frame = new VFrame(yuv.y, -1, 0, yuv.u - yuv.y, yuv.v - yuv.y, - yuv.y_stride, yuv.y_height, BC_YUV420P, - yuv.y_stride); // copy into temp frame... BC_CModels::transfer(frame->get_rows(), temp_frame->get_rows(), frame->get_y(), frame->get_u(), frame->get_v(), temp_frame->get_y(), temp_frame->get_u(), temp_frame->get_v(), 0, 0, yuv.y_width, yuv.y_height, 0, 0, yuv.y_width, // temp_frame can be larger than frame if width not dividable by 16 yuv.y_height, BC_YUV420P, frame->get_color_model(), 0, -temp_frame->get_w(), frame->get_w()); delete temp_frame; } next_frame_position ++; return 0; } int FileOGG::ogg_decode_more_samples(sync_window_t *sw, long serialno) { ogg_page og; ogg_packet op; int done = 0; while (!done) { while (ogg_stream_packetpeek(&tf->vo, NULL) != 1) { if (!ogg_get_next_page(sw, serialno, &og)) { eprintf(_("FileOGG: Cannot find next page while trying to decode more samples\n")); return 0; } ogg_stream_pagein(&tf->vo, &og); } ogg_stream_packetout(&tf->vo, &op); if(!vorbis_synthesis(&tf->vb, &op)) { done = 1; vorbis_synthesis_blockin(&tf->vd, &tf->vb); } } return 1; } int FileOGG::set_audio_position(int64_t x) { next_sample_position = x + start_sample; return 0; } int FileOGG::move_history(int from, int to, int len) { if( len > 0 ) { for(int i = 0; i < asset->channels; i++) memmove(pcm_history[i] + to, pcm_history[i] + from, sizeof(float) * len); } history_start = history_start + from - to; if( history_start < 0 ) history_start = 0; return 0; } int FileOGG::read_samples(double *buffer, int64_t len) { float **vorbis_buffer; if (len <= 0) return 0; // printf("Reading samples: Channel: %i, number of samples: %lli, reading at :%lli\n", file->current_channel, len, next_sample_position); // printf("\tnext_sample_position: %lli, length: %i\n", next_sample_position, len); // printf("\thistory_start: %lli, length: %i\n", history_start, history_size); if(len > HISTORY_MAX) { eprintf(_("max samples=%d\n"), HISTORY_MAX); return 1; } if(!pcm_history) { pcm_history = new float*[asset->channels]; for(int i = 0; i < asset->channels; i++) pcm_history[i] = new float[HISTORY_MAX]; history_start = -100000000; // insane value to trigger reload history_size = 0; } int64_t hole_start = -1; int64_t hole_len = -1; int64_t hole_absstart = -1; int64_t hole_fill = 0; if (history_start < next_sample_position && history_start + history_size > next_sample_position && history_start + history_size < next_sample_position + len) { // printf("a\n"); hole_fill = 1; hole_start = history_start + history_size - next_sample_position; hole_len = history_size - hole_start; hole_absstart = next_sample_position + hole_start; move_history(next_sample_position - history_start, 0, hole_start); // } else if (next_sample_position < history_start && history_start < next_sample_position + len) { // printf("b\n"); hole_fill = 1; hole_start = 0; hole_len = history_start - next_sample_position; hole_absstart = next_sample_position; // printf("hs: %lli, histstart: %lli, next_sample_position: %lli\n", history_size, history_start, next_sample_position); // printf("to: 0, from: %lli, size: %lli\n", // history_start - next_sample_position, // history_size - history_start + next_sample_position); move_history(0, history_start - next_sample_position, history_size - history_start + next_sample_position); } else if (next_sample_position >= history_start + history_size || next_sample_position + len <= history_start) { // printf("c\n"); hole_fill = 1; hole_start = 0; hole_len = HISTORY_MAX; hole_absstart = next_sample_position; history_start = hole_absstart; history_size = hole_len; } if (hole_fill) { if (hole_start < 0 || hole_len <= 0 || hole_absstart < 0) { eprintf(_("FileOGG: Error at finding out what to read from file\n")); return 1; } if (hole_absstart + hole_len > asset->audio_length + start_sample) { hole_len = asset->audio_length + start_sample - hole_absstart; history_size = asset->audio_length + start_sample - history_start; } else { history_size = HISTORY_MAX; } // printf("Decode samples at position: %lli, samples to read: %lli\n", hole_absstart, hole_len); int64_t samples_read = 0; if (ogg_sample_position != hole_absstart) { ogg_sample_position = hole_absstart; if (!ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, ogg_sample_position)) { eprintf(_("Error while seeking to sample\n")); return 1; } } // now we have ogg_sample_positon aligned int64_t samples_to_read = hole_len; while (samples_read < hole_len) { int64_t waiting_samples = vorbis_synthesis_pcmout(&tf->vd, &vorbis_buffer); int64_t takeout_samples; if (waiting_samples > samples_to_read - samples_read) takeout_samples = samples_to_read - samples_read; else takeout_samples = waiting_samples; // printf("takeout samples: %lli, samples_read: %lli\n", takeout_samples, samples_read); if(waiting_samples) { for(int i = 0; i < asset->channels; i++) { float *input = vorbis_buffer[i]; float *output = pcm_history[i] + hole_start; // TODO: use memcpy for(int j = 0; j < takeout_samples ; j++) { output[j] = input[j]; } } } vorbis_synthesis_read(&tf->vd, takeout_samples); samples_read += takeout_samples; ogg_sample_position += takeout_samples; hole_start += takeout_samples; if (samples_read < hole_len) if (!ogg_decode_more_samples(tf->audiosync, tf->vo.serialno)) { ogg_sample_position = -1; return 1; } } } // now we can be sure our history is correct, just copy it out if (next_sample_position < history_start || next_sample_position + len > history_start + history_size) { printf(_("FileOGG:: History not aligned properly \n")); printf(_("\tnext_sample_position: %jd, length: %jd\n"), next_sample_position, len); printf(_("\thistory_start: %jd, length: %jd\n"), history_start, history_size); return 1; } float *input = pcm_history[file->current_channel] + next_sample_position - history_start; for (int i = 0; i < len; i++) buffer[i] = input[i]; next_sample_position += len; return 0; } int FileOGG::write_audio_page() { int ret; ret = fwrite(tf->apage, 1, tf->apage_len, stream); if(ret < tf->apage_len) { eprintf(_("error writing audio page\n")); } tf->apage_valid = 0; tf->a_pkg -= ogg_page_packets((ogg_page *)&tf->apage); return ret; } int FileOGG::write_video_page() { int ret; ret = fwrite(tf->vpage, 1, tf->vpage_len, stream); if(ret < tf->vpage_len) { eprintf(_("error writing video page\n")); } tf->vpage_valid = 0; tf->v_pkg -= ogg_page_packets((ogg_page *)&tf->vpage); return ret; } void FileOGG::flush_ogg (int e_o_s) { int len; ogg_page og; flush_lock->lock(); /* flush out the ogg pages */ while(1) { /* Get pages for both streams, if not already present, and if available.*/ if(asset->video_data && !tf->vpage_valid) { // this way seeking is much better, // not sure if 23 packets is a good value. it works though int v_next=0; if(tf->v_pkg>22 && ogg_stream_flush(&tf->to, &og) > 0) { v_next=1; } else if(ogg_stream_pageout(&tf->to, &og) > 0) { v_next=1; } if(v_next) { len = og.header_len + og.body_len; if(tf->vpage_buffer_length < len) { tf->vpage = (unsigned char *)realloc(tf->vpage, len); tf->vpage_buffer_length = len; } tf->vpage_len = len; memcpy(tf->vpage, og.header, og.header_len); memcpy(tf->vpage+og.header_len , og.body, og.body_len); tf->vpage_valid = 1; tf->videotime = theora_granule_time (&tf->td, ogg_page_granulepos(&og)); } } if(asset->audio_data && !tf->apage_valid) { // this way seeking is much better, // not sure if 23 packets is a good value. it works though int a_next=0; if(tf->a_pkg>22 && ogg_stream_flush(&tf->vo, &og) > 0) { a_next=1; } else if(ogg_stream_pageout(&tf->vo, &og) > 0) { a_next=1; } if(a_next) { len = og.header_len + og.body_len; if(tf->apage_buffer_length < len) { tf->apage = (unsigned char *)realloc(tf->apage, len); tf->apage_buffer_length = len; } tf->apage_len = len; memcpy(tf->apage, og.header, og.header_len); memcpy(tf->apage+og.header_len , og.body, og.body_len); tf->apage_valid = 1; tf->audiotime= vorbis_granule_time (&tf->vd, ogg_page_granulepos(&og)); } } if(!asset->audio_data && tf->vpage_valid) { write_video_page(); } else if(!asset->video_data && tf->apage_valid) { write_audio_page(); } /* We're using both. We can output only: * a) If we have valid pages for both * b) At EOS, for the remaining stream. */ else if(tf->vpage_valid && tf->apage_valid) { /* Make sure they're in the right order. */ if(tf->videotime <= tf->audiotime) write_video_page(); else write_audio_page(); } else if(e_o_s && tf->vpage_valid) { write_video_page(); } else if(e_o_s && tf->apage_valid) { write_audio_page(); } else { break; /* Nothing more writable at the moment */ } } flush_lock->unlock(); } int FileOGG::write_samples_vorbis(double **buffer, int64_t len, int e_o_s) { float **vorbis_buffer; static int samples = 0; samples += len; if(e_o_s) { vorbis_analysis_wrote (&tf->vd, 0); } else { vorbis_buffer = vorbis_analysis_buffer (&tf->vd, len); /* double to float conversion */ for(int i = 0; ichannels; i++) { for (int j = 0; j < len; j++) { vorbis_buffer[i][j] = buffer[i][j]; } } vorbis_analysis_wrote (&tf->vd, len); } while(vorbis_analysis_blockout (&tf->vd, &tf->vb) == 1) { /* analysis, assume we want to use bitrate management */ vorbis_analysis (&tf->vb, NULL); vorbis_bitrate_addblock (&tf->vb); /* weld packets into the bitstream */ while (vorbis_bitrate_flushpacket (&tf->vd, &tf->op)) { flush_lock->lock(); ogg_stream_packetin (&tf->vo, &tf->op); tf->a_pkg++; flush_lock->unlock(); } } flush_ogg(0); return 0; } int FileOGG::write_samples(double **buffer, int64_t len) { if (len > 0) return write_samples_vorbis(buffer, len, 0); return 0; } int FileOGG::write_frames_theora(VFrame ***frames, int len, int e_o_s) { // due to clumsy theora's design we need to delay writing out by one frame // always stay one frame behind, so we can correctly encode e_o_s int result = 0; if(!stream) return 0; for(int j = 0; j < len && !result; j++) { if (temp_frame) // encode previous frame if available { yuv_buffer yuv; yuv.y_width = tf->ti.width; yuv.y_height = tf->ti.height; yuv.y_stride = temp_frame->get_bytes_per_line(); yuv.uv_width = tf->ti.width / 2; yuv.uv_height = tf->ti.height / 2; yuv.uv_stride = temp_frame->get_bytes_per_line() /2; yuv.y = temp_frame->get_y(); yuv.u = temp_frame->get_u(); yuv.v = temp_frame->get_v(); int ret = theora_encode_YUVin (&tf->td, &yuv); if (ret) { printf(_("FileOGG: theora_encode_YUVin failed with code %i\n"), ret); printf("yuv_buffer: y_width: %i, y_height: %i, y_stride: %i," " uv_width: %i, uv_height: %i, uv_stride: %i\n", yuv.y_width, yuv.y_height, yuv.y_stride, yuv.uv_width, yuv.uv_height, yuv.uv_stride); } while(theora_encode_packetout (&tf->td, e_o_s, &tf->op)) { flush_lock->lock(); ogg_stream_packetin (&tf->to, &tf->op); tf->v_pkg++; flush_lock->unlock(); } flush_ogg(0); // eos flush is done later at close_file } // If we have e_o_s, don't encode any new frames if (e_o_s) break; if (!temp_frame) { temp_frame = new VFrame ( tf->ti.width, tf->ti.height, BC_YUV420P, 0); } VFrame *frame = frames[0][j]; int in_color_model = frame->get_color_model(); if (in_color_model == BC_YUV422P && temp_frame->get_w() == frame->get_w() && temp_frame->get_h() == frame->get_h() && temp_frame->get_bytes_per_line() == frame->get_bytes_per_line()) { temp_frame->copy_from(frame); } else { BC_CModels::transfer(temp_frame->get_rows(), frame->get_rows(), temp_frame->get_y(), temp_frame->get_u(), temp_frame->get_v(), frame->get_y(), frame->get_u(), frame->get_v(), 0, 0, frame->get_w(), frame->get_h(), 0, 0, frame->get_w(), // temp_frame can be larger than frame if width not dividable by 16 frame->get_h(), frame->get_color_model(), BC_YUV420P, 0, frame->get_w(), temp_frame->get_w()); } } return 0; } int FileOGG::write_frames(VFrame ***frames, int len) { return write_frames_theora(frames, len, 0); } OGGConfigAudio::OGGConfigAudio(BC_WindowBase *parent_window, Asset *asset) : BC_Window(_(PROGRAM_NAME ": Audio Compression"), parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1), 350, 250) { this->parent_window = parent_window; this->asset = asset; } OGGConfigAudio::~OGGConfigAudio() { } void OGGConfigAudio::create_objects() { // add_tool(new BC_Title(10, 10, _("There are no audio options for this format"))); int x = 10, y = 10; int x1 = 150; char string[BCTEXTLEN]; lock_window("OGGConfigAudio::create_objects"); add_tool(fixed_bitrate = new OGGVorbisFixedBitrate(x, y, this)); add_tool(variable_bitrate = new OGGVorbisVariableBitrate(x1, y, this)); y += 30; sprintf(string, "%d", asset->vorbis_min_bitrate); add_tool(new BC_Title(x, y, _("Min bitrate:"))); add_tool(new OGGVorbisMinBitrate(x1, y, this, string)); y += 30; add_tool(new BC_Title(x, y, _("Avg bitrate:"))); sprintf(string, "%d", asset->vorbis_bitrate); add_tool(new OGGVorbisAvgBitrate(x1, y, this, string)); y += 30; add_tool(new BC_Title(x, y, _("Max bitrate:"))); sprintf(string, "%d", asset->vorbis_max_bitrate); add_tool(new OGGVorbisMaxBitrate(x1, y, this, string)); add_subwindow(new BC_OKButton(this)); show_window(1); unlock_window(); } int OGGConfigAudio::close_event() { set_done(0); return 1; } OGGVorbisFixedBitrate::OGGVorbisFixedBitrate(int x, int y, OGGConfigAudio *gui) : BC_Radial(x, y, !gui->asset->vorbis_vbr, _("Average bitrate")) { this->gui = gui; } int OGGVorbisFixedBitrate::handle_event() { gui->asset->vorbis_vbr = 0; gui->variable_bitrate->update(0); return 1; } OGGVorbisVariableBitrate::OGGVorbisVariableBitrate(int x, int y, OGGConfigAudio *gui) : BC_Radial(x, y, gui->asset->vorbis_vbr, _("Variable bitrate")) { this->gui = gui; } int OGGVorbisVariableBitrate::handle_event() { gui->asset->vorbis_vbr = 1; gui->fixed_bitrate->update(0); return 1; } OGGVorbisMinBitrate::OGGVorbisMinBitrate(int x, int y, OGGConfigAudio *gui, char *text) : BC_TextBox(x, y, 180, 1, text) { this->gui = gui; } int OGGVorbisMinBitrate::handle_event() { gui->asset->vorbis_min_bitrate = atol(get_text()); return 1; } OGGVorbisMaxBitrate::OGGVorbisMaxBitrate(int x, int y, OGGConfigAudio *gui, char *text) : BC_TextBox(x, y, 180, 1, text) { this->gui = gui; } int OGGVorbisMaxBitrate::handle_event() { gui->asset->vorbis_max_bitrate = atol(get_text()); return 1; } OGGVorbisAvgBitrate::OGGVorbisAvgBitrate(int x, int y, OGGConfigAudio *gui, char *text) : BC_TextBox(x, y, 180, 1, text) { this->gui = gui; } int OGGVorbisAvgBitrate::handle_event() { gui->asset->vorbis_bitrate = atol(get_text()); return 1; } OGGConfigVideo::OGGConfigVideo(BC_WindowBase *parent_window, Asset *asset) : BC_Window(_(PROGRAM_NAME ": Video Compression"), parent_window->get_abs_cursor_x(1), parent_window->get_abs_cursor_y(1), 450, 220) { this->parent_window = parent_window; this->asset = asset; } OGGConfigVideo::~OGGConfigVideo() { } void OGGConfigVideo::create_objects() { // add_tool(new BC_Title(10, 10, _("There are no video options for this format"))); int x = 10, y = 10; int x1 = x + 150; int x2 = x + 300; lock_window("OGGConfigVideo::create_objects"); add_subwindow(new BC_Title(x, y + 5, _("Bitrate:"))); add_subwindow(new OGGTheoraBitrate(x1, y, this)); add_subwindow(fixed_bitrate = new OGGTheoraFixedBitrate(x2, y, this)); y += 30; add_subwindow(new BC_Title(x, y, _("Quality:"))); add_subwindow(new BC_ISlider(x + 80, y, 0, 200, 200, 0, 63, asset->theora_quality, 0, 0, &asset->theora_quality)); add_subwindow(fixed_quality = new OGGTheoraFixedQuality(x2, y, this)); y += 30; add_subwindow(new BC_Title(x, y, _("Keyframe frequency:"))); OGGTheoraKeyframeFrequency *keyframe_frequency = new OGGTheoraKeyframeFrequency(x1 + 60, y, this); keyframe_frequency->create_objects(); y += 30; add_subwindow(new BC_Title(x, y, _("Keyframe force frequency:"))); OGGTheoraKeyframeForceFrequency *keyframe_force_frequency = new OGGTheoraKeyframeForceFrequency(x1 + 60, y, this); keyframe_force_frequency->create_objects(); y += 30; add_subwindow(new BC_Title(x, y, _("Sharpness:"))); OGGTheoraSharpness *sharpness = new OGGTheoraSharpness(x1 + 60, y, this); sharpness->create_objects(); y += 30; add_subwindow(new BC_OKButton(this)); show_window(1); unlock_window(); } int OGGConfigVideo::close_event() { set_done(0); return 1; } OGGTheoraBitrate::OGGTheoraBitrate(int x, int y, OGGConfigVideo *gui) : BC_TextBox(x, y, 100, 1, gui->asset->theora_bitrate) { this->gui = gui; } int OGGTheoraBitrate::handle_event() { // TODO: MIN / MAX check gui->asset->theora_bitrate = atol(get_text()); return 1; }; OGGTheoraFixedBitrate::OGGTheoraFixedBitrate(int x, int y, OGGConfigVideo *gui) : BC_Radial(x, y, gui->asset->theora_fix_bitrate, _("Fixed bitrate")) { this->gui = gui; } int OGGTheoraFixedBitrate::handle_event() { update(1); gui->asset->theora_fix_bitrate = 1; gui->fixed_quality->update(0); return 1; }; OGGTheoraFixedQuality::OGGTheoraFixedQuality(int x, int y, OGGConfigVideo *gui) : BC_Radial(x, y, !gui->asset->theora_fix_bitrate, _("Fixed quality")) { this->gui = gui; } int OGGTheoraFixedQuality::handle_event() { update(1); gui->asset->theora_fix_bitrate = 0; gui->fixed_bitrate->update(0); return 1; }; OGGTheoraKeyframeFrequency::OGGTheoraKeyframeFrequency(int x, int y, OGGConfigVideo *gui) : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency, (int64_t)1, (int64_t)500, x, y, 40) { this->gui = gui; } int OGGTheoraKeyframeFrequency::handle_event() { gui->asset->theora_keyframe_frequency = atol(get_text()); return 1; } OGGTheoraKeyframeForceFrequency::OGGTheoraKeyframeForceFrequency(int x, int y, OGGConfigVideo *gui) : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency, (int64_t)1, (int64_t)500, x, y, 40) { this->gui = gui; } int OGGTheoraKeyframeForceFrequency::handle_event() { gui->asset->theora_keyframe_frequency = atol(get_text()); return 1; } OGGTheoraSharpness::OGGTheoraSharpness(int x, int y, OGGConfigVideo *gui) : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_sharpness, (int64_t)0, (int64_t)2, x, y, 40) { this->gui = gui; } int OGGTheoraSharpness::handle_event() { gui->asset->theora_sharpness = atol(get_text()); return 1; } PackagingEngineOGG::PackagingEngineOGG() { packages = 0; default_asset = 0; } PackagingEngineOGG::~PackagingEngineOGG() { if(packages) { for(int i = 0; i < total_packages; i++) delete packages[i]; delete [] packages; } if (default_asset) default_asset->remove_user(); } int PackagingEngineOGG::create_packages_single_farm( EDL *edl, Preferences *preferences, Asset *default_asset, double total_start, double total_end) { this->total_start = total_start; this->total_end = total_end; this->edl = edl; this->preferences = preferences; // We make A COPY of the asset, because we set audio_data = 0 on local asset which is the same copy as default_asset... // Should be taken care of somewhere else actually this->default_asset = new Asset(*default_asset); audio_start = Units::to_int64(total_start * default_asset->sample_rate); video_start = Units::to_int64(total_start * default_asset->frame_rate); audio_position = audio_start; video_position = video_start; audio_end = Units::to_int64(total_end * default_asset->sample_rate); video_end = Units::to_int64(total_end * default_asset->frame_rate); current_package = 0; double total_len = total_end - total_start; //printf("PackageDispatcher::create_packages: %f / %d = %f\n", total_len, total_packages, package_len); total_packages = 0; if (default_asset->audio_data) total_packages++; if (default_asset->video_data) total_packages += preferences->renderfarm_job_count; packages = new RenderPackage*[total_packages]; int local_current_package = 0; if (default_asset->audio_data) { packages[local_current_package] = new RenderPackage; snprintf(packages[current_package]->path, sizeof(packages[current_package]->path), "%s.audio", default_asset->path); local_current_package++; } if (default_asset->video_data) { video_package_len = (total_len) / preferences->renderfarm_job_count; int current_number; // The number being injected into the filename. int number_start; // Character in the filename path at which the number begins int total_digits; // Total number of digits including padding the user specified. Render::get_starting_number(default_asset->path, current_number, number_start, total_digits, 3); for(int i = 0; i < preferences->renderfarm_job_count; i++) { RenderPackage *package = packages[local_current_package] = new RenderPackage; Render::create_filename(package->path, default_asset->path, current_number, total_digits, number_start); current_number++; local_current_package++; } } return 0; } RenderPackage* PackagingEngineOGG::get_package_single_farm(double frames_per_second, int client_number, int use_local_rate) { //printf("PackageDispatcher::get_package %ld %ld %ld %ld\n", audio_position, video_position, audio_end, video_end); if (current_package == total_packages) return 0; RenderPackage *result = 0; if (current_package == 0 && default_asset->audio_data) { result = packages[0]; result->audio_start = audio_start; result->video_start = video_start; result->audio_end = audio_end; result->video_end = video_end; result->audio_do = 1; result->video_do = 0; } else if (default_asset->video_data) { // Do not do any scaling according to node speed, so we know we can get evenly distributed 'forced' keyframes result = packages[current_package]; result->audio_do = 0; result->video_do = 1; result->audio_start = audio_position; result->video_start = video_position; result->audio_end = audio_position + Units::round(video_package_len * default_asset->sample_rate); result->video_end = video_position + Units::round(video_package_len * default_asset->frame_rate); // Last package... take it all! if (current_package == total_packages -1 ) { result->audio_end = audio_end; result->video_end = video_end; } audio_position = result->audio_end; video_position = result->video_end; } current_package ++; return result; } void PackagingEngineOGG::get_package_paths(ArrayList *path_list) { for(int i = 0; i < total_packages; i++) { path_list->append(strdup(packages[i]->path)); } // We will mux to the the final file at the end! path_list->append(strdup(default_asset->path)); path_list->set_free(); } int64_t PackagingEngineOGG::get_progress_max() { return Units::to_int64(default_asset->sample_rate * (total_end - total_start)) * 2+ Units::to_int64(preferences->render_preroll * total_packages * default_asset->sample_rate); } int PackagingEngineOGG::packages_are_done() { // Mux audio and video into one file // First fix our asset... have to workaround the bug of corruption of local asset // Render::check_asset(edl, *default_asset); Asset *video_asset = 0, *audio_asset = 0; File *audio_file_gen = 0, *video_file_gen = 0; FileOGG *video_file = 0, *audio_file = 0; ogg_stream_state audio_in_stream, video_in_stream; int local_current_package = 0; if (default_asset->audio_data) { audio_asset = new Asset(packages[local_current_package]->path); audio_asset->format = FILE_OGG; local_current_package++; audio_file_gen = new File(); audio_file_gen->open_file(preferences, audio_asset, 1, 0); audio_file = (FileOGG*) audio_file_gen->file; ogg_stream_init(&audio_in_stream, audio_file->tf->vo.serialno); audio_file->ogg_seek_to_databegin(audio_file->tf->audiosync, audio_file->tf->vo.serialno); } if (default_asset->video_data) { video_asset = new Asset(packages[local_current_package]->path); video_asset->format = FILE_OGG; local_current_package++; video_file_gen = new File(); video_file_gen->open_file(preferences, video_asset, 1, 0); video_file = (FileOGG*) video_file_gen->file; ogg_stream_init(&video_in_stream, video_file->tf->to.serialno); video_file->ogg_seek_to_databegin(video_file->tf->videosync, video_file->tf->to.serialno); } // Output file File *output_file_gen = new File(); output_file_gen->open_file(preferences, default_asset, 0, 1); FileOGG *output_file = (FileOGG*) output_file_gen->file; //ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ ogg_packet op; /* one raw packet of data for decode */ int audio_ready = default_asset->audio_data; int video_ready = default_asset->video_data; int64_t video_packetno = 1; int64_t audio_packetno = 1; int64_t frame_offset = 0; int64_t current_frame = 0; while ((default_asset->audio_data && audio_ready) || (default_asset->video_data && video_ready)) { if (video_ready) { while (ogg_stream_packetpeek(&video_in_stream, NULL) != 1) // get as many pages as needed for one package { if (!video_file->ogg_get_next_page(video_file->tf->videosync, video_file->tf->to.serialno, &video_file->tf->videopage)) { // We are at the end of our file, see if it is more and open more if there is if (local_current_package < total_packages) { frame_offset = current_frame +1; ogg_stream_clear(&video_in_stream); video_file_gen->close_file(); delete video_file_gen; if( video_asset ) video_asset->remove_user(); video_asset = new Asset(packages[local_current_package]->path); video_asset->format = FILE_OGG; local_current_package++; video_file_gen = new File(); video_file_gen->open_file(preferences, video_asset, 1, 0); video_file = (FileOGG*) video_file_gen->file; ogg_stream_init(&video_in_stream, video_file->tf->to.serialno); video_file->ogg_seek_to_databegin(video_file->tf->videosync, video_file->tf->to.serialno); } else video_ready = 0; break; } ogg_stream_pagein(&video_in_stream, &video_file->tf->videopage); } while (ogg_stream_packetpeek(&video_in_stream, NULL) == 1) // get all packets out of the page { ogg_stream_packetout(&video_in_stream, &op); if (local_current_package != total_packages) // keep it from closing the stream op.e_o_s = 0; if (video_packetno != 1) // if this is not the first video package do not start with b_o_s op.b_o_s = 0; else op.b_o_s = 1; op.packetno = video_packetno; video_packetno ++; int64_t granulepos = op.granulepos; if (granulepos != -1) { // Fix granulepos! int64_t rel_iframe = granulepos >> video_file->theora_keyframe_granule_shift; int64_t rel_pframe = granulepos - (rel_iframe << video_file->theora_keyframe_granule_shift); int64_t rel_current_frame = rel_iframe + rel_pframe; current_frame = frame_offset + rel_current_frame; int64_t abs_iframe = current_frame - rel_pframe; op.granulepos = (abs_iframe << video_file->theora_keyframe_granule_shift) + rel_pframe; // printf("iframe: %i, pframe: %i, granulepos: %i, op.packetno %lli, abs_iframe: %i\n", rel_iframe, rel_pframe, granulepos, op.packetno, abs_iframe); } ogg_stream_packetin (&output_file->tf->to, &op); output_file->tf->v_pkg++; } } if (audio_ready) { while (ogg_stream_packetpeek(&audio_in_stream, NULL) != 1) // get as many pages as needed for one package { if (!audio_file->ogg_get_next_page(audio_file->tf->audiosync, audio_file->tf->vo.serialno, &audio_file->tf->audiopage)) { audio_ready = 0; break; } ogg_stream_pagein(&audio_in_stream, &audio_file->tf->audiopage); } while (ogg_stream_packetpeek(&audio_in_stream, NULL) == 1) // get all packets out of the page { ogg_stream_packetout(&audio_in_stream, &op); ogg_stream_packetin (&output_file->tf->vo, &op); audio_packetno++; output_file->tf->a_pkg++; } } output_file->flush_ogg(0); } // flush_ogg(1) is called on file closing time... // output_file->flush_ogg(1); // Just prevent thet write_samples and write_frames are called output_file->final_write = 0; if (default_asset->audio_data) { ogg_stream_clear(&audio_in_stream); audio_file_gen->close_file(); delete audio_file_gen; if( audio_asset ) audio_asset->remove_user(); } if (default_asset->video_data) { ogg_stream_clear(&video_in_stream); video_file_gen->close_file(); delete video_file_gen; if( video_asset ) video_asset->remove_user(); } output_file_gen->close_file(); delete output_file_gen; // don't delete the temp files, for now // for(int i = 0; i < total_packages; i++) // unlink(packages[i]->path); return 0; }