Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / fileogg.C
index 99995fe975711988a77aec82167f866e9b3851c2..bac03b1eafa3688093fc5bd6920db0d596e5d1a9 100644 (file)
@@ -1,7 +1,7 @@
-
 /*
  * CINELERRA
  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * Copyright (C) 2003-2016 Cinelerra CV contributors
  *
  * 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
@@ -18,6 +18,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
+#ifdef HAVE_OGG
 
 #include "asset.h"
 #include "bcsignals.h"
 #include <string.h>
 #include <errno.h>
 
-#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;
-}
+#define READ_SIZE 4*66000
+#define SEEK_SIZE 2*66000
 
-FileOGG::~FileOGG()
+sync_window_t::sync_window_t(FILE *fp, Mutex *sync_lock, int64_t begin, int64_t end)
 {
-       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;
+       ogg_sync_init(this);
+       this->fp = fp;
+       this->sync_lock = sync_lock;
+       this->file_begin = begin;
+       this->file_end = end;
+       filepos = -1;
+       bufpos = -1;
+       pagpos = -1;
 }
 
-void FileOGG::get_parameters(BC_WindowBase *parent_window,
-       Asset *asset, BC_WindowBase* &format_window,
-       int audio_options, int video_options, EDL *edl)
+sync_window_t::~sync_window_t()
 {
-       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;
+       ogg_sync_clear(this);
 }
 
-static int read_buffer(FILE *in, sync_window_t *sw, int buflen)
+int sync_window_t::ogg_read_locked(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);
+       char *buffer = ogg_sync_buffer(this, buflen);
+       int len = fread(buffer, 1, buflen, fp);
+       ogg_sync_wrote(this, len);
+       filepos += len;
+       return len;
 }
 
-static int read_buffer_at(FILE *in, sync_window_t *sw, int buflen, off_t filepos)
+int sync_window_t::ogg_read_buffer(int buflen)
 {
-//     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);
+       sync_lock->lock("sync_window_t::ogg_read_buffer_at");
+       fseeko(fp, filepos, SEEK_SET);
+       int len = ogg_read_locked(buflen);
+       sync_lock->unlock();
+       return len;
 }
 
-static int take_page_out_autoadvance(FILE *in, sync_window_t *sw, ogg_page *og)
+int sync_window_t::ogg_read_buffer_at(off_t filepos, int buflen)
 {
-       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;
+       if( bufpos == filepos && buflen == this->filepos - bufpos )
+               return buflen;
+       sync_lock->lock("sync_window_t::ogg_read_buffer_at");
+       this->bufpos = filepos;
+       fseeko(fp, filepos, SEEK_SET);
+       this->filepos = filepos;
+       ogg_sync_reset(this);
+       int ret = ogg_read_locked(buflen);
+       sync_lock->unlock();
+       return ret;
 }
 
-
 // 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);
-       }
+int sync_window_t::ogg_sync_and_take_page_out(ogg_page *og)
+{
+       og->header_len = 0;
+       og->body_len = 0;
+       og->header = 0;
+       og->body = 0;
+       int ret = ogg_sync_pageseek(this, og);
+       bufpos += abs(ret); // can be zero
        return ret;
 }
 
-int FileOGG::open_file(int rd, int wr)
+int sync_window_t::ogg_sync_and_get_next_page(long serialno, ogg_page *og)
 {
-       if (!tf)
-       {
-               tf = new theoraframes_info_t;
-               memset(tf, 0, sizeof(*tf));
+       int ret = 0, retries = 1000;
+       while( --retries >= 0 && (ret = ogg_sync_and_take_page_out(og)) < 0 );
+       if( ret >= mn_pagesz && ogg_page_serialno(og) != serialno )
+               ret = ogg_get_next_page(serialno, og);
+       if( ret ) {
+               pagpos = bufpos - (og->header_len + og->body_len);
+               return 1;
        }
+       return 0;
+}
 
+int sync_window_t::ogg_get_next_page(long serialno, ogg_page *og)
+{
+       int ret = 0, retries = 1000;
+       while( --retries >= 0 && (ret=ogg_take_page_out_autoadvance(og)) &&
+               ogg_page_serialno(og) != serialno );
+       if( ret ) {
+               pagpos = bufpos - (og->header_len + og->body_len);
+}
+       else
+               printf("ogg_get_next_page missed\n");
+       return ret;
+}
 
-       if(wr)
-       {
-
-
-               if((stream = fopen(asset->path, "w+b")) == 0)
-               {
-                       eprintf(_("Error while opening \"%s\" for writing. %m\n"), asset->path);
-                       return 1;
+int sync_window_t::ogg_prev_page_search(long serialno, ogg_page *og,
+               off_t begin, off_t end)
+{
+       ogg_page page;
+       int retries = 100, ret = 0;
+       int64_t ppos = -1;
+       while( ppos < 0 && --retries >= 0 ) {
+               int64_t fpos = end;
+               int read_len = SEEK_SIZE;
+               fpos -= read_len;
+               if( fpos < begin ) {
+                       read_len += fpos - begin;
+                       if( read_len <= 0 ) break;
+                       fpos = begin;
                }
-
-               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"));
+               read_len = ogg_read_buffer_at(fpos, read_len);
+               if( read_len <= 0 ) return 0;
+               while( (ret=ogg_sync_and_take_page_out(&page)) < 0 );
+               end = bufpos;
+               while( ret > 0 ) {
+                       if( ogg_page_serialno(&page) == serialno ) {
+                               memcpy(og, &page, sizeof(page));
+                               ppos = bufpos - (page.header_len + page.body_len);
                        }
+                       ret = ogg_sync_pageout(this, &page);
+                       bufpos += page.header_len + page.body_len;
                }
-               /* 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;
-                       }
+       }
+       if( ppos >= 0 ) {
+               pagpos = ppos;
+               return 1;
+       }
+       printf("ogg_prev_page_search missed\n");
+       return 0;
+}
 
-                       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);
+int sync_window_t::ogg_get_prev_page(long serialno, ogg_page *og)
+{
+       return ogg_prev_page_search(serialno, og, file_begin, pagpos);
+}
 
-               }
-               /* 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);
+int sync_window_t::ogg_get_first_page(long serialno, ogg_page *og)
+{
+       ogg_read_buffer_at(file_begin, SEEK_SIZE);
+       return ogg_sync_and_get_next_page(serialno, og);
+}
 
-                       /* remaining vorbis header packets */
-                       ogg_stream_packetin (&tf->vo, &header_comm);
-                       ogg_stream_packetin (&tf->vo, &header_code);
-               }
+int sync_window_t::ogg_get_last_page(long serialno, ogg_page *og)
+{
 
-               /* 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;
+       ogg_page page;
+       off_t filepos = file_end - READ_SIZE;
+       if( filepos < 0 ) filepos = 0;
+       int ret = 0, first_page_offset = 0;
+       while( !ret && filepos >= 0 ) {
+               int readlen = ogg_read_buffer_at(filepos, READ_SIZE);
+               int page_offset = 0, page_length = 0;
+               int first_page = 1; // read all pages in the buffer
+               while( first_page || page_length ) {
+                       // if negative, skip bytes
+                       while( (page_length = ogg_sync_and_take_page_out(&page)) < 0 )
+                               page_offset -= page_length;
+                       if( page_length < mn_pagesz ) continue;
+                       if( first_page ) {
+                               first_page = 0;
+                               first_page_offset = page_offset;
                        }
-                       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( ogg_page_serialno(&page) == serialno ) {
+                               // return last match page
+                               pagpos = bufpos - (page.header_len + page.body_len);
+                               memcpy(og, &page, sizeof(page));
+                               ret = 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);
-
+               filepos -= readlen - first_page_offset;  // move backward
+       }
+       return ret;
+}
 
-               /* init supporting Vorbis structures needed in header parsing */
-               vorbis_info_init(&tf->vi);
-               vorbis_comment_init(&tf->vc);
+OGG_PageBfr::OGG_PageBfr()
+{
+       allocated = len = 0;
+       valid = packets = 0;
+       position = 0;
+       page = 0;
+}
 
+OGG_PageBfr::~OGG_PageBfr()
+{
+       delete [] page;
+}
 
-               /* init supporting Theora structures needed in header parsing */
-               theora_comment_init(&tf->tc);
-               theora_info_init(&tf->ti);
+void OGG_PageBfr::demand(int sz)
+{
+       if( allocated >= sz ) return;
+       uint8_t *new_page = new uint8_t[sz];
+       memcpy(new_page, page, len);
+       delete [] page;  page = new_page;
+       allocated = sz;
+}
 
+int OGG_PageBfr::write_page(FILE *fp)
+{
+       int sz = fwrite(page, 1, len, fp);
+       if( sz != len ) return -1;
+       ogg_page op;  // kludgy
+       op.header = page;    op.header_len = len;
+       op.body = page+len;  op.body_len = 0;
+       packets -= ogg_page_packets(&op);
+       valid = len = 0;
+       return packets;
+}
 
+int64_t OGG_PageBfr::load(ogg_page *og)
+{
+       int sz = og->header_len + og->body_len;
+       demand(sz);
+       memcpy(page, og->header, og->header_len);
+       memcpy(page+og->header_len, og->body, og->body_len);
+       len = sz;  valid = 1;
+       position = ogg_page_granulepos(og);
+       return position;
+}
 
-               /* 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;
+FileOGG::FileOGG(Asset *asset, File *file)
+ : FileBase(asset, file)
+{
+       if( asset->format == FILE_UNKNOWN )
+               asset->format = FILE_OGG;
+       asset->byte_order = 0;
+       init();
+       file_lock = new Mutex("OGGFile::Flush lock");
+}
 
-                       while(take_page_out_autoadvance(stream, &oy, &tf->og) > 0)
-                       {
-                               ogg_stream_state test;
+FileOGG::~FileOGG()
+{
+       close_file();
+       delete file_lock;
+}
 
-                               /* 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;
-                               }
+void FileOGG::init()
+{
+       inp = 0;
+       out = 0;
+       audio = 0;
+       video = 0;
+       file_length = 0;
+       temp_frame = 0;
+       file_lock = 0;
+       ach = 0;
+       ahz = 0;
+       asz = 0;
+       amn = 0;
+       amx = 0;
+       abr = 0;
+       avbr = 0;
+       aqu = 0;
+       afrmsz = 0;
+       pcm_history = 0;
+       pcm_channels = 0;
+       frame_position = 0;
+       sample_position = 0;
+       audiosync = 0;
+       videosync = 0;
+       file_begin = 0;
+       file_end = 0;
+
+       memset(&to, 0, sizeof(to));
+       memset(&vo, 0, sizeof(vo));
+       ogg_sample_position = 0;
+       ogg_frame_position = 0;
+       next_sample_position = 0;
+       next_frame_position = 0;
+       start_sample = 0;
+       last_sample = 0;
+       start_frame = 0;
+       last_frame = 0;
+       audiotime = 0;
+       videotime = 0;
+       audio_pos = 0;  audio_eos = 0;
+       video_pos = 0;  video_eos = 0;
+
+       keyframe_granule_shift = 0;
+       iframe_granule_offset = 0;
+       theora_cmodel = BC_YUV420P;
+       enc = 0;
+       dec = 0;
+       memset(&ti, 0, sizeof(ti));
+       ts = 0;
+       memset(&tc, 0, sizeof(tc));
+       memset(&vi, 0, sizeof(vi));
+       memset(&vc, 0, sizeof(vc));
+       memset(&vd, 0, sizeof(vd));
+       memset(&vb, 0, sizeof(vb));
+       force_keyframes = 0;
+       vp3_compatible = 0;
+       soft_target = 0;
+
+       pic_x = pic_y = 0;
+       pic_w = pic_h = 0;
+       frame_w = frame_h = 0;
+       colorspace = OC_CS_UNSPECIFIED;
+       pixfmt = TH_PF_420;
+       bitrate = 0;  quality = 0;
+       keyframe_period = 0;
+       keyframe_force = 0;
+       fps_num = fps_den = 0;
+       aratio_num = aratio_den = 0;
+}
+
+
+static int ilog(unsigned v)
+{
+       int ret = 0;
+       while( v ) { ++ret;  v >>= 1; }
+       return ret;
+}
 
-                               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 */
+int FileOGG::encode_theora_init()
+{
+       ogg_stream_init(&to, rand());
+       th_info_init(&ti);
+       pic_w = asset->width, pic_h = asset->height;
+       frame_w = (pic_w+0x0f) & ~0x0f;
+       frame_h = (pic_h+0x0f) & ~0x0f;
+       pic_x = ((frame_w-pic_w) >> 1) & ~1;
+       pic_y = ((frame_h-pic_h) >> 1) & ~1;
+       fps_num = asset->frame_rate * 1000000;
+       fps_den = 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;
+               aratio_num = pixel_aspect * 1000000;
+               aratio_den = 1000000;
+       }
+       else {
+               aratio_num = 1000000;
+               aratio_den = 1000000;
+       }
+       if( EQUIV(asset->frame_rate, 25) || EQUIV(asset->frame_rate, 50) )
+               colorspace = OC_CS_ITU_REC_470BG;
+       else if( (asset->frame_rate > 29 && asset->frame_rate < 31) ||
+                (asset->frame_rate > 59 && asset->frame_rate < 61) )
+               colorspace = OC_CS_ITU_REC_470M;
+       else
+               colorspace = OC_CS_UNSPECIFIED;
+       pixfmt = TH_PF_420;
+       if( asset->theora_fix_bitrate ) {
+               bitrate = asset->theora_bitrate;
+               quality = -1;
+       }
+       else {
+               bitrate = -1;
+               quality = asset->theora_quality;     // 0-63
+       }
+       keyframe_period = asset->theora_keyframe_frequency;
+       keyframe_force = asset->theora_keyframe_force_frequency;
+       vp3_compatible = 1;
+       soft_target = 0;
+
+       ti.frame_width = frame_w;
+       ti.frame_height = frame_h;
+       ti.pic_width = pic_w;
+       ti.pic_height = pic_h;
+       ti.pic_x = pic_x;
+       ti.pic_y = pic_x;
+       ti.colorspace = (th_colorspace)colorspace;
+       ti.pixel_fmt = (th_pixel_fmt)pixfmt;
+       ti.target_bitrate = bitrate;
+       ti.quality = quality;
+       ti.fps_numerator = fps_num;
+       ti.fps_denominator = fps_den;
+       ti.aspect_numerator = aratio_num;
+       ti.aspect_denominator = aratio_den;
+       ti.keyframe_granule_shift = ilog(keyframe_period-1);
+
+       enc = th_encode_alloc(&ti);
+       int ret =  enc ? 0 : 1;
+       if( !ret && force_keyframes )
+               ret = th_encode_ctl(enc,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
+                       &keyframe_period, sizeof(keyframe_period));
+       if( !ret && vp3_compatible )
+               ret = th_encode_ctl(enc,TH_ENCCTL_SET_VP3_COMPATIBLE,
+                       &vp3_compatible, sizeof(vp3_compatible));
+       if( !ret && soft_target ) {
+               int arg = TH_RATECTL_CAP_UNDERFLOW;
+               if( th_encode_ctl(enc, TH_ENCCTL_SET_RATE_FLAGS, &arg, sizeof(arg)) < 0 ) {
+                       eprintf(_("Could not set rate flags"));
+                       ret = 1;
                }
-
-
-               /* 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;
-                       }
+               int kr = keyframe_period*7>>1, fr = 5*fps_num/fps_den;
+               arg = kr > fr ? kr : fr;
+               if( th_encode_ctl(enc, TH_ENCCTL_SET_RATE_BUFFER, &arg, sizeof(arg)) ) {
+                       eprintf(_("Could not set rate buffer"));
+                       ret = 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( ret ) {
+               eprintf(_("theora init context failed"));
+               return 1;
+       }
 
-                       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;
+       th_comment_init(&tc);
+       th_comment_add_tag(&tc, (char*)"ENCODER",
+               (char*)PROGRAM_NAME " " CINELERRA_VERSION);
+       ogg_page og;
+       ogg_packet op;
+       ret = th_encode_flushheader(enc, &tc, &op);
+       if( ret <= 0 ) return 1;
+       ogg_stream_packetin(&to, &op);
+       ret = ogg_stream_pageout(&to, &og) != 1 ? 1 : 0;
+       if( !ret ) {
+               fwrite(og.header, 1, og.header_len, out);
+               fwrite(og.body, 1, og.body_len, out);
+       }
+       if( ret ) {
+               eprintf(_("write header out failed"));
+               return 1;
+       }
+       while( (ret=th_encode_flushheader(enc, &tc, &op)) > 0 )
+               ogg_stream_packetin(&to, &op);
+       if( ret ) {
+               eprintf(_("ogg_encoder_init video failed"));
+               return 1;
+       }
+       return 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;
+int FileOGG::encode_vorbis_init()
+{
+       ach = asset->channels;
+       ahz = asset->sample_rate;
+       amx = asset->vorbis_max_bitrate;
+       amn = asset->vorbis_min_bitrate;
+       abr = asset->vorbis_bitrate;
+       avbr = asset->vorbis_vbr;
+       asz = sizeof(short);
+       afrmsz = asz * ach;
+       aqu = -99;
+       ogg_stream_init(&vo, rand());
+       vorbis_info_init(&vi);
+       int ret = 0;
+       if( avbr ) {
+               ret = vorbis_encode_setup_managed(&vi, ach, ahz, -1, abr, -1);
+               if( !ret )
+                       ret = vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_AVG, 0);
+               if( !ret )
+                       ret = vorbis_encode_setup_init(&vi);
+       }
+       else
+               ret = vorbis_encode_init(&vi, ach, ahz, amx, abr, amn);
+       if( ret ) {
+               eprintf(_("ogg_encoder_init audio init failed"));
+               return 1;
+       }
+       vorbis_comment_init(&vc);
+       vorbis_comment_add_tag(&vc, (char*)"ENCODER",
+               (char*)PROGRAM_NAME " " CINELERRA_VERSION);
+       vorbis_analysis_init(&vd, &vi);
+       vorbis_block_init(&vd, &vb);
+       ogg_packet header;
+       ogg_packet header_comm;
+       ogg_packet header_code;
+       vorbis_analysis_headerout(&vd, &vc,
+               &header, &header_comm, &header_code);
+       ogg_stream_packetin(&vo, &header);
+       ogg_page og;
+       ret = ogg_stream_pageout(&vo, &og)==1 ? 0 : -1;
+       if( ret >= 0 ) {
+               fwrite(og.header, 1, og.header_len, out);
+               fwrite(og.body, 1, og.body_len, out);
+               ogg_stream_packetin(&vo, &header_comm);
+               ogg_stream_packetin(&vo, &header_code);
+       }
+       if( ret < 0 ) {
+               eprintf(_("ogg_encoder_init audio failed"));
+               return 1;
+       }
+       return 0;
+}
 
-                       // 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);
+int FileOGG::ogg_init_encode(FILE *out)
+{
+       this->out = out;
+       srand(time(0));
+       video = asset->video_data;
+       if( video && encode_theora_init() )
+               return 1;
+       audio = asset->audio_data;
+       if( audio && encode_vorbis_init() )
+               return 1;
+       ogg_page og;
+       int ret = 0;
+       if( !ret && video ) {
+               while( (ret=ogg_stream_flush(&to, &og)) > 0 ) {
+                       fwrite(og.header, 1, og.header_len, out);
+                       fwrite(og.body, 1, og.body_len, out);
                }
-
-
-               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);
+       }
+       if( !ret && audio ) {
+               while( (ret=ogg_stream_flush(&vo, &og)) > 0 ) {
+                       fwrite(og.header, 1, og.header_len, out);
+                       fwrite(og.body, 1, og.body_len, out);
                }
+       }
+       if( ret < 0 ) {
+               eprintf(_("render init failed"));
+               return 1;
+       }
+       return 0;
+}
 
-               ogg_sync_clear(&oy.sync);
+int FileOGG::decode_theora_init()
+{
+       dec = th_decode_alloc(&ti, ts);
+       if( !dec ) {
+               eprintf(_("Error in probe data"));
+               return 1;
+       }
+       keyframe_granule_shift = ti.keyframe_granule_shift;
+       iframe_granule_offset = th_granule_frame(dec, 0);
+       double fps = (double)ti.fps_numerator/ti.fps_denominator;
 
+       videosync = new sync_window_t(inp, file_lock, file_begin, file_end);
+       ogg_page og;
+       int ret = videosync->ogg_get_first_page(to.serialno, &og);
+       if( ret <= 0 ) {
+               eprintf(_("cannot read video page from file"));
+               return 1;
        }
+       videosync->file_begin = videosync->pagpos;
+       ret = videosync->ogg_get_first_page(to.serialno, &og);
+       // video data starts here
+       // get to the page of the finish of the first packet
+       while( ret > 0 && !ogg_page_packets(&og) ) {
+               if( ogg_page_granulepos(&og) != -1 ) {
+                       printf(_("FileOGG: Broken ogg file - broken page:"
+                               " ogg_page_packets == 0 and granulepos != -1\n"));
+                       return 1;
+               }
+               ret = videosync->ogg_get_next_page(to.serialno, &og);
+       }
+       // video frames start here
+       start_frame = ogg_frame_pos(&og);
+       ret = videosync->ogg_get_first_page(to.serialno, &og);
+       if( ret <= 0 ) {
+               printf(_("FileOGG: Cannot read data past header\n"));
+               return 1;
+       }
+//printf("start frame = %jd, gpos %jd, begins %jd\n",
+// start_frame, ogg_page_granulepos(&og), videosync->file_begin);
+
+       ret = videosync->ogg_get_last_page(to.serialno, &og);
+       while( ret > 0 && !ogg_page_packets(&og) )
+               ret = videosync->ogg_get_prev_page(to.serialno, &og);
+       if( ret > 0 ) {
+               last_frame = ogg_next_frame_pos(&og);
+               if( start_frame >= last_frame ) {
+                       eprintf(_("no video frames in file"));
+                       last_frame = start_frame = 0;
+               }
+               asset->video_length = last_frame - start_frame;
+       }
+       else {
+               printf("FileOGG: Cannot find the video length\n");
+               return 1;
+       }
+       asset->layers = 1;
+       asset->width = ti.pic_width;
+       asset->height = ti.pic_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;
+
+       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);
        return 0;
 }
 
-int FileOGG::ogg_get_prev_page(sync_window_t *sw, long serialno, ogg_page *og)
+int FileOGG::decode_vorbis_init()
 {
-       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;
+       ogg_stream_reset(&vo);
+       vorbis_synthesis_init(&vd, &vi);
+       vorbis_block_init(&vd, &vb);
+       audiosync = new sync_window_t(inp, file_lock, file_begin, file_end);
+       ogg_page og;
+       int ret = audiosync->ogg_get_first_page(vo.serialno, &og);
+       if( ret <= 0 ) {
+               eprintf(_("cannot read audio page from file"));
+               return 1;
+       }
+       // audio data starts here
+       audiosync->file_begin = audiosync->pagpos;
+       // audio samples starts here
+       start_sample = ogg_sample_pos(&og);
+//printf("start sample = %jd, gpos %jd, begins %jd\n",
+// start_sample, ogg_page_granulepos(&og), audiosync->file_begin);
+       ret = audiosync->ogg_get_last_page(vo.serialno, &og);
+       last_sample = ret > 0 ? ogg_next_sample_pos(&og) : 0;
+       asset->audio_length = last_sample - start_sample;
+       if( asset->audio_length <= 0 ) {
+               eprintf(_("no audio samples in file"));
+               asset->audio_length = 0;
+               last_sample = start_sample;
+       }
 
-//     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;
+       asset->channels = vi.channels;
+       if( !asset->sample_rate )
+               asset->sample_rate = vi.rate;
+       asset->audio_data = 1;
 
-// 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;
+       ogg_sample_position = -10;
+       set_audio_position(0); // make sure seeking is done to the first sample
+       strncpy(asset->acodec, "vorb", 4);
+       return 0;
+}
 
-//                             if (filepos == 0)
-//                                     printf("BBBb page_len: %i\n", page_length);
+int FileOGG::ogg_init_decode(FILE *inp)
+{
+       if( !inp ) return 1;
+       this->inp = inp;
+       struct stat file_stat; /* get file length */
+       file_end = stat(asset->path, &file_stat)>=0 ? file_stat.st_size : 0;
+       if( file_end < mn_pagesz ) return 1;
+       fseek(inp, 0, SEEK_SET);
+       vorbis_info_init(&vi);
+       vorbis_comment_init(&vc);
+       th_comment_init(&tc);
+       th_info_init(&ti);
+       ogg_page og;
+       ogg_packet op;
+       sync_window_t sy(inp, file_lock, 0, file_end);
+       int ret = sy.ogg_read_buffer_at(0, READ_SIZE);
+       if( ret < mn_pagesz ) return 1;
+       if( !sy.ogg_sync_and_take_page_out(&og) ) return 1;
+       ogg_stream_state tst;
+
+       while( ogg_page_bos(&og) ) {
+               ogg_stream_init(&tst, ogg_page_serialno(&og));
+               ogg_stream_pagein(&tst, &og);
+               if( ogg_stream_packetout(&tst, &op) ) {
+                       if( !video && th_decode_headerin(&ti, &tc, &ts, &op) >=0 ) {
+                               ogg_stream_init(&to, ogg_page_serialno(&og));
+                               video = 1;
                        }
-//                     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));
+                       else if( !audio && vorbis_synthesis_headerin(&vi, &vc, &op) >=0 ) {
+                               ogg_stream_init(&vo, ogg_page_serialno(&og));
+                               audio = 1;
                        }
                }
-//             printf("fpo: %i\n", first_page_offset);
-               filepos += first_page_offset - READ_SIZE;
+               ogg_stream_clear(&tst);
+               ret = sy.ogg_take_page_out_autoadvance(&og);
        }
 
-//     printf("finished\n");
-       if (done)
+       if( !ret || !video && !audio )
                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));
+       // expecting more a/v header packets
+       int vpkts = video ? 2 : 0;
+       int apkts = audio ? 2 : 0;
+       int retries = 100;
+       ret = 0;
+       while( --retries >= 0 && !ret && (vpkts || apkts) ) {
+               if( vpkts && ogg_page_serialno(&og) == to.serialno ) {
+                       ogg_stream_init(&tst, to.serialno);
+                       ogg_stream_pagein(&tst, &og);
+                       while( !ret && vpkts > 0 ) {
+                               while( (ret=ogg_stream_packetout(&tst, &op)) < 0 );
+                               if( !ret ) break;
+                               --vpkts;
+                               ret = !th_decode_headerin(&ti, &tc, &ts, &op) ? 1 : 0;
                        }
+                       if( ret )
+                               printf("theora header error\n");
+                       ogg_stream_clear(&tst);
                }
-               filepos = filepos + first_page_offset - READ_SIZE;
-       }
+               else if( apkts && ogg_page_serialno(&og) == vo.serialno ) {
+                       ogg_stream_init(&tst, vo.serialno);
+                       ogg_stream_pagein(&tst, &og);
+                       while( !ret && apkts > 0 ) {
+                               while( (ret=ogg_stream_packetout(&tst, &op)) < 0 );
+                               if( !ret ) break;
+                               --apkts;
+                               ret = vorbis_synthesis_headerin(&vi, &vc, &op) ? 1 : 0;
+                       }
+                       if( ret )
+                               printf("vorbis header error\n");
+                       ogg_stream_clear(&tst);
+               }
+               if( !ret && !sy.ogg_take_page_out_autoadvance(&og) )
+                       ret = 1;
+               if( ret )
+                       printf("incomplete headers\n");
 
-       if (done)
+       }
+// find first start packet (not continued) with data
+       int64_t start_pos = sy.bufpos - (og.header_len + og.body_len);
+       if( !ret ) {
+               while( --retries >= 0 && !ret && !ogg_page_packets(&og) ) {
+                       if( !ogg_page_continued(&og) )
+                               start_pos = sy.bufpos - (og.header_len + og.body_len);
+                       if( !sy.ogg_take_page_out_autoadvance(&og) ) ret = 1;
+               }
+               if( ret )
+                       printf("no data past headers\n");
+               if( audio && apkts )
+                       printf("missed %d audio headers\n",apkts);
+               if( video && vpkts )
+                       printf("missed %d video headers\n",vpkts);
+       }
+       if( retries < 0 || ret || (audio && apkts) || (video && vpkts) ) {
+               eprintf(_("Error in headers"));
                return 1;
-       else
-               return 0;
-}
+       }
+       // headers end here
+       file_begin = start_pos;
 
-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));
+       if( video && decode_theora_init() )
+               return 1;
+       if( audio && decode_vorbis_init() )
+               return 1;
+       return 0;
 }
 
-int FileOGG::ogg_seek_to_databegin(sync_window_t *sw, long serialno)
+void FileOGG::close_encoder()
 {
+// flush streams
+       if( audio )
+               write_samples_vorbis(0, 0, 1);
+       if( video )
+               write_frames_theora(0, 1, 1);
+       flush_ogg(1);
 
-//     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);
+       if( audio ) {
+               vorbis_block_clear(&vb);
+               vorbis_dsp_clear(&vd);
+               vorbis_comment_clear(&vc);
+               vorbis_info_clear(&vi);
+               ogg_stream_clear(&vo);
+               audio = 0;
+       }
+       if( video ) {
+               th_comment_clear(&tc);
+               ogg_stream_clear(&to);
+               video = 0;
+       }
+       if( enc ) {
+               th_encode_free(enc);
+               enc = 0;
+       }
+       if( out ) {
+               fclose(out);
+               out = 0;
+       }
 }
 
-int FileOGG::ogg_get_next_page(sync_window_t *sw, long serialno, ogg_page *og)
+void FileOGG::close_decoder()
 {
-       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;
-               }
+       if( audio ) {
+               for( int i=0; i<pcm_channels; ++i )
+                       delete [] pcm_history[i];
+               pcm_channels = 0;
+               delete [] pcm_history;  pcm_history = 0;
+
+               vorbis_dsp_clear(&vd);
+               vorbis_info_clear(&vi);
+               vorbis_block_clear(&vb);
+               vorbis_comment_clear(&vc);
+               ogg_stream_clear(&vo);
+               delete audiosync;  audiosync = 0;
+               audio = 0;
+       }
+       if( video ) {
+               th_info_clear(&ti);
+               th_setup_free(ts);  ts = 0;
+               th_comment_clear(&tc);
+               ogg_stream_clear(&to);
+               delete videosync;  videosync = 0;
+               video = 0;
+       }
+       if( dec ) {
+               th_decode_free(dec);
+               dec = 0;
+       }
+       if( inp ) {
+               fclose(inp);
+               inp = 0;
        }
-       return 0;
 }
 
-int FileOGG::ogg_sync_and_get_next_page(sync_window_t *sw, long serialno, ogg_page *og)
+
+
+void FileOGG::get_parameters(BC_WindowBase *parent_window, Asset *asset,
+       BC_WindowBase* &format_window, int audio_options,
+       int video_options, EDL *edl)
 {
-// TODO: Put better error reporting inhere
-       int ret;
-       while ((ret = sync_and_take_page_out(sw, og)) < 0)
+       if(audio_options)
        {
-               // do nothing;
+               OGGConfigAudio *window = new OGGConfigAudio(parent_window, asset);
+               format_window = window;
+               window->create_objects();
+               window->run_window();
+               delete window;
        }
-       if (ret == 0)
-               return 0;
-       if (ogg_page_serialno(og) == serialno)
+       else
+       if(video_options)
        {
-               sw->file_pagepos_found = sw->file_pagepos - og->header_len - og->body_len;
-               return 1;
+               OGGConfigVideo *window = new OGGConfigVideo(parent_window, asset);
+               format_window = window;
+               window->create_objects();
+               window->run_window();
+               delete window;
        }
-       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)
+
+
+
+int sync_window_t::ogg_take_page_out_autoadvance(ogg_page *og)
 {
-// 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);
+       for(;;) {
+               int ret = ogg_sync_pageout(this, og);
+               if( ret < 0 ) {
+                       printf("FileOGG: Lost sync reading input file\n");
+                       return 0;
                }
-               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);
+               if( ret > 0 ) {
+                       bufpos += og->header_len + og->body_len;
+                       return ret;
+               }
+               // need more data for page
+               if( !ogg_read_buffer(READ_SIZE) ) {
+                       printf("FileOGG: Read past end of input file\n");
+                       return 0;  // No more data
                }
-       // 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)
+int FileOGG::check_sig(Asset *asset)
 {
-       // 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));
+       FILE *fp = fopen(asset->path, "rb");
+       if( !fp ) return 0;
+// Test for "OggS"
+       fseek(fp, 0, SEEK_SET);
+       char data[4];
+       int ret = fread(data, 4, 1, fp) == 1 &&
+               data[0] == 'O' && data[1] == 'g' &&
+               data[2] == 'g' && data[3] == 'S' ? 1 : 0;
+       fclose(fp);
+       return ret;
 
-       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);
+int FileOGG::open_file(int rd, int wr)
+{
+       int ret = 1;
+       if( wr ) {
+               if( !(out = fopen(asset->path, "wb")) ) {
+                       eprintf(_("Error while opening %s for writing. %m\n"), asset->path);
+                       return 1;
                }
-               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( (ret = ogg_init_encode(out)) && out ) {
+                       fclose(out);  out = 0;
                }
-               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;
-                               }
+       }
+       else if( rd ) {
+               if( !(inp = fopen(asset->path, "rb")) ) {
+                       eprintf(_("Error while opening %s for reading. %m\n"), asset->path);
+                       return 1;
+               }
+               if( (ret = ogg_init_decode(inp)) && inp ) {
+                       fclose(inp);  inp = 0;
+               }
+       }
+       return ret;
+}
 
-                       }
+int FileOGG::close_file()
+{
+       if( file->wr )
+               close_encoder();
+       else if( file->rd )
+               close_decoder();
+       return 0;
+}
 
-               }
+
+int64_t FileOGG::ogg_sample_pos(ogg_page *og)
+{
+       ogg_packet op;
+       ogg_stream_state ss;
+       ogg_stream_init(&ss, vo.serialno);
+       ogg_stream_pagein(&ss, og);
+       int64_t bsz = 0;
+       long prev = -1;
+       int ret = 0;
+       while( (ret=ogg_stream_packetout(&ss, &op)) ) {
+               if( ret < 0 ) continue; // ignore holes
+               long sz =  vorbis_packet_blocksize(&vi, &op);
+               if( prev != -1 ) bsz += (prev + sz) >> 2;
+               prev = sz;
        }
+       ogg_stream_clear(&ss);
+       return ogg_next_sample_pos(og) - bsz;
+}
 
+int64_t FileOGG::ogg_next_sample_pos(ogg_page *og)
+{
+       return ogg_page_granulepos(og);
+}
 
-       return 0;
+int64_t FileOGG::ogg_frame_pos(ogg_page *og)
+{
+       int64_t pos = th_granule_frame(dec, ogg_page_granulepos(og)) - ogg_page_packets(og);
+       if( ogg_page_continued(og) ) --pos;
+       return pos;
 }
 
-int FileOGG::ogg_get_page_of_frame(sync_window_t *sw, long serialno, ogg_page *og, int64_t frame)
+int64_t FileOGG::ogg_next_frame_pos(ogg_page *og)
 {
-       if (frame >= asset->video_length + start_frame)
-       {
-               eprintf(_("FileOGG: Illegal seek beyond end of frames\n"));
+       return th_granule_frame(dec, ogg_page_granulepos(og)) + 1;
+}
+
+
+int FileOGG::ogg_get_page_of_sample(ogg_page *og, int64_t sample)
+{
+       if( sample >= asset->audio_length + start_sample ) {
+               printf(_("FileOGG: Illegal seek beyond end of samples\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"));
+// guess about position
+       int64_t file_length =  audiosync->file_end - audiosync->file_begin;
+       off_t guess = file_length * (sample - start_sample) /
+               asset->audio_length - SEEK_SIZE;
+       if( guess < 0 ) guess = 0;
+       guess += audiosync->file_begin;
+       audiosync->ogg_read_buffer_at(guess, READ_SIZE);
+       if( !audiosync->ogg_sync_and_get_next_page(vo.serialno, og) ) {
+               printf(_("FileOGG: llegal seek no pages\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
-
-
+        int ret = 1;
+       while( ret && (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) )
+               ret = videosync->ogg_get_next_page(to.serialno, og);
+       if( !ret ) return 0;
        // 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));
+       int missp = 0, missm = 0;
+       int64_t next_pos = ogg_next_sample_pos(og);
+       if( sample >= next_pos ) { // scan forward
+               while( sample >= next_pos ) {
+                       while( !(ret=audiosync->ogg_get_next_page(vo.serialno, og)) &&
+                               (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) );
+                       if( !ret ) break;
+                       next_pos = ogg_next_sample_pos(og);
+                       ++missp;
+//printf("audio %jd next %jd %jd\n", sample, ogg_sample_pos(og), next_pos);
                }
-               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--;
+       }
+       else { // scan backward
+               int64_t pos = ogg_sample_pos(og);
+               while( sample < pos ) {
+                       while( (ret=audiosync->ogg_get_prev_page(vo.serialno, og)) &&
+                               (ogg_page_continued(og) && ogg_page_packets(og) == 1) );
+                       if( !ret ) break;
+                       ++missm;
+                       pos = ogg_sample_pos(og);
+//printf("audio %jd prev %jd %jd\n", sample, pos, ogg_next_sample_pos(og));
                }
        }
-//     printf("Miss plus: %i, miss minus: %i\n", missp, missm);
-//     printf("last frame of page with frame : %lli\n", pageend_frame);
-       return 1;
+//printf("audio %d seek %jd, missp %d, missm %d  from %jd to %jd\n", ret,
+// sample, missp, missm, ogg_sample_pos(og), ogg_next_sample_pos(og));
+       return ret;
 }
 
-
-int FileOGG::ogg_seek_to_keyframe(sync_window_t *sw, long serialno, int64_t frame, int64_t *position)
+int FileOGG::ogg_seek_to_sample(int64_t ogg_sample)
 {
-//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"));
+       if( !ogg_get_page_of_sample(&og, ogg_sample) ) {
+               eprintf(_("Seeking to sample's page failed\n"));
                return 0;
        }
-       // find a page with packets
-       while( ogg_page_packets(&og) == 0 ) {
-               ogg_get_prev_page(sw, serialno, &og);
+       int ret = 1;
+       int64_t pos = ogg_sample_pos(&og);
+       int64_t next_pos = pos;
+       if( ogg_page_continued(&og) ) {
+               while( (ret=audiosync->ogg_get_prev_page(to.serialno, &og)) &&
+                       (ogg_page_packets(&og) == 0 && ogg_page_continued(&og)) );
        }
-       int64_t granulepos = ogg_page_granulepos(&og);
-       granulepos &= ~((1<<theora_keyframe_granule_shift)-1);
-       int64_t iframe = theora_granule_frame(&tf->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;
+       if( ret ) {
+               audio_eos = 0;
+               ogg_stream_reset(&vo);
+               ogg_stream_pagein(&vo, &og);
+               vorbis_synthesis_restart(&vd);
+               ret = ogg_get_audio_packet(&op);
        }
-       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);
+       if( ret && !vorbis_synthesis(&vb, &op) ) {
+               vorbis_synthesis_blockin(&vd, &vb);
+               if( vorbis_synthesis_pcmout(&vd, 0) )
+                       ret = 0;
        }
-
-       *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;
+       if( !ret ) {
+               eprintf(_("Something wrong while trying to seek\n"));
+               return 0;
        }
 
-       fclose(fd);
+       while( ogg_sample > next_pos ) {
+               if( !(ret=ogg_get_audio_packet(&op)) ) break;
+               if( vorbis_synthesis(&vb, &op) ) continue;
+               vorbis_synthesis_blockin(&vd, &vb);
+               pos = next_pos;
+               next_pos += vorbis_synthesis_pcmout(&vd, NULL);
+               if( next_pos > ogg_sample ) break;
+               // discard decoded data before current sample
+               vorbis_synthesis_read(&vd, (next_pos - pos));
+       }
+       if( ret ) {
+               audio_pos = next_pos;
+               vorbis_synthesis_read(&vd, (ogg_sample - pos));
+       }
+       return ret;
+}
 
-       return 0;
 
+int FileOGG::ogg_get_page_of_frame(ogg_page *og, int64_t frame)
+{
+       if( frame >= asset->video_length + start_frame ) {
+               eprintf(_("Illegal seek beyond end of frames\n"));
+               return 0;
+       }
+       if( frame < start_frame ) {
+               eprintf(_("Illegal seek before start of frames\n"));
+               return 0;
+       }
+       int64_t file_length = videosync->file_end - videosync->file_begin;
+       off_t guess = file_length * (frame - start_frame) /
+                asset->video_length - SEEK_SIZE;
+       if( guess < 0 ) guess = 0;
+       guess += videosync->file_begin;
+       videosync->ogg_read_buffer_at(guess, SEEK_SIZE);
+       videosync->ogg_sync_and_get_next_page(to.serialno, og);
+       // find the page with "real" ending
+       int ret = 1;
+       while( ret && (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) )
+              ret = videosync->ogg_get_next_page(to.serialno, og);
+       int64_t pos = ogg_next_frame_pos(og);
+       // linear search
+       int missp = 0, missm = 0;
+// move back if continued
+       if( frame >= pos ) {
+               do { // scan forward
+                       while( (ret=videosync->ogg_get_next_page(to.serialno, og)) &&
+                               ogg_page_packets(og) == 0 );
+                       if( !ret ) break;
+                       missp++;
+                       pos = ogg_next_frame_pos(og);
+//printf("video %jd next %jd %jd\n", frame, ogg_frame_pos(og), pos);
+               } while( frame >= pos );
+       }
+       else if( (pos=ogg_frame_pos(og)) > frame ) {
+               while( pos > start_frame && frame < pos ) { // scan backward
+                       while( (ret=videosync->ogg_get_prev_page(to.serialno, og)) &&
+                               ogg_page_packets(og) == 0 && ogg_page_continued(og) );
+                       if( !ret ) break;
+                       missm++;
+                       pos = ogg_frame_pos(og);
+//printf("video %jd next %jd %jd\n", frame, pos, ogg_next_frame_pos(og));
+               }
+       }
+//printf("video %d seek %jd, missp %d, missm %d first %jd, next %jd\n", ret,
+// frame, missp, missm, ogg_frame_pos(og), ogg_next_frame_pos(og));
+       return ret;
 }
 
-int FileOGG::close_file()
+int FileOGG::ogg_seek_to_keyframe(int64_t frame, int64_t *keyframe_number)
 {
-
-       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);
+//printf("ogg_seek_to_keyframe of === %jd\n", frame);
+       ogg_page og;
+       ogg_packet op;
+       int64_t ipagpos = -1;
+       int64_t istart = -1;
+       int64_t iframe = -1;
+       int ipkts = -1;
+       int retries = 1000, ret = 1;
+       while( --retries>=0 && frame>=start_frame ) {
+               if( !ogg_get_page_of_frame(&og, frame) ) break;
+               int64_t pos = ogg_frame_pos(&og);
+               istart = pos;
+               if( ogg_page_continued(&og) ) {
+                       while( (ret=videosync->ogg_get_prev_page(to.serialno, &og)) &&
+                               (ogg_page_packets(&og) == 0 && ogg_page_continued(&og)) );
                }
-               if (asset->video_data)
-               {
-                       theora_info_clear (&tf->ti);
-                       ogg_stream_clear (&tf->to);
-                       theora_clear (&tf->td);
+               int64_t pagpos = videosync->pagpos;
+               video_eos = 0;
+               ogg_stream_reset(&to);
+               ogg_stream_pagein(&to, &og);
+               int pkts = 0;
+               while( frame >= pos && (ret=ogg_get_video_packet(&op)) ) {
+                       if( th_packet_iskeyframe(&op) == 1 ) {
+                               ipagpos = pagpos;
+                               iframe = pos;
+                               ipkts = pkts;
+//printf("keyframe %jd pkts %d\n", pos, pkts);
+                       }
+//printf("packet %jd pkts %d is a %d\n", pos, pkts,  th_packet_iskeyframe(&op));
+                       ++pkts;  ++pos;
                }
-
-               if (stream) fclose(stream);
-               stream = 0;
+               if( ipagpos >= 0 ) break;
+               frame = istart - 1;
        }
-       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;
-
+       if( ipagpos < 0 ) {
+               printf(_("Seeking to keyframe %jd search failed\n"), frame);
+               return 0;
        }
-       return 0;
+       videosync->ogg_read_buffer_at(ipagpos, READ_SIZE);
+       videosync->ogg_sync_and_get_next_page(to.serialno, &og);
+       video_eos = 0;
+       ogg_stream_reset(&to);
+       ogg_stream_pagein(&to, &og);
+       video_pos = ogg_next_frame_pos(&og);
+// skip prev packets
+//     int ipkts = iframe - ogg_frame_pos(&og);
+//printf("iframe %jd, page %jd, ipkts %d\n", iframe, ogg_page_pageno(&og), ipkts);
+       while( --ipkts >= 0 )
+               ogg_get_video_packet(&op);
+       *keyframe_number = iframe;
+       return 1;
 }
 
-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()
 {
@@ -1413,37 +1218,54 @@ int FileOGG::get_best_colormodel(Asset *asset, int driver)
        return BC_YUV420P;
 }
 
-
-int FileOGG::read_frame(VFrame *frame)
+int FileOGG::set_audio_position(int64_t x)
 {
+       next_sample_position = x + start_sample;
+       return 0;
+}
 
-       if(!stream) return 1;
 
+int FileOGG::ogg_get_video_packet(ogg_packet *op)
+{
+       int ret = 1;
+       while( (ret=ogg_stream_packetout(&to, op)) <= 0 ) {
+               if( video_eos ) return 0;
+               ogg_page og;
+               if( !videosync->ogg_get_next_page(to.serialno, &og) ) break;
+               if( ogg_page_granulepos(&og) >= 0 )
+                       video_pos = ogg_next_frame_pos(&og);
+               ogg_stream_pagein(&to, &og);
+               video_eos = ogg_page_eos(&og);
+       }
+       if( ret <= 0 ) {
+               printf("FileOGG: Cannot read video packet\n");
+               return 0;
+       }
+       return 1;
+}
 
+int FileOGG::read_frame(VFrame *frame)
+{
+       if( !inp || !video ) return 1;
        // skip is cheaper than seek, do it...
        int decode_frames = 0;
        int expect_keyframe = 0;
-       if (ogg_frame_position >= 0 &&
+       ifogg_frame_position >= 0 &&
            next_frame_position >= ogg_frame_position &&
-           next_frame_position - ogg_frame_position < 32)
-       {
+           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"
+       }
+       else if( next_frame_position != ogg_frame_position ) {
+               if( !ogg_seek_to_keyframe(next_frame_position, &ogg_frame_position) ) {
+                       eprintf(_("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,"
+               decode_frames = next_frame_position - ogg_frame_position + 1;
+               --ogg_frame_position;
+               if( decode_frames <= 0 ) {
+                       eprintf(_("Error while seeking to keyframe,"
                                " wrong keyframe number (frame: %jd, keyframe: %jd)\n"),
                                next_frame_position, ogg_frame_position);
                        return 1;
@@ -1451,127 +1273,86 @@ int FileOGG::read_frame(VFrame *frame)
                }
                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))
-               {
+       int ret = 0;
+       ogg_packet op;
+       while( decode_frames > 0 ) {
+               if( !ogg_get_video_packet(&op) ) break;
+               if( expect_keyframe ) {
+                       expect_keyframe = 0;
+                       if( th_packet_iskeyframe(&op) <= 0 )
                                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 ++;
+               ogg_int64_t granpos = 0;
+               if( th_decode_packetin(dec, &op, &granpos) >= 0 )
+                       ret = 1;
+               ++ogg_frame_position;
+               --decode_frames;
        }
-       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);
+//if(ret < 0 )printf("ret = %d\n", ret);
+       if( ret > 0 ) {
+               th_ycbcr_buffer ycbcr;
+               ret = th_decode_ycbcr_out(dec, ycbcr);
+               if( ret ) {
+                       eprintf(_("th_decode_ycbcr_out failed with code %i\n"), ret);
+                       ret = 0; // not always fatal
                }
+               uint8_t *yp = ycbcr[0].data;
+               uint8_t *up = ycbcr[1].data;
+               uint8_t *vp = ycbcr[2].data;
+               int yst = ycbcr[0].stride;
+               int yw = ycbcr[0].width;
+               int yh = ycbcr[0].height;
+               VFrame temp_frame(yp, -1, 0, up-yp, vp-yp, yw,yh, BC_YUV420P, yst);
+               int px = ti.pic_x, py = ti.pic_y;
+               int pw = ti.pic_width, ph = ti.pic_height;
+               frame->transfer_from(&temp_frame, -1, px, py, pw, ph);
+       }
 
-// 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;
+       next_frame_position++;
+       return ret;
 }
 
 
-
-int FileOGG::ogg_decode_more_samples(sync_window_t *sw, long serialno)
+int FileOGG::ogg_get_audio_packet(ogg_packet *op)
 {
-       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);
-               }
+       int ret = 1;
+       while( (ret=ogg_stream_packetout(&vo, op)) <= 0 ) {
+               if( audio_eos ) return 0;
+               ogg_page og;
+               if( !audiosync->ogg_get_next_page(vo.serialno, &og) ) break;
+               if( ogg_page_granulepos(&og) >= 0 )
+                       audio_pos = ogg_next_sample_pos(&og);
+               ogg_stream_pagein(&vo, &og);
+               audio_eos = ogg_page_eos(&og);
+       }
+       if( ret <= 0 ) {
+               printf("FileOGG: Cannot read audio packet\n");
+               return 0;
        }
        return 1;
 }
 
-int FileOGG::set_audio_position(int64_t x)
+int FileOGG::ogg_decode_more_samples()
 {
-       next_sample_position = x + start_sample;
+       ogg_packet op;
+       while( ogg_get_audio_packet(&op) ) {
+               if( !vorbis_synthesis(&vb, &op) ) {
+                       vorbis_synthesis_blockin(&vd, &vb);
+                       return 1;
+               }
+       }
+       ogg_sample_position = -11;
+       eprintf(_("Cannot find next page while trying to decode more samples\n"));
        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);
+               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;
@@ -1581,24 +1362,18 @@ int FileOGG::move_history(int from, int to, int len)
 int FileOGG::read_samples(double *buffer, int64_t len)
 {
        float **vorbis_buffer;
-       if (len <= 0)
+       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)
-       {
+       if( len > HISTORY_MAX ) {
                eprintf(_("max samples=%d\n"), HISTORY_MAX);
                return 1;
        }
 
-       if(!pcm_history)
-       {
+       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_start = -100000000;
                history_size = 0;
        }
 
@@ -1607,37 +1382,28 @@ int FileOGG::read_samples(double *buffer, int64_t len)
        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");
+       if( history_start < next_sample_position &&
+           history_start + history_size > next_sample_position &&
+           history_start + history_size < next_sample_position + len ) {
                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");
+               move_history(next_sample_position - history_start, 0, hole_start);
+       }
+       else if( next_sample_position < history_start &&
+                history_start < next_sample_position + len ) {
                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);
+                       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");
+       }
+       else if( next_sample_position >= history_start + history_size ||
+                next_sample_position + len <= history_start ) {
                hole_fill = 1;
                hole_start = 0;
                hole_len = HISTORY_MAX;
@@ -1646,82 +1412,65 @@ int FileOGG::read_samples(double *buffer, int64_t len)
                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"));
+       if( hole_fill ) {
+               if( hole_start < 0 || hole_len <= 0 || hole_absstart < 0 ) {
+                       eprintf(_("Error in finding read file position\n"));
                        return 1;
                }
 
-               if (hole_absstart + hole_len > asset->audio_length + start_sample)
-               {
+               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
-               {
+               }
+               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)
-               {
+               if( ogg_sample_position != hole_absstart ) {
                        ogg_sample_position = hole_absstart;
-                       if (!ogg_seek_to_sample(tf->audiosync, tf->vo.serialno, ogg_sample_position))
-                       {
+                       if( !ogg_seek_to_sample(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];
+               while( samples_read < hole_len ) {
+                       int64_t samples_waiting = vorbis_synthesis_pcmout(&vd, &vorbis_buffer);
+                       int64_t samples_avail = !samples_waiting && audio_eos ?
+                               hole_len - samples_read : // silence after eos
+                               samples_waiting ;
+                       int64_t sample_demand = samples_to_read - samples_read;
+                       int64_t sample_count = MIN(samples_avail, sample_demand);
+                       if( sample_count > 0 ) {
+                               int sz = sample_count*sizeof(float);
+                               if( samples_waiting ) {
+                                       for( int i=0; i<asset->channels; ++i ) {
+                                               float *input = vorbis_buffer[i];
+                                               float *output = pcm_history[i] + hole_start;
+                                               memcpy(output, input, sz);
                                        }
+                                       vorbis_synthesis_read(&vd, sample_count);
                                }
-                       }
-
-                       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;
+                               else {
+                                       for( int i=0; i<asset->channels; ++i ) {
+                                               float *output = pcm_history[i] + hole_start;
+                                               memset(output, 0, sz);
+                                       }
                                }
-
-
+                               ogg_sample_position += sample_count;
+                               hole_start += sample_count;
+                               samples_read += sample_count;
+                               if( samples_read >= hole_len ) break;
+                       }
+                       if( samples_read < hole_len && !ogg_decode_more_samples() )
+                               break;
                }
        }
 
-       // 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)
-       {
+       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);
@@ -1738,164 +1487,89 @@ int FileOGG::read_samples(double *buffer, int64_t len)
 
 int FileOGG::write_audio_page()
 {
-       int ret;
-
-       ret = fwrite(tf->apage, 1, tf->apage_len, stream);
-       if(ret < tf->apage_len)
-       {
+       int ret = apage.write_page(out);
+       if( ret < 0 )
                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)
-       {
+       int ret = vpage.write_page(out);
+       if( ret < 0 )
                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)
+// flush out the ogg pages
+void FileOGG::flush_ogg(int last)
 {
-       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; i<asset->channels; i++)
-               {
-                       for (int j = 0; j < len; j++)
-                       {
-                               vorbis_buffer[i][j] = buffer[i][j];
+       ogg_page og;
+       file_lock->lock("FileOGG::flush_ogg");
+       for(;;) {
+// this way seeking is much better, (per original fileogg)
+// not sure if 32 packets is a good value.
+               int mx_pkts = 32;
+               if( video && !vpage.valid ) {
+                       if( (vpage.packets > mx_pkts && ogg_stream_flush(&to, &og) > 0) ||
+                           ogg_stream_pageout(&to, &og) > 0 ) {
+                               videotime = th_granule_time(enc, vpage.load(&og));
+                       }
+               }
+               if( audio && !apage.valid ) {
+                       if( (apage.packets > mx_pkts && ogg_stream_flush(&vo, &og) > 0) ||
+                           ogg_stream_pageout(&vo, &og) > 0 ) {
+                               audiotime = vorbis_granule_time(&vd, apage.load(&og));
                        }
                }
-               vorbis_analysis_wrote (&tf->vd, len);
+               if( !audio && vpage.valid )
+                       write_video_page();
+               else if( !video && apage.valid )
+                       write_audio_page();
+               else if( !vpage.valid || !apage.valid )
+                       break;
+// output earliest page
+               else if( videotime > audiotime ) // output earliest
+                       write_audio_page();
+               else
+                       write_video_page();
        }
-       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);
+       if( last ) {  // at last
+               if( vpage.valid )
+                       write_video_page();
+               if( apage.valid )
+                       write_audio_page();
+       }
+       file_lock->unlock();
+}
 
-           /* 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();
-           }
 
-       }
+int FileOGG::write_samples_vorbis(double **buffer, int64_t len, int last)
+{
+       if( !audio || !out ) return 1;
        flush_ogg(0);
-       return 0;
-
+       if( !last ) {
+               float **vorbis_buffer = vorbis_analysis_buffer(&vd, len);
+               for( int i=0; i<asset->channels; ++i ) // double to float
+                       for( int j=0; j < len; ++j )
+                               vorbis_buffer[i][j] = buffer[i][j];
+       }
+       else
+               len = 0;
+       vorbis_analysis_wrote(&vd, len);
 
+       while( vorbis_analysis_blockout(&vd, &vb) == 1 ) {
+               vorbis_analysis(&vb, 0);
+               vorbis_bitrate_addblock(&vb);
+               ogg_packet op;
+               while( vorbis_bitrate_flushpacket(&vd, &op) ) {
+                       file_lock->lock("FileOGG::write_vorbis_audio");
+                       ogg_stream_packetin(&vo, &op);
+                       ++apage.packets;
+                       file_lock->unlock();
+               }
+       }
+       return 0;
 }
 
 int FileOGG::write_samples(double **buffer, int64_t len)
@@ -1905,108 +1579,64 @@ int FileOGG::write_samples(double **buffer, int64_t len)
        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);
+int FileOGG::write_frames_theora(VFrame ***frames, int len, int last)
+{
+       if( !video || !out ) return 1;
+       for( int j=0; j<len; ++j ) {
+               flush_ogg(0);
+               if( temp_frame ) {
+                       th_ycbcr_buffer ycbcr;
+                       ycbcr[0].width = frame_w;
+                       ycbcr[0].height = frame_h;
+                       ycbcr[0].stride = temp_frame->get_bytes_per_line();
+                       ycbcr[0].data = temp_frame->get_y();
+                       ycbcr[1].width = frame_w/2;
+                       ycbcr[1].height = frame_h/2;
+                       ycbcr[1].stride = (temp_frame->get_bytes_per_line()+1)/2;
+                       ycbcr[1].data = temp_frame->get_u();
+                       ycbcr[2].width = frame_w/2;
+                       ycbcr[2].height = frame_h/2;
+                       ycbcr[2].stride = (temp_frame->get_bytes_per_line()+1)/2;
+                       ycbcr[2].data = temp_frame->get_v();
+                       if( th_encode_ycbcr_in(enc, ycbcr) ) {
+                               eprintf(_("th_encode_ycbcr_in failed"));
+                               return 1;
+                       }
+                       ogg_packet op;
+                       while( th_encode_packetout(enc, last, &op) > 0 ) {
+                               file_lock->lock();
+                               ogg_stream_packetin (&to, &op);
+                               ++vpage.packets;
+                               file_lock->unlock();
                        }
-                       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);
                }
+               if( last ) return 0;
+               if( !temp_frame )
+                       temp_frame = new VFrame (0, -1, frame_w, frame_h, theora_cmodel, -1);
                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());
-
-               }
+               temp_frame->transfer_from(frame);
        }
-
        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"),
+ : BC_Window(PROGRAM_NAME ": Audio Compression",
        parent_window->get_abs_cursor_x(1),
        parent_window->get_abs_cursor_y(1),
-       350, 250)
+       xS(350), yS(250))
 {
        this->parent_window = parent_window;
        this->asset = asset;
+// *** CONTEXT_HELP ***
+       context_help_set_keyword("Single File Rendering");
 }
 
 OGGConfigAudio::~OGGConfigAudio()
@@ -2018,25 +1648,27 @@ 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;
+       int x = xS(10), y = yS(10);
+       int x1 = xS(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));
+       add_tool(variable_bitrate = new OGGVorbisVariableBitrate(x + fixed_bitrate->get_w() + xS(5),
+               y,
+               this));
 
-       y += 30;
+       y += yS(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;
+       y += yS(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;
+       y += yS(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));
@@ -2082,7 +1714,7 @@ OGGVorbisMinBitrate::OGGVorbisMinBitrate(int x,
        int y,
        OGGConfigAudio *gui,
        char *text)
- : BC_TextBox(x, y, 180, 1, text)
+ : BC_TextBox(x, y, xS(180), 1, text)
 {
        this->gui = gui;
 }
@@ -2098,7 +1730,7 @@ OGGVorbisMaxBitrate::OGGVorbisMaxBitrate(int x,
        int y,
        OGGConfigAudio *gui,
        char *text)
- : BC_TextBox(x, y, 180, 1, text)
+ : BC_TextBox(x, y, xS(180), 1, text)
 {
        this->gui = gui;
 }
@@ -2111,7 +1743,7 @@ int OGGVorbisMaxBitrate::handle_event()
 
 
 OGGVorbisAvgBitrate::OGGVorbisAvgBitrate(int x, int y, OGGConfigAudio *gui, char *text)
- : BC_TextBox(x, y, 180, 1, text)
+ : BC_TextBox(x, y, xS(180), 1, text)
 {
        this->gui = gui;
 }
@@ -2126,13 +1758,15 @@ int OGGVorbisAvgBitrate::handle_event()
 
 
 OGGConfigVideo::OGGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
- : BC_Window(_(PROGRAM_NAME ": Video Compression"),
+ : BC_Window(PROGRAM_NAME ": Video Compression",
        parent_window->get_abs_cursor_x(1),
        parent_window->get_abs_cursor_y(1),
-       450, 220)
+       xS(450), yS(220))
 {
        this->parent_window = parent_window;
        this->asset = asset;
+// *** CONTEXT_HELP ***
+       context_help_set_keyword("Single File Rendering");
 }
 
 OGGConfigVideo::~OGGConfigVideo()
@@ -2143,42 +1777,43 @@ 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;
+       int x = xS(10), y = yS(10);
+       int x1 = x + xS(150);
+       int x2 = x + xS(300);
 
        lock_window("OGGConfigVideo::create_objects");
-       add_subwindow(new BC_Title(x, y + 5, _("Bitrate:")));
-       add_subwindow(new OGGTheoraBitrate(x1, y, this));
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(x, y, _("Bitrate:")));
+       add_subwindow(new OGGTheoraBitrate(x + title->get_w() + xS(5), y, this));
        add_subwindow(fixed_bitrate = new OGGTheoraFixedBitrate(x2, y, this));
-       y += 30;
+       y += yS(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(new BC_ISlider(x + xS(80), y, 0, xS(200), xS(200),
+               0, 63, asset->theora_quality,
+               0, 0, &asset->theora_quality));
+
 
        add_subwindow(fixed_quality = new OGGTheoraFixedQuality(x2, y, this));
-       y += 30;
+       y += yS(30);
 
        add_subwindow(new BC_Title(x, y, _("Keyframe frequency:")));
        OGGTheoraKeyframeFrequency *keyframe_frequency =
-               new OGGTheoraKeyframeFrequency(x1 + 60, y, this);
+               new OGGTheoraKeyframeFrequency(x1 + xS(60), y, this);
        keyframe_frequency->create_objects();
-       y += 30;
+       y += yS(30);
 
        add_subwindow(new BC_Title(x, y, _("Keyframe force frequency:")));
        OGGTheoraKeyframeForceFrequency *keyframe_force_frequency =
-               new OGGTheoraKeyframeForceFrequency(x1 + 60, y, this);
+               new OGGTheoraKeyframeForceFrequency(x1 + xS(60), y, this);
        keyframe_force_frequency->create_objects();
-       y += 30;
+       y += yS(30);
 
        add_subwindow(new BC_Title(x, y, _("Sharpness:")));
        OGGTheoraSharpness *sharpness =
-               new OGGTheoraSharpness(x1 + 60, y, this);
+               new OGGTheoraSharpness(x1 + xS(60), y, this);
        sharpness->create_objects();
-       y += 30;
+       y += yS(30);
 
 
        add_subwindow(new BC_OKButton(this));
@@ -2187,6 +1822,8 @@ void OGGConfigVideo::create_objects()
 }
 
 
+
+
 int OGGConfigVideo::close_event()
 {
        set_done(0);
@@ -2194,7 +1831,7 @@ int OGGConfigVideo::close_event()
 }
 
 OGGTheoraBitrate::OGGTheoraBitrate(int x, int y, OGGConfigVideo *gui)
- : BC_TextBox(x, y, 100, 1, gui->asset->theora_bitrate)
+ : BC_TextBox(x, y, xS(100), 1, gui->asset->theora_bitrate)
 {
        this->gui = gui;
 }
@@ -2239,13 +1876,8 @@ int OGGTheoraFixedQuality::handle_event()
 };
 
 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)
+ : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency,
+       (int64_t)1, (int64_t)500, x, y, xS(40))
 {
        this->gui = gui;
 }
@@ -2257,13 +1889,8 @@ int OGGTheoraKeyframeFrequency::handle_event()
 }
 
 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)
+ : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency,
+       (int64_t)1, (int64_t)500, x, y, xS(40))
 {
        this->gui = gui;
 }
@@ -2276,13 +1903,8 @@ int OGGTheoraKeyframeForceFrequency::handle_event()
 
 
 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)
+ : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_sharpness,
+       (int64_t)0, (int64_t)2, x, y, xS(40))
 {
        this->gui = gui;
 }
@@ -2294,345 +1916,4 @@ int OGGTheoraSharpness::handle_event()
 }
 
 
-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<char*> *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;
-}
-
-
-
+#endif