just a few adjustments from Andrew
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / fileogg.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20 #ifdef HAVE_OGG
21
22 #include "asset.h"
23 #include "bcsignals.h"
24 #include "byteorder.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "file.h"
28 #include "fileogg.h"
29 #include "guicast.h"
30 #include "interlacemodes.h"
31 #include "language.h"
32 #include "mainerror.h"
33 #include "mutex.h"
34 #include "mwindow.inc"
35 #include "preferences.h"
36 #include "render.h"
37 #include "vframe.h"
38 #include "versioninfo.h"
39 #include "videodevice.inc"
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <errno.h>
47
48 /* This code was aspired by ffmpeg2theora */
49 /* Special thanks for help on this code goes out to j@v2v.cc */
50
51
52 #define READ_SIZE 4*66000
53 #define SEEK_SIZE 2*66000
54
55 sync_window_t::sync_window_t(FILE *fp, Mutex *sync_lock, int64_t begin, int64_t end)
56 {
57         ogg_sync_init(this);
58         this->fp = fp;
59         this->sync_lock = sync_lock;
60         this->file_begin = begin;
61         this->file_end = end;
62         filepos = -1;
63         bufpos = -1;
64         pagpos = -1;
65 }
66
67 sync_window_t::~sync_window_t()
68 {
69         ogg_sync_clear(this);
70 }
71
72 int sync_window_t::ogg_read_locked(int buflen)
73 {
74         char *buffer = ogg_sync_buffer(this, buflen);
75         int len = fread(buffer, 1, buflen, fp);
76         ogg_sync_wrote(this, len);
77         filepos += len;
78         return len;
79 }
80
81 int sync_window_t::ogg_read_buffer(int buflen)
82 {
83         sync_lock->lock("sync_window_t::ogg_read_buffer_at");
84         fseeko(fp, filepos, SEEK_SET);
85         int len = ogg_read_locked(buflen);
86         sync_lock->unlock();
87         return len;
88 }
89
90 int sync_window_t::ogg_read_buffer_at(off_t filepos, int buflen)
91 {
92         if( bufpos == filepos && buflen == this->filepos - bufpos )
93                 return buflen;
94         sync_lock->lock("sync_window_t::ogg_read_buffer_at");
95         this->bufpos = filepos;
96         fseeko(fp, filepos, SEEK_SET);
97         this->filepos = filepos;
98         ogg_sync_reset(this);
99         int ret = ogg_read_locked(buflen);
100         sync_lock->unlock();
101         return ret;
102 }
103
104 // we never need to autoadvance when syncing, since our read chunks are larger than
105 // maximum page size
106 int sync_window_t::ogg_sync_and_take_page_out(ogg_page *og)
107 {
108         og->header_len = 0;
109         og->body_len = 0;
110         og->header = 0;
111         og->body = 0;
112         int ret = ogg_sync_pageseek(this, og);
113         bufpos += abs(ret); // can be zero
114         return ret;
115 }
116
117 int sync_window_t::ogg_sync_and_get_next_page(long serialno, ogg_page *og)
118 {
119         int ret = 0, retries = 1000;
120         while( --retries >= 0 && (ret = ogg_sync_and_take_page_out(og)) < 0 );
121         if( ret >= mn_pagesz && ogg_page_serialno(og) != serialno )
122                 ret = ogg_get_next_page(serialno, og);
123         if( ret ) {
124                 pagpos = bufpos - (og->header_len + og->body_len);
125                 return 1;
126         }
127         return 0;
128 }
129
130 int sync_window_t::ogg_get_next_page(long serialno, ogg_page *og)
131 {
132         int ret = 0, retries = 1000;
133         while( --retries >= 0 && (ret=ogg_take_page_out_autoadvance(og)) &&
134                 ogg_page_serialno(og) != serialno );
135         if( ret ) {
136                 pagpos = bufpos - (og->header_len + og->body_len);
137 }
138         else
139                 printf("ogg_get_next_page missed\n");
140         return ret;
141 }
142
143 int sync_window_t::ogg_prev_page_search(long serialno, ogg_page *og,
144                 off_t begin, off_t end)
145 {
146         ogg_page page;
147         int retries = 100, ret = 0;
148         int64_t ppos = -1;
149         while( ppos < 0 && --retries >= 0 ) {
150                 int64_t fpos = end;
151                 int read_len = SEEK_SIZE;
152                 fpos -= read_len;
153                 if( fpos < begin ) {
154                         read_len += fpos - begin;
155                         if( read_len <= 0 ) break;
156                         fpos = begin;
157                 }
158                 read_len = ogg_read_buffer_at(fpos, read_len);
159                 if( read_len <= 0 ) return 0;
160                 while( (ret=ogg_sync_and_take_page_out(&page)) < 0 );
161                 end = bufpos;
162                 while( ret > 0 ) {
163                         if( ogg_page_serialno(&page) == serialno ) {
164                                 memcpy(og, &page, sizeof(page));
165                                 ppos = bufpos - (page.header_len + page.body_len);
166                         }
167                         ret = ogg_sync_pageout(this, &page);
168                         bufpos += page.header_len + page.body_len;
169                 }
170         }
171         if( ppos >= 0 ) {
172                 pagpos = ppos;
173                 return 1;
174         }
175         printf("ogg_prev_page_search missed\n");
176         return 0;
177 }
178
179 int sync_window_t::ogg_get_prev_page(long serialno, ogg_page *og)
180 {
181         return ogg_prev_page_search(serialno, og, file_begin, pagpos);
182 }
183
184 int sync_window_t::ogg_get_first_page(long serialno, ogg_page *og)
185 {
186         ogg_read_buffer_at(file_begin, SEEK_SIZE);
187         return ogg_sync_and_get_next_page(serialno, og);
188 }
189
190 int sync_window_t::ogg_get_last_page(long serialno, ogg_page *og)
191 {
192
193         ogg_page page;
194         off_t filepos = file_end - READ_SIZE;
195         if( filepos < 0 ) filepos = 0;
196         int ret = 0, first_page_offset = 0;
197         while( !ret && filepos >= 0 ) {
198                 int readlen = ogg_read_buffer_at(filepos, READ_SIZE);
199                 int page_offset = 0, page_length = 0;
200                 int first_page = 1; // read all pages in the buffer
201                 while( first_page || page_length ) {
202                         // if negative, skip bytes
203                         while( (page_length = ogg_sync_and_take_page_out(&page)) < 0 )
204                                 page_offset -= page_length;
205                         if( page_length < mn_pagesz ) continue;
206                         if( first_page ) {
207                                 first_page = 0;
208                                 first_page_offset = page_offset;
209                         }
210                         if( ogg_page_serialno(&page) == serialno ) {
211                                 // return last match page
212                                 pagpos = bufpos - (page.header_len + page.body_len);
213                                 memcpy(og, &page, sizeof(page));
214                                 ret = 1;
215                         }
216                 }
217                 filepos -= readlen - first_page_offset;  // move backward
218         }
219         return ret;
220 }
221
222 OGG_PageBfr::OGG_PageBfr()
223 {
224         allocated = len = 0;
225         valid = packets = 0;
226         position = 0;
227         page = 0;
228 }
229
230 OGG_PageBfr::~OGG_PageBfr()
231 {
232         delete [] page;
233 }
234
235 void OGG_PageBfr::demand(int sz)
236 {
237         if( allocated >= sz ) return;
238         uint8_t *new_page = new uint8_t[sz];
239         memcpy(new_page, page, len);
240         delete [] page;  page = new_page;
241         allocated = sz;
242 }
243
244 int OGG_PageBfr::write_page(FILE *fp)
245 {
246         int sz = fwrite(page, 1, len, fp);
247         if( sz != len ) return -1;
248         ogg_page op;  // kludgy
249         op.header = page;    op.header_len = len;
250         op.body = page+len;  op.body_len = 0;
251         packets -= ogg_page_packets(&op);
252         valid = len = 0;
253         return packets;
254 }
255
256 int64_t OGG_PageBfr::load(ogg_page *og)
257 {
258         int sz = og->header_len + og->body_len;
259         demand(sz);
260         memcpy(page, og->header, og->header_len);
261         memcpy(page+og->header_len, og->body, og->body_len);
262         len = sz;  valid = 1;
263         position = ogg_page_granulepos(og);
264         return position;
265 }
266
267
268
269 FileOGG::FileOGG(Asset *asset, File *file)
270  : FileBase(asset, file)
271 {
272         if( asset->format == FILE_UNKNOWN )
273                 asset->format = FILE_OGG;
274         asset->byte_order = 0;
275         init();
276         file_lock = new Mutex("OGGFile::Flush lock");
277 }
278
279 FileOGG::~FileOGG()
280 {
281         close_file();
282         delete file_lock;
283 }
284
285
286 void FileOGG::init()
287 {
288         inp = 0;
289         out = 0;
290         audio = 0;
291         video = 0;
292         file_length = 0;
293         temp_frame = 0;
294         file_lock = 0;
295         ach = 0;
296         ahz = 0;
297         asz = 0;
298         amn = 0;
299         amx = 0;
300         abr = 0;
301         avbr = 0;
302         aqu = 0;
303         afrmsz = 0;
304         pcm_history = 0;
305         pcm_channels = 0;
306         frame_position = 0;
307         sample_position = 0;
308         audiosync = 0;
309         videosync = 0;
310         file_begin = 0;
311         file_end = 0;
312
313         memset(&to, 0, sizeof(to));
314         memset(&vo, 0, sizeof(vo));
315         ogg_sample_position = 0;
316         ogg_frame_position = 0;
317         next_sample_position = 0;
318         next_frame_position = 0;
319         start_sample = 0;
320         last_sample = 0;
321         start_frame = 0;
322         last_frame = 0;
323         audiotime = 0;
324         videotime = 0;
325         audio_pos = 0;  audio_eos = 0;
326         video_pos = 0;  video_eos = 0;
327
328         keyframe_granule_shift = 0;
329         iframe_granule_offset = 0;
330         theora_cmodel = BC_YUV420P;
331         enc = 0;
332         dec = 0;
333         memset(&ti, 0, sizeof(ti));
334         ts = 0;
335         memset(&tc, 0, sizeof(tc));
336         memset(&vi, 0, sizeof(vi));
337         memset(&vc, 0, sizeof(vc));
338         memset(&vd, 0, sizeof(vd));
339         memset(&vb, 0, sizeof(vb));
340         force_keyframes = 0;
341         vp3_compatible = 0;
342         soft_target = 0;
343
344         pic_x = pic_y = 0;
345         pic_w = pic_h = 0;
346         frame_w = frame_h = 0;
347         colorspace = OC_CS_UNSPECIFIED;
348         pixfmt = TH_PF_420;
349         bitrate = 0;  quality = 0;
350         keyframe_period = 0;
351         keyframe_force = 0;
352         fps_num = fps_den = 0;
353         aratio_num = aratio_den = 0;
354 }
355
356
357 static int ilog(unsigned v)
358 {
359         int ret = 0;
360         while( v ) { ++ret;  v >>= 1; }
361         return ret;
362 }
363
364 int FileOGG::encode_theora_init()
365 {
366         ogg_stream_init(&to, rand());
367         th_info_init(&ti);
368         pic_w = asset->width, pic_h = asset->height;
369         frame_w = (pic_w+0x0f) & ~0x0f;
370         frame_h = (pic_h+0x0f) & ~0x0f;
371         pic_x = ((frame_w-pic_w) >> 1) & ~1;
372         pic_y = ((frame_h-pic_h) >> 1) & ~1;
373         fps_num = asset->frame_rate * 1000000;
374         fps_den = 1000000;
375         if( asset->aspect_ratio > 0 ) {
376                 // Cinelerra uses frame aspect ratio, theora uses pixel aspect ratio
377                 float pixel_aspect = asset->aspect_ratio / asset->width * asset->height;
378                 aratio_num = pixel_aspect * 1000000;
379                 aratio_den = 1000000;
380         }
381         else {
382                 aratio_num = 1000000;
383                 aratio_den = 1000000;
384         }
385         if( EQUIV(asset->frame_rate, 25) || EQUIV(asset->frame_rate, 50) )
386                 colorspace = OC_CS_ITU_REC_470BG;
387         else if( (asset->frame_rate > 29 && asset->frame_rate < 31) ||
388                  (asset->frame_rate > 59 && asset->frame_rate < 61) )
389                 colorspace = OC_CS_ITU_REC_470M;
390         else
391                 colorspace = OC_CS_UNSPECIFIED;
392         pixfmt = TH_PF_420;
393         if( asset->theora_fix_bitrate ) {
394                 bitrate = asset->theora_bitrate;
395                 quality = -1;
396         }
397         else {
398                 bitrate = -1;
399                 quality = asset->theora_quality;     // 0-63
400         }
401         keyframe_period = asset->theora_keyframe_frequency;
402         keyframe_force = asset->theora_keyframe_force_frequency;
403         vp3_compatible = 1;
404         soft_target = 0;
405
406         ti.frame_width = frame_w;
407         ti.frame_height = frame_h;
408         ti.pic_width = pic_w;
409         ti.pic_height = pic_h;
410         ti.pic_x = pic_x;
411         ti.pic_y = pic_x;
412         ti.colorspace = (th_colorspace)colorspace;
413         ti.pixel_fmt = (th_pixel_fmt)pixfmt;
414         ti.target_bitrate = bitrate;
415         ti.quality = quality;
416         ti.fps_numerator = fps_num;
417         ti.fps_denominator = fps_den;
418         ti.aspect_numerator = aratio_num;
419         ti.aspect_denominator = aratio_den;
420         ti.keyframe_granule_shift = ilog(keyframe_period-1);
421
422         enc = th_encode_alloc(&ti);
423         int ret =  enc ? 0 : 1;
424         if( !ret && force_keyframes )
425                 ret = th_encode_ctl(enc,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
426                         &keyframe_period, sizeof(keyframe_period));
427         if( !ret && vp3_compatible )
428                 ret = th_encode_ctl(enc,TH_ENCCTL_SET_VP3_COMPATIBLE,
429                         &vp3_compatible, sizeof(vp3_compatible));
430         if( !ret && soft_target ) {
431                 int arg = TH_RATECTL_CAP_UNDERFLOW;
432                 if( th_encode_ctl(enc, TH_ENCCTL_SET_RATE_FLAGS, &arg, sizeof(arg)) < 0 ) {
433                         eprintf(_("Could not set rate flags"));
434                         ret = 1;
435                 }
436                 int kr = keyframe_period*7>>1, fr = 5*fps_num/fps_den;
437                 arg = kr > fr ? kr : fr;
438                 if( th_encode_ctl(enc, TH_ENCCTL_SET_RATE_BUFFER, &arg, sizeof(arg)) ) {
439                         eprintf(_("Could not set rate buffer"));
440                         ret = 1;
441                 }
442         }
443         if( ret ) {
444                 eprintf(_("theora init context failed"));
445                 return 1;
446         }
447
448         th_comment_init(&tc);
449         th_comment_add_tag(&tc, (char*)"ENCODER",
450                 (char*)PROGRAM_NAME " " CINELERRA_VERSION);
451         ogg_page og;
452         ogg_packet op;
453         ret = th_encode_flushheader(enc, &tc, &op);
454         if( ret <= 0 ) return 1;
455         ogg_stream_packetin(&to, &op);
456         ret = ogg_stream_pageout(&to, &og) != 1 ? 1 : 0;
457         if( !ret ) {
458                 fwrite(og.header, 1, og.header_len, out);
459                 fwrite(og.body, 1, og.body_len, out);
460         }
461         if( ret ) {
462                 eprintf(_("write header out failed"));
463                 return 1;
464         }
465         while( (ret=th_encode_flushheader(enc, &tc, &op)) > 0 )
466                 ogg_stream_packetin(&to, &op);
467         if( ret ) {
468                 eprintf(_("ogg_encoder_init video failed"));
469                 return 1;
470         }
471         return 0;
472 }
473
474 int FileOGG::encode_vorbis_init()
475 {
476         ach = asset->channels;
477         ahz = asset->sample_rate;
478         amx = asset->vorbis_max_bitrate;
479         amn = asset->vorbis_min_bitrate;
480         abr = asset->vorbis_bitrate;
481         avbr = asset->vorbis_vbr;
482         asz = sizeof(short);
483         afrmsz = asz * ach;
484         aqu = -99;
485         ogg_stream_init(&vo, rand());
486         vorbis_info_init(&vi);
487         int ret = 0;
488         if( avbr ) {
489                 ret = vorbis_encode_setup_managed(&vi, ach, ahz, -1, abr, -1);
490                 if( !ret )
491                         ret = vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_AVG, 0);
492                 if( !ret )
493                         ret = vorbis_encode_setup_init(&vi);
494         }
495         else
496                 ret = vorbis_encode_init(&vi, ach, ahz, amx, abr, amn);
497         if( ret ) {
498                 eprintf(_("ogg_encoder_init audio init failed"));
499                 return 1;
500         }
501         vorbis_comment_init(&vc);
502         vorbis_comment_add_tag(&vc, (char*)"ENCODER",
503                 (char*)PROGRAM_NAME " " CINELERRA_VERSION);
504         vorbis_analysis_init(&vd, &vi);
505         vorbis_block_init(&vd, &vb);
506         ogg_packet header;
507         ogg_packet header_comm;
508         ogg_packet header_code;
509         vorbis_analysis_headerout(&vd, &vc,
510                 &header, &header_comm, &header_code);
511         ogg_stream_packetin(&vo, &header);
512         ogg_page og;
513         ret = ogg_stream_pageout(&vo, &og)==1 ? 0 : -1;
514         if( ret >= 0 ) {
515                 fwrite(og.header, 1, og.header_len, out);
516                 fwrite(og.body, 1, og.body_len, out);
517                 ogg_stream_packetin(&vo, &header_comm);
518                 ogg_stream_packetin(&vo, &header_code);
519         }
520         if( ret < 0 ) {
521                 eprintf(_("ogg_encoder_init audio failed"));
522                 return 1;
523         }
524         return 0;
525 }
526
527 int FileOGG::ogg_init_encode(FILE *out)
528 {
529         this->out = out;
530         srand(time(0));
531         video = asset->video_data;
532         if( video && encode_theora_init() )
533                 return 1;
534         audio = asset->audio_data;
535         if( audio && encode_vorbis_init() )
536                 return 1;
537         ogg_page og;
538         int ret = 0;
539         if( !ret && video ) {
540                 while( (ret=ogg_stream_flush(&to, &og)) > 0 ) {
541                         fwrite(og.header, 1, og.header_len, out);
542                         fwrite(og.body, 1, og.body_len, out);
543                 }
544         }
545         if( !ret && audio ) {
546                 while( (ret=ogg_stream_flush(&vo, &og)) > 0 ) {
547                         fwrite(og.header, 1, og.header_len, out);
548                         fwrite(og.body, 1, og.body_len, out);
549                 }
550         }
551         if( ret < 0 ) {
552                 eprintf(_("render init failed"));
553                 return 1;
554         }
555         return 0;
556 }
557
558 int FileOGG::decode_theora_init()
559 {
560         dec = th_decode_alloc(&ti, ts);
561         if( !dec ) {
562                 eprintf(_("Error in probe data"));
563                 return 1;
564         }
565         keyframe_granule_shift = ti.keyframe_granule_shift;
566         iframe_granule_offset = th_granule_frame(dec, 0);
567         double fps = (double)ti.fps_numerator/ti.fps_denominator;
568
569         videosync = new sync_window_t(inp, file_lock, file_begin, file_end);
570         ogg_page og;
571         int ret = videosync->ogg_get_first_page(to.serialno, &og);
572         if( ret <= 0 ) {
573                 eprintf(_("cannot read video page from file"));
574                 return 1;
575         }
576         videosync->file_begin = videosync->pagpos;
577         ret = videosync->ogg_get_first_page(to.serialno, &og);
578         // video data starts here
579         // get to the page of the finish of the first packet
580         while( ret > 0 && !ogg_page_packets(&og) ) {
581                 if( ogg_page_granulepos(&og) != -1 ) {
582                         printf(_("FileOGG: Broken ogg file - broken page:"
583                                 " ogg_page_packets == 0 and granulepos != -1\n"));
584                         return 1;
585                 }
586                 ret = videosync->ogg_get_next_page(to.serialno, &og);
587         }
588         // video frames start here
589         start_frame = ogg_frame_pos(&og);
590         ret = videosync->ogg_get_first_page(to.serialno, &og);
591         if( ret <= 0 ) {
592                 printf(_("FileOGG: Cannot read data past header\n"));
593                 return 1;
594         }
595 //printf("start frame = %jd, gpos %jd, begins %jd\n",
596 // start_frame, ogg_page_granulepos(&og), videosync->file_begin);
597
598         ret = videosync->ogg_get_last_page(to.serialno, &og);
599         while( ret > 0 && !ogg_page_packets(&og) )
600                 ret = videosync->ogg_get_prev_page(to.serialno, &og);
601         if( ret > 0 ) {
602                 last_frame = ogg_next_frame_pos(&og);
603                 if( start_frame >= last_frame ) {
604                         eprintf(_("no video frames in file"));
605                         last_frame = start_frame = 0;
606                 }
607                 asset->video_length = last_frame - start_frame;
608         }
609         else {
610                 printf("FileOGG: Cannot find the video length\n");
611                 return 1;
612         }
613         asset->layers = 1;
614         asset->width = ti.pic_width;
615         asset->height = ti.pic_height;
616 // Don't want a user configured frame rate to get destroyed
617         if( !asset->frame_rate )
618                 asset->frame_rate = fps;
619 // All theora material is noninterlaced by definition
620         if( !asset->interlace_mode )
621                 asset->interlace_mode = ILACE_MODE_NOTINTERLACED;
622
623         set_video_position(0); // make sure seeking is done to the first sample
624         ogg_frame_position = -10;
625         asset->video_data = 1;
626         strncpy(asset->vcodec, "theo", 4);
627 //      report_colorspace(&ti);
628 //      dump_comments(&tc);
629         return 0;
630 }
631
632 int FileOGG::decode_vorbis_init()
633 {
634         ogg_stream_reset(&vo);
635         vorbis_synthesis_init(&vd, &vi);
636         vorbis_block_init(&vd, &vb);
637         audiosync = new sync_window_t(inp, file_lock, file_begin, file_end);
638         ogg_page og;
639         int ret = audiosync->ogg_get_first_page(vo.serialno, &og);
640         if( ret <= 0 ) {
641                 eprintf(_("cannot read audio page from file"));
642                 return 1;
643         }
644         // audio data starts here
645         audiosync->file_begin = audiosync->pagpos;
646         // audio samples starts here
647         start_sample = ogg_sample_pos(&og);
648 //printf("start sample = %jd, gpos %jd, begins %jd\n",
649 // start_sample, ogg_page_granulepos(&og), audiosync->file_begin);
650         ret = audiosync->ogg_get_last_page(vo.serialno, &og);
651         last_sample = ret > 0 ? ogg_next_sample_pos(&og) : 0;
652         asset->audio_length = last_sample - start_sample;
653         if( asset->audio_length <= 0 ) {
654                 eprintf(_("no audio samples in file"));
655                 asset->audio_length = 0;
656                 last_sample = start_sample;
657         }
658
659         asset->channels = vi.channels;
660         if( !asset->sample_rate )
661                 asset->sample_rate = vi.rate;
662         asset->audio_data = 1;
663
664         ogg_sample_position = -10;
665         set_audio_position(0); // make sure seeking is done to the first sample
666         strncpy(asset->acodec, "vorb", 4);
667         return 0;
668 }
669
670 int FileOGG::ogg_init_decode(FILE *inp)
671 {
672         if( !inp ) return 1;
673         this->inp = inp;
674         struct stat file_stat; /* get file length */
675         file_end = stat(asset->path, &file_stat)>=0 ? file_stat.st_size : 0;
676         if( file_end < mn_pagesz ) return 1;
677         fseek(inp, 0, SEEK_SET);
678         vorbis_info_init(&vi);
679         vorbis_comment_init(&vc);
680         th_comment_init(&tc);
681         th_info_init(&ti);
682         ogg_page og;
683         ogg_packet op;
684         sync_window_t sy(inp, file_lock, 0, file_end);
685         int ret = sy.ogg_read_buffer_at(0, READ_SIZE);
686         if( ret < mn_pagesz ) return 1;
687         if( !sy.ogg_sync_and_take_page_out(&og) ) return 1;
688         ogg_stream_state tst;
689
690         while( ogg_page_bos(&og) ) {
691                 ogg_stream_init(&tst, ogg_page_serialno(&og));
692                 ogg_stream_pagein(&tst, &og);
693                 if( ogg_stream_packetout(&tst, &op) ) {
694                         if( !video && th_decode_headerin(&ti, &tc, &ts, &op) >=0 ) {
695                                 ogg_stream_init(&to, ogg_page_serialno(&og));
696                                 video = 1;
697                         }
698                         else if( !audio && vorbis_synthesis_headerin(&vi, &vc, &op) >=0 ) {
699                                 ogg_stream_init(&vo, ogg_page_serialno(&og));
700                                 audio = 1;
701                         }
702                 }
703                 ogg_stream_clear(&tst);
704                 ret = sy.ogg_take_page_out_autoadvance(&og);
705         }
706
707         if( !ret || !video && !audio )
708                 return 1;
709
710         // expecting more a/v header packets
711         int vpkts = video ? 2 : 0;
712         int apkts = audio ? 2 : 0;
713         int retries = 100;
714         ret = 0;
715         while( --retries >= 0 && !ret && (vpkts || apkts) ) {
716                 if( vpkts && ogg_page_serialno(&og) == to.serialno ) {
717                         ogg_stream_init(&tst, to.serialno);
718                         ogg_stream_pagein(&tst, &og);
719                         while( !ret && vpkts > 0 ) {
720                                 while( (ret=ogg_stream_packetout(&tst, &op)) < 0 );
721                                 if( !ret ) break;
722                                 --vpkts;
723                                 ret = !th_decode_headerin(&ti, &tc, &ts, &op) ? 1 : 0;
724                         }
725                         if( ret )
726                                 printf("theora header error\n");
727                         ogg_stream_clear(&tst);
728                 }
729                 else if( apkts && ogg_page_serialno(&og) == vo.serialno ) {
730                         ogg_stream_init(&tst, vo.serialno);
731                         ogg_stream_pagein(&tst, &og);
732                         while( !ret && apkts > 0 ) {
733                                 while( (ret=ogg_stream_packetout(&tst, &op)) < 0 );
734                                 if( !ret ) break;
735                                 --apkts;
736                                 ret = vorbis_synthesis_headerin(&vi, &vc, &op) ? 1 : 0;
737                         }
738                         if( ret )
739                                 printf("vorbis header error\n");
740                         ogg_stream_clear(&tst);
741                 }
742                 if( !ret && !sy.ogg_take_page_out_autoadvance(&og) )
743                         ret = 1;
744                 if( ret )
745                         printf("incomplete headers\n");
746
747         }
748 // find first start packet (not continued) with data
749         int64_t start_pos = sy.bufpos - (og.header_len + og.body_len);
750         if( !ret ) {
751                 while( --retries >= 0 && !ret && !ogg_page_packets(&og) ) {
752                         if( !ogg_page_continued(&og) )
753                                 start_pos = sy.bufpos - (og.header_len + og.body_len);
754                         if( !sy.ogg_take_page_out_autoadvance(&og) ) ret = 1;
755                 }
756                 if( ret )
757                         printf("no data past headers\n");
758                 if( audio && apkts )
759                         printf("missed %d audio headers\n",apkts);
760                 if( video && vpkts )
761                         printf("missed %d video headers\n",vpkts);
762         }
763         if( retries < 0 || ret || (audio && apkts) || (video && vpkts) ) {
764                 eprintf(_("Error in headers"));
765                 return 1;
766         }
767         // headers end here
768         file_begin = start_pos;
769
770         if( video && decode_theora_init() )
771                 return 1;
772         if( audio && decode_vorbis_init() )
773                 return 1;
774         return 0;
775 }
776
777 void FileOGG::close_encoder()
778 {
779 // flush streams
780         if( audio )
781                 write_samples_vorbis(0, 0, 1);
782         if( video )
783                 write_frames_theora(0, 1, 1);
784         flush_ogg(1);
785
786         if( audio ) {
787                 vorbis_block_clear(&vb);
788                 vorbis_dsp_clear(&vd);
789                 vorbis_comment_clear(&vc);
790                 vorbis_info_clear(&vi);
791                 ogg_stream_clear(&vo);
792                 audio = 0;
793         }
794         if( video ) {
795                 th_comment_clear(&tc);
796                 ogg_stream_clear(&to);
797                 video = 0;
798         }
799         if( enc ) {
800                 th_encode_free(enc);
801                 enc = 0;
802         }
803         if( out ) {
804                 fclose(out);
805                 out = 0;
806         }
807 }
808
809 void FileOGG::close_decoder()
810 {
811         if( audio ) {
812                 for( int i=0; i<pcm_channels; ++i )
813                         delete [] pcm_history[i];
814                 pcm_channels = 0;
815                 delete [] pcm_history;  pcm_history = 0;
816
817                 vorbis_dsp_clear(&vd);
818                 vorbis_info_clear(&vi);
819                 vorbis_block_clear(&vb);
820                 vorbis_comment_clear(&vc);
821                 ogg_stream_clear(&vo);
822                 delete audiosync;  audiosync = 0;
823                 audio = 0;
824         }
825         if( video ) {
826                 th_info_clear(&ti);
827                 th_setup_free(ts);  ts = 0;
828                 th_comment_clear(&tc);
829                 ogg_stream_clear(&to);
830                 delete videosync;  videosync = 0;
831                 video = 0;
832         }
833         if( dec ) {
834                 th_decode_free(dec);
835                 dec = 0;
836         }
837         if( inp ) {
838                 fclose(inp);
839                 inp = 0;
840         }
841 }
842
843
844
845 void FileOGG::get_parameters(BC_WindowBase *parent_window, Asset *asset,
846         BC_WindowBase* &format_window, int audio_options,
847         int video_options, EDL *edl)
848 {
849         if(audio_options)
850         {
851                 OGGConfigAudio *window = new OGGConfigAudio(parent_window, asset);
852                 format_window = window;
853                 window->create_objects();
854                 window->run_window();
855                 delete window;
856         }
857         else
858         if(video_options)
859         {
860                 OGGConfigVideo *window = new OGGConfigVideo(parent_window, asset);
861                 format_window = window;
862                 window->create_objects();
863                 window->run_window();
864                 delete window;
865         }
866 }
867
868
869
870 int sync_window_t::ogg_take_page_out_autoadvance(ogg_page *og)
871 {
872         for(;;) {
873                 int ret = ogg_sync_pageout(this, og);
874                 if( ret < 0 ) {
875                         printf("FileOGG: Lost sync reading input file\n");
876                         return 0;
877                 }
878                 if( ret > 0 ) {
879                         bufpos += og->header_len + og->body_len;
880                         return ret;
881                 }
882                 // need more data for page
883                 if( !ogg_read_buffer(READ_SIZE) ) {
884                         printf("FileOGG: Read past end of input file\n");
885                         return 0;  // No more data
886                 }
887         }
888         return 1;
889 }
890
891
892 int FileOGG::check_sig(Asset *asset)
893 {
894         FILE *fp = fopen(asset->path, "rb");
895         if( !fp ) return 0;
896 // Test for "OggS"
897         fseek(fp, 0, SEEK_SET);
898         char data[4];
899         int ret = fread(data, 4, 1, fp) == 1 &&
900                 data[0] == 'O' && data[1] == 'g' &&
901                 data[2] == 'g' && data[3] == 'S' ? 1 : 0;
902         fclose(fp);
903         return ret;
904
905 }
906
907 int FileOGG::open_file(int rd, int wr)
908 {
909         int ret = 1;
910         if( wr ) {
911                 if( !(out = fopen(asset->path, "wb")) ) {
912                         eprintf(_("Error while opening %s for writing. %m\n"), asset->path);
913                         return 1;
914                 }
915                 if( (ret = ogg_init_encode(out)) && out ) {
916                         fclose(out);  out = 0;
917                 }
918         }
919         else if( rd ) {
920                 if( !(inp = fopen(asset->path, "rb")) ) {
921                         eprintf(_("Error while opening %s for reading. %m\n"), asset->path);
922                         return 1;
923                 }
924                 if( (ret = ogg_init_decode(inp)) && inp ) {
925                         fclose(inp);  inp = 0;
926                 }
927         }
928         return ret;
929 }
930
931 int FileOGG::close_file()
932 {
933         if( file->wr )
934                 close_encoder();
935         else if( file->rd )
936                 close_decoder();
937         return 0;
938 }
939
940
941 int64_t FileOGG::ogg_sample_pos(ogg_page *og)
942 {
943         ogg_packet op;
944         ogg_stream_state ss;
945         ogg_stream_init(&ss, vo.serialno);
946         ogg_stream_pagein(&ss, og);
947         int64_t bsz = 0;
948         long prev = -1;
949         int ret = 0;
950         while( (ret=ogg_stream_packetout(&ss, &op)) ) {
951                 if( ret < 0 ) continue; // ignore holes
952                 long sz =  vorbis_packet_blocksize(&vi, &op);
953                 if( prev != -1 ) bsz += (prev + sz) >> 2;
954                 prev = sz;
955         }
956         ogg_stream_clear(&ss);
957         return ogg_next_sample_pos(og) - bsz;
958 }
959
960 int64_t FileOGG::ogg_next_sample_pos(ogg_page *og)
961 {
962         return ogg_page_granulepos(og);
963 }
964
965 int64_t FileOGG::ogg_frame_pos(ogg_page *og)
966 {
967         int64_t pos = th_granule_frame(dec, ogg_page_granulepos(og)) - ogg_page_packets(og);
968         if( ogg_page_continued(og) ) --pos;
969         return pos;
970 }
971
972 int64_t FileOGG::ogg_next_frame_pos(ogg_page *og)
973 {
974         return th_granule_frame(dec, ogg_page_granulepos(og)) + 1;
975 }
976
977
978 int FileOGG::ogg_get_page_of_sample(ogg_page *og, int64_t sample)
979 {
980         if( sample >= asset->audio_length + start_sample ) {
981                 printf(_("FileOGG: Illegal seek beyond end of samples\n"));
982                 return 0;
983         }
984 // guess about position
985         int64_t file_length =  audiosync->file_end - audiosync->file_begin;
986         off_t guess = file_length * (sample - start_sample) /
987                 asset->audio_length - SEEK_SIZE;
988         if( guess < 0 ) guess = 0;
989         guess += audiosync->file_begin;
990         audiosync->ogg_read_buffer_at(guess, READ_SIZE);
991         if( !audiosync->ogg_sync_and_get_next_page(vo.serialno, og) ) {
992                 printf(_("FileOGG: llegal seek no pages\n"));
993                 return 0;
994         }
995         int ret = 1;
996         while( ret && (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) )
997                 ret = videosync->ogg_get_next_page(to.serialno, og);
998         if( !ret ) return 0;
999         // linear seek to the sample
1000         int missp = 0, missm = 0;
1001         int64_t next_pos = ogg_next_sample_pos(og);
1002         if( sample >= next_pos ) { // scan forward
1003                 while( sample >= next_pos ) {
1004                         while( !(ret=audiosync->ogg_get_next_page(vo.serialno, og)) &&
1005                                 (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) );
1006                         if( !ret ) break;
1007                         next_pos = ogg_next_sample_pos(og);
1008                         ++missp;
1009 //printf("audio %jd next %jd %jd\n", sample, ogg_sample_pos(og), next_pos);
1010                 }
1011         }
1012         else { // scan backward
1013                 int64_t pos = ogg_sample_pos(og);
1014                 while( sample < pos ) {
1015                         while( (ret=audiosync->ogg_get_prev_page(vo.serialno, og)) &&
1016                                 (ogg_page_continued(og) && ogg_page_packets(og) == 1) );
1017                         if( !ret ) break;
1018                         ++missm;
1019                         pos = ogg_sample_pos(og);
1020 //printf("audio %jd prev %jd %jd\n", sample, pos, ogg_next_sample_pos(og));
1021                 }
1022         }
1023 //printf("audio %d seek %jd, missp %d, missm %d  from %jd to %jd\n", ret,
1024 // sample, missp, missm, ogg_sample_pos(og), ogg_next_sample_pos(og));
1025         return ret;
1026 }
1027
1028 int FileOGG::ogg_seek_to_sample(int64_t ogg_sample)
1029 {
1030         ogg_page og;
1031         ogg_packet op;
1032         if( !ogg_get_page_of_sample(&og, ogg_sample) ) {
1033                 eprintf(_("Seeking to sample's page failed\n"));
1034                 return 0;
1035         }
1036         int ret = 1;
1037         int64_t pos = ogg_sample_pos(&og);
1038         int64_t next_pos = pos;
1039         if( ogg_page_continued(&og) ) {
1040                 while( (ret=audiosync->ogg_get_prev_page(to.serialno, &og)) &&
1041                         (ogg_page_packets(&og) == 0 && ogg_page_continued(&og)) );
1042         }
1043         if( ret ) {
1044                 audio_eos = 0;
1045                 ogg_stream_reset(&vo);
1046                 ogg_stream_pagein(&vo, &og);
1047                 vorbis_synthesis_restart(&vd);
1048                 ret = ogg_get_audio_packet(&op);
1049         }
1050         if( ret && !vorbis_synthesis(&vb, &op) ) {
1051                 vorbis_synthesis_blockin(&vd, &vb);
1052                 if( vorbis_synthesis_pcmout(&vd, 0) )
1053                         ret = 0;
1054         }
1055         if( !ret ) {
1056                 eprintf(_("Something wrong while trying to seek\n"));
1057                 return 0;
1058         }
1059
1060         while( ogg_sample > next_pos ) {
1061                 if( !(ret=ogg_get_audio_packet(&op)) ) break;
1062                 if( vorbis_synthesis(&vb, &op) ) continue;
1063                 vorbis_synthesis_blockin(&vd, &vb);
1064                 pos = next_pos;
1065                 next_pos += vorbis_synthesis_pcmout(&vd, NULL);
1066                 if( next_pos > ogg_sample ) break;
1067                 // discard decoded data before current sample
1068                 vorbis_synthesis_read(&vd, (next_pos - pos));
1069         }
1070         if( ret ) {
1071                 audio_pos = next_pos;
1072                 vorbis_synthesis_read(&vd, (ogg_sample - pos));
1073         }
1074         return ret;
1075 }
1076
1077
1078 int FileOGG::ogg_get_page_of_frame(ogg_page *og, int64_t frame)
1079 {
1080         if( frame >= asset->video_length + start_frame ) {
1081                 eprintf(_("Illegal seek beyond end of frames\n"));
1082                 return 0;
1083         }
1084         if( frame < start_frame ) {
1085                 eprintf(_("Illegal seek before start of frames\n"));
1086                 return 0;
1087         }
1088         int64_t file_length = videosync->file_end - videosync->file_begin;
1089         off_t guess = file_length * (frame - start_frame) /
1090                  asset->video_length - SEEK_SIZE;
1091         if( guess < 0 ) guess = 0;
1092         guess += videosync->file_begin;
1093         videosync->ogg_read_buffer_at(guess, SEEK_SIZE);
1094         videosync->ogg_sync_and_get_next_page(to.serialno, og);
1095         // find the page with "real" ending
1096         int ret = 1;
1097         while( ret && (ogg_page_granulepos(og) == -1 || !ogg_page_packets(og)) )
1098                ret = videosync->ogg_get_next_page(to.serialno, og);
1099         int64_t pos = ogg_next_frame_pos(og);
1100         // linear search
1101         int missp = 0, missm = 0;
1102 // move back if continued
1103         if( frame >= pos ) {
1104                 do { // scan forward
1105                         while( (ret=videosync->ogg_get_next_page(to.serialno, og)) &&
1106                                 ogg_page_packets(og) == 0 );
1107                         if( !ret ) break;
1108                         missp++;
1109                         pos = ogg_next_frame_pos(og);
1110 //printf("video %jd next %jd %jd\n", frame, ogg_frame_pos(og), pos);
1111                 } while( frame >= pos );
1112         }
1113         else if( (pos=ogg_frame_pos(og)) > frame ) {
1114                 while( pos > start_frame && frame < pos ) { // scan backward
1115                         while( (ret=videosync->ogg_get_prev_page(to.serialno, og)) &&
1116                                 ogg_page_packets(og) == 0 && ogg_page_continued(og) );
1117                         if( !ret ) break;
1118                         missm++;
1119                         pos = ogg_frame_pos(og);
1120 //printf("video %jd next %jd %jd\n", frame, pos, ogg_next_frame_pos(og));
1121                 }
1122         }
1123 //printf("video %d seek %jd, missp %d, missm %d first %jd, next %jd\n", ret,
1124 // frame, missp, missm, ogg_frame_pos(og), ogg_next_frame_pos(og));
1125         return ret;
1126 }
1127
1128 int FileOGG::ogg_seek_to_keyframe(int64_t frame, int64_t *keyframe_number)
1129 {
1130 //printf("ogg_seek_to_keyframe of === %jd\n", frame);
1131         ogg_page og;
1132         ogg_packet op;
1133         int64_t ipagpos = -1;
1134         int64_t istart = -1;
1135         int64_t iframe = -1;
1136         int ipkts = -1;
1137         int retries = 1000, ret = 1;
1138         while( --retries>=0 && frame>=start_frame ) {
1139                 if( !ogg_get_page_of_frame(&og, frame) ) break;
1140                 int64_t pos = ogg_frame_pos(&og);
1141                 istart = pos;
1142                 if( ogg_page_continued(&og) ) {
1143                         while( (ret=videosync->ogg_get_prev_page(to.serialno, &og)) &&
1144                                 (ogg_page_packets(&og) == 0 && ogg_page_continued(&og)) );
1145                 }
1146                 int64_t pagpos = videosync->pagpos;
1147                 video_eos = 0;
1148                 ogg_stream_reset(&to);
1149                 ogg_stream_pagein(&to, &og);
1150                 int pkts = 0;
1151                 while( frame >= pos && (ret=ogg_get_video_packet(&op)) ) {
1152                         if( th_packet_iskeyframe(&op) == 1 ) {
1153                                 ipagpos = pagpos;
1154                                 iframe = pos;
1155                                 ipkts = pkts;
1156 //printf("keyframe %jd pkts %d\n", pos, pkts);
1157                         }
1158 //printf("packet %jd pkts %d is a %d\n", pos, pkts,  th_packet_iskeyframe(&op));
1159                         ++pkts;  ++pos;
1160                 }
1161                 if( ipagpos >= 0 ) break;
1162                 frame = istart - 1;
1163         }
1164         if( ipagpos < 0 ) {
1165                 printf(_("Seeking to keyframe %jd search failed\n"), frame);
1166                 return 0;
1167         }
1168         videosync->ogg_read_buffer_at(ipagpos, READ_SIZE);
1169         videosync->ogg_sync_and_get_next_page(to.serialno, &og);
1170         video_eos = 0;
1171         ogg_stream_reset(&to);
1172         ogg_stream_pagein(&to, &og);
1173         video_pos = ogg_next_frame_pos(&og);
1174 // skip prev packets
1175 //      int ipkts = iframe - ogg_frame_pos(&og);
1176 //printf("iframe %jd, page %jd, ipkts %d\n", iframe, ogg_page_pageno(&og), ipkts);
1177         while( --ipkts >= 0 )
1178                 ogg_get_video_packet(&op);
1179         *keyframe_number = iframe;
1180         return 1;
1181 }
1182
1183
1184 int64_t FileOGG::get_video_position()
1185 {
1186 //      printf("GVP\n");
1187         return next_frame_position - start_frame;
1188 }
1189
1190 int64_t FileOGG::get_audio_position()
1191 {
1192         return next_sample_position - start_sample;
1193 }
1194
1195 int FileOGG::set_video_position(int64_t x)
1196 {
1197 //      x=0;
1198 //      printf("SVP: %lli\n", x);
1199
1200         next_frame_position = x + start_frame;
1201         return 1;
1202 }
1203
1204
1205 int FileOGG::colormodel_supported(int colormodel)
1206 {
1207 //      printf("CMS\n");
1208
1209         if (colormodel == BC_YUV420P)
1210                 return BC_YUV420P;
1211         else
1212                 return colormodel;
1213 }
1214 int FileOGG::get_best_colormodel(Asset *asset, int driver)
1215 {
1216
1217         return BC_YUV420P;
1218 }
1219
1220 int FileOGG::set_audio_position(int64_t x)
1221 {
1222         next_sample_position = x + start_sample;
1223         return 0;
1224 }
1225
1226
1227 int FileOGG::ogg_get_video_packet(ogg_packet *op)
1228 {
1229         int ret = 1;
1230         while( (ret=ogg_stream_packetout(&to, op)) <= 0 ) {
1231                 if( video_eos ) return 0;
1232                 ogg_page og;
1233                 if( !videosync->ogg_get_next_page(to.serialno, &og) ) break;
1234                 if( ogg_page_granulepos(&og) >= 0 )
1235                         video_pos = ogg_next_frame_pos(&og);
1236                 ogg_stream_pagein(&to, &og);
1237                 video_eos = ogg_page_eos(&og);
1238         }
1239         if( ret <= 0 ) {
1240                 printf("FileOGG: Cannot read video packet\n");
1241                 return 0;
1242         }
1243         return 1;
1244 }
1245
1246 int FileOGG::read_frame(VFrame *frame)
1247 {
1248         if( !inp || !video ) return 1;
1249         // skip is cheaper than seek, do it...
1250         int decode_frames = 0;
1251         int expect_keyframe = 0;
1252         if( ogg_frame_position >= 0 &&
1253             next_frame_position >= ogg_frame_position &&
1254             next_frame_position - ogg_frame_position < 32) {
1255                 decode_frames = next_frame_position - ogg_frame_position;
1256         }
1257         else if( next_frame_position != ogg_frame_position ) {
1258                 if( !ogg_seek_to_keyframe(next_frame_position, &ogg_frame_position) ) {
1259                         eprintf(_("Error while seeking to frame's keyframe"
1260                                 " (frame: %jd, keyframe: %jd)\n"),
1261                                 next_frame_position, ogg_frame_position);
1262                         return 1;
1263                 }
1264                 decode_frames = next_frame_position - ogg_frame_position + 1;
1265                 --ogg_frame_position;
1266                 if( decode_frames <= 0 ) {
1267                         eprintf(_("Error while seeking to keyframe,"
1268                                 " wrong keyframe number (frame: %jd, keyframe: %jd)\n"),
1269                                 next_frame_position, ogg_frame_position);
1270                         return 1;
1271
1272                 }
1273                 expect_keyframe = 1;
1274         }
1275         int ret = 0;
1276         ogg_packet op;
1277         while( decode_frames > 0 ) {
1278                 if( !ogg_get_video_packet(&op) ) break;
1279                 if( expect_keyframe ) {
1280                         expect_keyframe = 0;
1281                         if( th_packet_iskeyframe(&op) <= 0 )
1282                                 eprintf(_("FileOGG: Expecting keyframe, but didn't get it\n"));
1283                 }
1284                 ogg_int64_t granpos = 0;
1285                 if( th_decode_packetin(dec, &op, &granpos) >= 0 )
1286                         ret = 1;
1287                 ++ogg_frame_position;
1288                 --decode_frames;
1289         }
1290 //if(ret < 0 )printf("ret = %d\n", ret);
1291         if( ret > 0 ) {
1292                 th_ycbcr_buffer ycbcr;
1293                 ret = th_decode_ycbcr_out(dec, ycbcr);
1294                 if( ret ) {
1295                         eprintf(_("th_decode_ycbcr_out failed with code %i\n"), ret);
1296                         ret = 0; // not always fatal
1297                 }
1298                 uint8_t *yp = ycbcr[0].data;
1299                 uint8_t *up = ycbcr[1].data;
1300                 uint8_t *vp = ycbcr[2].data;
1301                 int yst = ycbcr[0].stride;
1302                 int yw = ycbcr[0].width;
1303                 int yh = ycbcr[0].height;
1304                 VFrame temp_frame(yp, -1, 0, up-yp, vp-yp, yw,yh, BC_YUV420P, yst);
1305                 int px = ti.pic_x, py = ti.pic_y;
1306                 int pw = ti.pic_width, ph = ti.pic_height;
1307                 frame->transfer_from(&temp_frame, -1, px, py, pw, ph);
1308         }
1309
1310         next_frame_position++;
1311         return ret;
1312 }
1313
1314
1315 int FileOGG::ogg_get_audio_packet(ogg_packet *op)
1316 {
1317         int ret = 1;
1318         while( (ret=ogg_stream_packetout(&vo, op)) <= 0 ) {
1319                 if( audio_eos ) return 0;
1320                 ogg_page og;
1321                 if( !audiosync->ogg_get_next_page(vo.serialno, &og) ) break;
1322                 if( ogg_page_granulepos(&og) >= 0 )
1323                         audio_pos = ogg_next_sample_pos(&og);
1324                 ogg_stream_pagein(&vo, &og);
1325                 audio_eos = ogg_page_eos(&og);
1326         }
1327         if( ret <= 0 ) {
1328                 printf("FileOGG: Cannot read audio packet\n");
1329                 return 0;
1330         }
1331         return 1;
1332 }
1333
1334 int FileOGG::ogg_decode_more_samples()
1335 {
1336         ogg_packet op;
1337         while( ogg_get_audio_packet(&op) ) {
1338                 if( !vorbis_synthesis(&vb, &op) ) {
1339                         vorbis_synthesis_blockin(&vd, &vb);
1340                         return 1;
1341                 }
1342         }
1343         ogg_sample_position = -11;
1344         eprintf(_("Cannot find next page while trying to decode more samples\n"));
1345         return 0;
1346 }
1347
1348 int FileOGG::move_history(int from, int to, int len)
1349 {
1350         if( len > 0 ) {
1351                 for( int i=0; i<asset->channels; ++i )
1352                         memmove(pcm_history[i] + to,
1353                                 pcm_history[i] + from,
1354                                 sizeof(float) * len);
1355         }
1356         history_start = history_start + from - to;
1357         if( history_start < 0 ) history_start = 0;
1358         return 0;
1359 }
1360
1361 int FileOGG::read_samples(double *buffer, int64_t len)
1362 {
1363         float **vorbis_buffer;
1364         if( len <= 0 )
1365                 return 0;
1366         if( len > HISTORY_MAX ) {
1367                 eprintf(_("max samples=%d\n"), HISTORY_MAX);
1368                 return 1;
1369         }
1370
1371         if( !pcm_history ) {
1372                 pcm_history = new float*[asset->channels];
1373                 for(int i = 0; i < asset->channels; i++)
1374                         pcm_history[i] = new float[HISTORY_MAX];
1375                 history_start = -100000000;
1376                 history_size = 0;
1377         }
1378
1379         int64_t hole_start = -1;
1380         int64_t hole_len = -1;
1381         int64_t hole_absstart = -1;
1382         int64_t hole_fill = 0;
1383
1384         if( history_start < next_sample_position &&
1385             history_start + history_size > next_sample_position &&
1386             history_start + history_size < next_sample_position + len ) {
1387                 hole_fill = 1;
1388                 hole_start = history_start + history_size - next_sample_position;
1389                 hole_len = history_size - hole_start;
1390                 hole_absstart = next_sample_position + hole_start;
1391                 move_history(next_sample_position - history_start, 0, hole_start);
1392         }
1393         else if( next_sample_position < history_start &&
1394                  history_start < next_sample_position + len ) {
1395                 hole_fill = 1;
1396                 hole_start = 0;
1397                 hole_len = history_start - next_sample_position;
1398                 hole_absstart = next_sample_position;
1399                 move_history(0,
1400                         history_start - next_sample_position,
1401                         history_size - history_start + next_sample_position);
1402
1403         }
1404         else if( next_sample_position >= history_start + history_size ||
1405                  next_sample_position + len <= history_start ) {
1406                 hole_fill = 1;
1407                 hole_start = 0;
1408                 hole_len = HISTORY_MAX;
1409                 hole_absstart = next_sample_position;
1410                 history_start = hole_absstart;
1411                 history_size = hole_len;
1412         }
1413
1414         if( hole_fill ) {
1415                 if( hole_start < 0 || hole_len <= 0 || hole_absstart < 0 ) {
1416                         eprintf(_("Error in finding read file position\n"));
1417                         return 1;
1418                 }
1419
1420                 if( hole_absstart + hole_len > asset->audio_length + start_sample ) {
1421                         hole_len = asset->audio_length + start_sample - hole_absstart;
1422                         history_size = asset->audio_length + start_sample - history_start;
1423                 }
1424                 else {
1425                         history_size = HISTORY_MAX;
1426                 }
1427
1428                 int64_t samples_read = 0;
1429                 if( ogg_sample_position != hole_absstart ) {
1430                         ogg_sample_position = hole_absstart;
1431                         if( !ogg_seek_to_sample(ogg_sample_position) ) {
1432                                 eprintf(_("Error while seeking to sample\n"));
1433                                 return 1;
1434                         }
1435                 }
1436                 // now we have ogg_sample_positon aligned
1437                 int64_t samples_to_read = hole_len;
1438                 while( samples_read < hole_len ) {
1439                         int64_t samples_waiting = vorbis_synthesis_pcmout(&vd, &vorbis_buffer);
1440                         int64_t samples_avail = !samples_waiting && audio_eos ?
1441                                 hole_len - samples_read : // silence after eos
1442                                 samples_waiting ;
1443                         int64_t sample_demand = samples_to_read - samples_read;
1444                         int64_t sample_count = MIN(samples_avail, sample_demand);
1445                         if( sample_count > 0 ) {
1446                                 int sz = sample_count*sizeof(float);
1447                                 if( samples_waiting ) {
1448                                         for( int i=0; i<asset->channels; ++i ) {
1449                                                 float *input = vorbis_buffer[i];
1450                                                 float *output = pcm_history[i] + hole_start;
1451                                                 memcpy(output, input, sz);
1452                                         }
1453                                         vorbis_synthesis_read(&vd, sample_count);
1454                                 }
1455                                 else {
1456                                         for( int i=0; i<asset->channels; ++i ) {
1457                                                 float *output = pcm_history[i] + hole_start;
1458                                                 memset(output, 0, sz);
1459                                         }
1460                                 }
1461                                 ogg_sample_position += sample_count;
1462                                 hole_start += sample_count;
1463                                 samples_read += sample_count;
1464                                 if( samples_read >= hole_len ) break;
1465                         }
1466                         if( samples_read < hole_len && !ogg_decode_more_samples() )
1467                                 break;
1468                 }
1469         }
1470
1471         if( next_sample_position < history_start ||
1472             next_sample_position + len > history_start + history_size ) {
1473                 printf(_("FileOGG:: History not aligned properly \n"));
1474                 printf(_("\tnext_sample_position: %jd, length: %jd\n"), next_sample_position, len);
1475                 printf(_("\thistory_start: %jd, length: %jd\n"), history_start, history_size);
1476                 return 1;
1477         }
1478         float *input = pcm_history[file->current_channel] + next_sample_position - history_start;
1479         for (int i = 0; i < len; i++)
1480                 buffer[i] = input[i];
1481
1482         next_sample_position += len;
1483         return 0;
1484 }
1485
1486
1487 int FileOGG::write_audio_page()
1488 {
1489         int ret = apage.write_page(out);
1490         if( ret < 0 )
1491                 eprintf(_("error writing audio page\n"));
1492         return ret;
1493 }
1494
1495 int FileOGG::write_video_page()
1496 {
1497         int ret = vpage.write_page(out);
1498         if( ret < 0 )
1499                 eprintf(_("error writing video page\n"));
1500         return ret;
1501 }
1502
1503 // flush out the ogg pages
1504 void FileOGG::flush_ogg(int last)
1505 {
1506         ogg_page og;
1507         file_lock->lock("FileOGG::flush_ogg");
1508         for(;;) {
1509 // this way seeking is much better, (per original fileogg)
1510 // not sure if 32 packets is a good value.
1511                 int mx_pkts = 32;
1512                 if( video && !vpage.valid ) {
1513                         if( (vpage.packets > mx_pkts && ogg_stream_flush(&to, &og) > 0) ||
1514                             ogg_stream_pageout(&to, &og) > 0 ) {
1515                                 videotime = th_granule_time(enc, vpage.load(&og));
1516                         }
1517                 }
1518                 if( audio && !apage.valid ) {
1519                         if( (apage.packets > mx_pkts && ogg_stream_flush(&vo, &og) > 0) ||
1520                             ogg_stream_pageout(&vo, &og) > 0 ) {
1521                                 audiotime = vorbis_granule_time(&vd, apage.load(&og));
1522                         }
1523                 }
1524                 if( !audio && vpage.valid )
1525                         write_video_page();
1526                 else if( !video && apage.valid )
1527                         write_audio_page();
1528                 else if( !vpage.valid || !apage.valid )
1529                         break;
1530 // output earliest page
1531                 else if( videotime > audiotime ) // output earliest
1532                         write_audio_page();
1533                 else
1534                         write_video_page();
1535         }
1536         if( last ) {  // at last
1537                 if( vpage.valid )
1538                         write_video_page();
1539                 if( apage.valid )
1540                         write_audio_page();
1541         }
1542         file_lock->unlock();
1543 }
1544
1545
1546 int FileOGG::write_samples_vorbis(double **buffer, int64_t len, int last)
1547 {
1548         if( !audio || !out ) return 1;
1549         flush_ogg(0);
1550         if( !last ) {
1551                 float **vorbis_buffer = vorbis_analysis_buffer(&vd, len);
1552                 for( int i=0; i<asset->channels; ++i ) // double to float
1553                         for( int j=0; j < len; ++j )
1554                                 vorbis_buffer[i][j] = buffer[i][j];
1555         }
1556         else
1557                 len = 0;
1558         vorbis_analysis_wrote(&vd, len);
1559
1560         while( vorbis_analysis_blockout(&vd, &vb) == 1 ) {
1561                 vorbis_analysis(&vb, 0);
1562                 vorbis_bitrate_addblock(&vb);
1563                 ogg_packet op;
1564                 while( vorbis_bitrate_flushpacket(&vd, &op) ) {
1565                         file_lock->lock("FileOGG::write_vorbis_audio");
1566                         ogg_stream_packetin(&vo, &op);
1567                         ++apage.packets;
1568                         file_lock->unlock();
1569                 }
1570         }
1571         return 0;
1572 }
1573
1574 int FileOGG::write_samples(double **buffer, int64_t len)
1575 {
1576         if (len > 0)
1577                 return write_samples_vorbis(buffer, len, 0);
1578         return 0;
1579 }
1580
1581
1582 int FileOGG::write_frames_theora(VFrame ***frames, int len, int last)
1583 {
1584         if( !video || !out ) return 1;
1585         for( int j=0; j<len; ++j ) {
1586                 flush_ogg(0);
1587                 if( temp_frame ) {
1588                         th_ycbcr_buffer ycbcr;
1589                         ycbcr[0].width = frame_w;
1590                         ycbcr[0].height = frame_h;
1591                         ycbcr[0].stride = temp_frame->get_bytes_per_line();
1592                         ycbcr[0].data = temp_frame->get_y();
1593                         ycbcr[1].width = frame_w/2;
1594                         ycbcr[1].height = frame_h/2;
1595                         ycbcr[1].stride = (temp_frame->get_bytes_per_line()+1)/2;
1596                         ycbcr[1].data = temp_frame->get_u();
1597                         ycbcr[2].width = frame_w/2;
1598                         ycbcr[2].height = frame_h/2;
1599                         ycbcr[2].stride = (temp_frame->get_bytes_per_line()+1)/2;
1600                         ycbcr[2].data = temp_frame->get_v();
1601                         if( th_encode_ycbcr_in(enc, ycbcr) ) {
1602                                 eprintf(_("th_encode_ycbcr_in failed"));
1603                                 return 1;
1604                         }
1605                         ogg_packet op;
1606                         while( th_encode_packetout(enc, last, &op) > 0 ) {
1607                                 file_lock->lock();
1608                                 ogg_stream_packetin (&to, &op);
1609                                 ++vpage.packets;
1610                                 file_lock->unlock();
1611                         }
1612                 }
1613                 if( last ) return 0;
1614                 if( !temp_frame )
1615                         temp_frame = new VFrame (0, -1, frame_w, frame_h, theora_cmodel, -1);
1616                 VFrame *frame = frames[0][j];
1617                 temp_frame->transfer_from(frame);
1618         }
1619         return 0;
1620 }
1621
1622 int FileOGG::write_frames(VFrame ***frames, int len)
1623 {
1624         return write_frames_theora(frames, len, 0);
1625 }
1626
1627
1628
1629 OGGConfigAudio::OGGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
1630  : BC_Window(PROGRAM_NAME ": Audio Compression",
1631         parent_window->get_abs_cursor_x(1),
1632         parent_window->get_abs_cursor_y(1),
1633         xS(350), yS(250))
1634 {
1635         this->parent_window = parent_window;
1636         this->asset = asset;
1637 // *** CONTEXT_HELP ***
1638         context_help_set_keyword("Single File Rendering");
1639 }
1640
1641 OGGConfigAudio::~OGGConfigAudio()
1642 {
1643
1644 }
1645
1646 void OGGConfigAudio::create_objects()
1647 {
1648 //      add_tool(new BC_Title(10, 10, _("There are no audio options for this format")));
1649
1650         int x = xS(10), y = yS(10);
1651         int x1 = xS(150);
1652         char string[BCTEXTLEN];
1653
1654         lock_window("OGGConfigAudio::create_objects");
1655         add_tool(fixed_bitrate = new OGGVorbisFixedBitrate(x, y, this));
1656         add_tool(variable_bitrate = new OGGVorbisVariableBitrate(x + fixed_bitrate->get_w() + xS(5),
1657                 y,
1658                 this));
1659
1660         y += yS(30);
1661         sprintf(string, "%d", asset->vorbis_min_bitrate);
1662         add_tool(new BC_Title(x, y, _("Min bitrate:")));
1663         add_tool(new OGGVorbisMinBitrate(x1, y, this, string));
1664
1665         y += yS(30);
1666         add_tool(new BC_Title(x, y, _("Avg bitrate:")));
1667         sprintf(string, "%d", asset->vorbis_bitrate);
1668         add_tool(new OGGVorbisAvgBitrate(x1, y, this, string));
1669
1670         y += yS(30);
1671         add_tool(new BC_Title(x, y, _("Max bitrate:")));
1672         sprintf(string, "%d", asset->vorbis_max_bitrate);
1673         add_tool(new OGGVorbisMaxBitrate(x1, y, this, string));
1674
1675
1676         add_subwindow(new BC_OKButton(this));
1677         show_window(1);
1678         unlock_window();
1679 }
1680
1681 int OGGConfigAudio::close_event()
1682 {
1683         set_done(0);
1684         return 1;
1685 }
1686
1687 OGGVorbisFixedBitrate::OGGVorbisFixedBitrate(int x, int y, OGGConfigAudio *gui)
1688  : BC_Radial(x, y, !gui->asset->vorbis_vbr, _("Average bitrate"))
1689 {
1690         this->gui = gui;
1691 }
1692 int OGGVorbisFixedBitrate::handle_event()
1693 {
1694         gui->asset->vorbis_vbr = 0;
1695         gui->variable_bitrate->update(0);
1696         return 1;
1697 }
1698
1699 OGGVorbisVariableBitrate::OGGVorbisVariableBitrate(int x, int y, OGGConfigAudio *gui)
1700  : BC_Radial(x, y, gui->asset->vorbis_vbr, _("Variable bitrate"))
1701 {
1702         this->gui = gui;
1703 }
1704 int OGGVorbisVariableBitrate::handle_event()
1705 {
1706         gui->asset->vorbis_vbr = 1;
1707         gui->fixed_bitrate->update(0);
1708         return 1;
1709 }
1710
1711
1712 OGGVorbisMinBitrate::OGGVorbisMinBitrate(int x,
1713         int y,
1714         OGGConfigAudio *gui,
1715         char *text)
1716  : BC_TextBox(x, y, xS(180), 1, text)
1717 {
1718         this->gui = gui;
1719 }
1720 int OGGVorbisMinBitrate::handle_event()
1721 {
1722         gui->asset->vorbis_min_bitrate = atol(get_text());
1723         return 1;
1724 }
1725
1726
1727
1728 OGGVorbisMaxBitrate::OGGVorbisMaxBitrate(int x,
1729         int y,
1730         OGGConfigAudio *gui,
1731         char *text)
1732  : BC_TextBox(x, y, xS(180), 1, text)
1733 {
1734         this->gui = gui;
1735 }
1736 int OGGVorbisMaxBitrate::handle_event()
1737 {
1738         gui->asset->vorbis_max_bitrate = atol(get_text());
1739         return 1;
1740 }
1741
1742
1743
1744 OGGVorbisAvgBitrate::OGGVorbisAvgBitrate(int x, int y, OGGConfigAudio *gui, char *text)
1745  : BC_TextBox(x, y, xS(180), 1, text)
1746 {
1747         this->gui = gui;
1748 }
1749 int OGGVorbisAvgBitrate::handle_event()
1750 {
1751         gui->asset->vorbis_bitrate = atol(get_text());
1752         return 1;
1753 }
1754
1755
1756
1757
1758
1759 OGGConfigVideo::OGGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
1760  : BC_Window(PROGRAM_NAME ": Video Compression",
1761         parent_window->get_abs_cursor_x(1),
1762         parent_window->get_abs_cursor_y(1),
1763         xS(450), yS(220))
1764 {
1765         this->parent_window = parent_window;
1766         this->asset = asset;
1767 // *** CONTEXT_HELP ***
1768         context_help_set_keyword("Single File Rendering");
1769 }
1770
1771 OGGConfigVideo::~OGGConfigVideo()
1772 {
1773
1774 }
1775
1776 void OGGConfigVideo::create_objects()
1777 {
1778 //      add_tool(new BC_Title(10, 10, _("There are no video options for this format")));
1779         int x = xS(10), y = yS(10);
1780         int x1 = x + xS(150);
1781         int x2 = x + xS(300);
1782
1783         lock_window("OGGConfigVideo::create_objects");
1784         BC_Title *title;
1785         add_subwindow(title = new BC_Title(x, y, _("Bitrate:")));
1786         add_subwindow(new OGGTheoraBitrate(x + title->get_w() + xS(5), y, this));
1787         add_subwindow(fixed_bitrate = new OGGTheoraFixedBitrate(x2, y, this));
1788         y += yS(30);
1789
1790         add_subwindow(new BC_Title(x, y, _("Quality:")));
1791         add_subwindow(new BC_ISlider(x + xS(80), y, 0, xS(200), xS(200),
1792                 0, 63, asset->theora_quality,
1793                 0, 0, &asset->theora_quality));
1794
1795
1796         add_subwindow(fixed_quality = new OGGTheoraFixedQuality(x2, y, this));
1797         y += yS(30);
1798
1799         add_subwindow(new BC_Title(x, y, _("Keyframe frequency:")));
1800         OGGTheoraKeyframeFrequency *keyframe_frequency =
1801                 new OGGTheoraKeyframeFrequency(x1 + xS(60), y, this);
1802         keyframe_frequency->create_objects();
1803         y += yS(30);
1804
1805         add_subwindow(new BC_Title(x, y, _("Keyframe force frequency:")));
1806         OGGTheoraKeyframeForceFrequency *keyframe_force_frequency =
1807                 new OGGTheoraKeyframeForceFrequency(x1 + xS(60), y, this);
1808         keyframe_force_frequency->create_objects();
1809         y += yS(30);
1810
1811         add_subwindow(new BC_Title(x, y, _("Sharpness:")));
1812         OGGTheoraSharpness *sharpness =
1813                 new OGGTheoraSharpness(x1 + xS(60), y, this);
1814         sharpness->create_objects();
1815         y += yS(30);
1816
1817
1818         add_subwindow(new BC_OKButton(this));
1819         show_window(1);
1820         unlock_window();
1821 }
1822
1823
1824
1825
1826 int OGGConfigVideo::close_event()
1827 {
1828         set_done(0);
1829         return 1;
1830 }
1831
1832 OGGTheoraBitrate::OGGTheoraBitrate(int x, int y, OGGConfigVideo *gui)
1833  : BC_TextBox(x, y, xS(100), 1, gui->asset->theora_bitrate)
1834 {
1835         this->gui = gui;
1836 }
1837
1838
1839 int OGGTheoraBitrate::handle_event()
1840 {
1841         // TODO: MIN / MAX check
1842         gui->asset->theora_bitrate = atol(get_text());
1843         return 1;
1844 };
1845
1846
1847
1848
1849 OGGTheoraFixedBitrate::OGGTheoraFixedBitrate(int x, int y, OGGConfigVideo *gui)
1850  : BC_Radial(x, y, gui->asset->theora_fix_bitrate, _("Fixed bitrate"))
1851 {
1852         this->gui = gui;
1853 }
1854
1855 int OGGTheoraFixedBitrate::handle_event()
1856 {
1857         update(1);
1858         gui->asset->theora_fix_bitrate = 1;
1859         gui->fixed_quality->update(0);
1860         return 1;
1861 };
1862
1863 OGGTheoraFixedQuality::OGGTheoraFixedQuality(int x, int y, OGGConfigVideo *gui)
1864  : BC_Radial(x, y, !gui->asset->theora_fix_bitrate, _("Fixed quality"))
1865 {
1866         this->gui = gui;
1867 }
1868
1869 int OGGTheoraFixedQuality::handle_event()
1870 {
1871         update(1);
1872         gui->asset->theora_fix_bitrate = 0;
1873         gui->fixed_bitrate->update(0);
1874         return 1;
1875 };
1876
1877 OGGTheoraKeyframeFrequency::OGGTheoraKeyframeFrequency(int x, int y, OGGConfigVideo *gui)
1878  : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency,
1879         (int64_t)1, (int64_t)500, x, y, xS(40))
1880 {
1881         this->gui = gui;
1882 }
1883
1884 int OGGTheoraKeyframeFrequency::handle_event()
1885 {
1886         gui->asset->theora_keyframe_frequency = atol(get_text());
1887         return 1;
1888 }
1889
1890 OGGTheoraKeyframeForceFrequency::OGGTheoraKeyframeForceFrequency(int x, int y, OGGConfigVideo *gui)
1891  : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_keyframe_frequency,
1892         (int64_t)1, (int64_t)500, x, y, xS(40))
1893 {
1894         this->gui = gui;
1895 }
1896
1897 int OGGTheoraKeyframeForceFrequency::handle_event()
1898 {
1899         gui->asset->theora_keyframe_frequency = atol(get_text());
1900         return 1;
1901 }
1902
1903
1904 OGGTheoraSharpness::OGGTheoraSharpness(int x, int y, OGGConfigVideo *gui)
1905  : BC_TumbleTextBox(gui, (int64_t)gui->asset->theora_sharpness,
1906         (int64_t)0, (int64_t)2, x, y, xS(40))
1907 {
1908         this->gui = gui;
1909 }
1910
1911 int OGGTheoraSharpness::handle_event()
1912 {
1913         gui->asset->theora_sharpness = atol(get_text());
1914         return 1;
1915 }
1916
1917
1918 #endif