theorautils.c 63.9 KB
Newer Older
1
/* -*- tab-width:4;c-file-style:"cc-mode"; -*- */
2
/*
Jan Gerber's avatar
Jan Gerber committed
3
 * theorautils.c - Ogg Theora/Ogg Vorbis Abstraction and Muxing
4
 * Copyright (C) 2003-2011 <j@v2v.cc>
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
10
11
12
13
14
15
16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
17
 * along with This program.  If not, see <http://www.gnu.org/licenses/>.
18
19
 */

20
21
22
23
24
25
26
27
28
29
30
31
32
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_LARGEFILE_SOURCE)
#define _LARGEFILE_SOURCE
#endif
#if !defined(_LARGEFILE64_SOURCE)
#define _LARGEFILE64_SOURCE
#endif
#if !defined(_FILE_OFFSET_BITS)
#define _FILE_OFFSET_BITS 64
#endif

33
#ifndef WIN32
34
35
36
#if !defined(off64_t)
#define off64_t off_t
#endif
37
#endif
38
39
40
41
42
43

#ifdef WIN32
#define fseeko fseeko64
#define ftello ftello64
#endif

44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
48
#include <time.h>
49
50
#include <sys/stat.h>
#include <assert.h>
51
#include <math.h>
Jan Gerber's avatar
Jan Gerber committed
52
#include <limits.h>
53

Jan Gerber's avatar
Jan Gerber committed
54
#include "theora/theoraenc.h"
55
56
#include "vorbis/codec.h"
#include "vorbis/vorbisenc.h"
57
58
59
#ifdef HAVE_OGGKATE
#include "kate/oggkate.h"
#endif
60
61
62

#include "theorautils.h"

63

Jan Gerber's avatar
Jan Gerber committed
64
void init_info(oggmux_info *info) {
65
    info->output_seekable = MAYBE_SEEKABLE;
Jan Gerber's avatar
Jan Gerber committed
66
    info->with_skeleton = 1; /* skeleton is enabled by default    */
67
    info->skeleton_3 = 0; /* by default, output skeleton 4 with keyframe indexes. */
68
    info->index_interval = 2000;
Jan Gerber's avatar
Jan Gerber committed
69
70
    info->theora_index_reserve = -1;
    info->vorbis_index_reserve = -1;
71
    info->kate_index_reserve = -1;
72
    info->indexing_complete = 0;
73
    info->frontend = NULL; /*frontend mode*/
74
75
    info->videotime =  0;
    info->audiotime = 0;
Jan Gerber's avatar
Jan Gerber committed
76
77
    info->audio_bytesout = 0;
    info->video_bytesout = 0;
78
    info->kate_bytesout = 0;
Jan Gerber's avatar
Jan Gerber committed
79

80
81
82
83
84
85
    info->videopage_valid = 0;
    info->audiopage_valid = 0;
    info->audiopage_buffer_length = 0;
    info->videopage_buffer_length = 0;
    info->audiopage = NULL;
    info->videopage = NULL;
86
87
    info->start_time = time(NULL);
    info->duration = -1;
Jan Gerber's avatar
Jan Gerber committed
88
    info->speed_level = -1;
Jan Gerber's avatar
Jan Gerber committed
89

90
91
    info->v_pkg=0;
    info->a_pkg=0;
92
    info->k_pkg=0;
93
94
95
#ifdef OGGMUX_DEBUG
    info->a_page=0;
    info->v_page=0;
96
    info->k_page=0;
97
#endif
98

Jan Gerber's avatar
Jan Gerber committed
99
100
101
102
    info->twopass_file = NULL;
    info->twopass = 0;
    info->passno = 0;

103
104
    info->with_kate = 0;
    info->n_kate_streams = 0;
105
    info->kate_streams = NULL;
106
107

    info->prev_vorbis_window = -1;
108
    info->content_offset = 0;
109
110

    info->serialno = 0;
111
112
113
114
115
116
117
}

void oggmux_setup_kate_streams(oggmux_info *info, int n_kate_streams)
{
    int n;

    info->n_kate_streams = n_kate_streams;
118
    info->kate_streams = NULL;
119
120
121
122
123
124
125
126
    if (n_kate_streams == 0) return;
    info->kate_streams = (oggmux_kate_stream*)malloc(n_kate_streams*sizeof(oggmux_kate_stream));
    for (n=0; n<n_kate_streams; ++n) {
        oggmux_kate_stream *ks=info->kate_streams+n;
        ks->katepage_valid = 0;
        ks->katepage_buffer_length = 0;
        ks->katepage = NULL;
        ks->katetime = 0;
127
        ks->last_end_time = -1;
128
129
130
131
132
    }
}

static void write16le(unsigned char *ptr,ogg_uint16_t v)
{
Jan Gerber's avatar
Jan Gerber committed
133
134
    ptr[0]=v&0xff;
    ptr[1]=(v>>8)&0xff;
135
136
137
138
}

static void write32le(unsigned char *ptr,ogg_uint32_t v)
{
Jan Gerber's avatar
Jan Gerber committed
139
140
141
142
    ptr[0]=v&0xff;
    ptr[1]=(v>>8)&0xff;
    ptr[2]=(v>>16)&0xff;
    ptr[3]=(v>>24)&0xff;
143
144
145
146
}

static void write64le(unsigned char *ptr,ogg_int64_t v)
{
Jan Gerber's avatar
Jan Gerber committed
147
148
149
150
151
152
153
154
155
    ogg_uint32_t hi=v>>32;
    ptr[0]=v&0xff;
    ptr[1]=(v>>8)&0xff;
    ptr[2]=(v>>16)&0xff;
    ptr[3]=(v>>24)&0xff;
    ptr[4]=hi&0xff;
    ptr[5]=(hi>>8)&0xff;
    ptr[6]=(hi>>16)&0xff;
    ptr[7]=(hi>>24)&0xff;
156
157
}

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* Write an ogg page to the output file. The first time this is called, we
   determine the seekable-ness of the output stream, and store the result
   in info->output_seekable. */
static void
write_page(oggmux_info* info, ogg_page* page)
{
    int x;
    assert(page->header_len > 0);
    x = fwrite(page->header, 1, page->header_len, info->outfile);
    if (x != page->header_len) {
        fprintf(stderr, "FAILURE: Failed to write page header to disk!\n");
        exit(1);
    }
    x = fwrite(page->body, 1, page->body_len, info->outfile);
    if (x != page->body_len) {
        fprintf(stderr, "FAILURE: Failed to write page body to disk!\n");
        exit(1);
    }
    if (info->output_seekable == MAYBE_SEEKABLE) {
        /* This is our first page write. Determine if the output
           is seekable. */
        ogg_int64_t offset = ftello(info->outfile);
        if (offset == -1 || fseeko(info->outfile, 0, SEEK_SET) < 0) {
            info->output_seekable = NOT_SEEKABLE;
        } else {
            /* Output appears to be seekable, seek the write cursor back
               to previous position. */
            info->output_seekable = SEEKABLE;
            assert(info->output_seekable > 0);
            if (fseeko(info->outfile, offset, SEEK_SET) < 0) {
                fprintf(stderr, "ERROR: failed to seek in seekable output file!?!\n");
                exit (1);
            }  
        }
    }
    /* We should know the seekableness by now... */
    assert(info->output_seekable != MAYBE_SEEKABLE);
}

197
198
199
static ogg_int64_t output_file_length(oggmux_info* info)
{
    ogg_int64_t offset, length;
200
    if (info->skeleton_3 || !info->indexing_complete) {
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
        return -1;
    }
    offset = ftello(info->outfile);
    if (fseeko(info->outfile, 0, SEEK_END) < 0) {
        fprintf(stderr, "ERROR: Can't seek output file to write index!\n");
        return -1;
    }
    length = ftello(info->outfile);
    if (fseeko(info->outfile, offset, SEEK_SET) < 0) {
        fprintf(stderr, "ERROR: Can't seek output file to write index!\n");
        return -1;
    }

    return length;
}


void add_fishead_packet (oggmux_info *info,
                         ogg_uint16_t ver_maj,
                         ogg_uint16_t ver_min) {
221
    ogg_packet op;
222
223
224
    size_t packet_size = 0;
    ogg_uint32_t version = SKELETON_VERSION(ver_maj, ver_min);

225
    assert(version >= SKELETON_VERSION(3,0) ||
226
           version == SKELETON_VERSION(4,0));
227

228
    packet_size = (version == SKELETON_VERSION(4,0)) ? 80 : 64;
229
230

    memset (&op, 0, sizeof (op));
Jan Gerber's avatar
Jan Gerber committed
231

232
    op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
233
234
    if (op.packet == NULL) return;

235
    memset (op.packet, 0, packet_size);
236
    memcpy (op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */
237
238
    write16le(op.packet+8, ver_maj); /* version major */
    write16le(op.packet+10, ver_min); /* version minor */
239
240
241
242
    write64le(op.packet+12, (ogg_int64_t)0); /* presentationtime numerator */
    write64le(op.packet+20, (ogg_int64_t)1000); /* presentationtime denominator */
    write64le(op.packet+28, (ogg_int64_t)0); /* basetime numerator */
    write64le(op.packet+36, (ogg_int64_t)1000); /* basetime denominator */
243
    /* both the numerator are zero hence handled by the memset */
244
    write32le(op.packet+44, 0); /* UTC time, set to zero for now */
Jan Gerber's avatar
Jan Gerber committed
245

246
    /* Index start/end time, if unknown or non-indexed, will be -1. */
247
248
249
    if (version == SKELETON_VERSION(4,0)) {
        write64le(op.packet+64, output_file_length(info));
        write64le(op.packet+72, info->content_offset);
250
    }
251
252
    op.b_o_s = 1; /* its the first packet of the stream */
    op.e_o_s = 0; /* its not the last packet of the stream */
253
    op.bytes = packet_size; /* length of the packet in bytes */
254
255
256
257
258

    ogg_stream_packetin (&info->so, &op); /* adding the packet to the skeleton stream */
    _ogg_free (op.packet);
}

259
260
261
262
263
264
265
266
267
268
269
270
271
const char* theora_message_headers = "Content-Type: video/theora\r\n"
                                     "Role: video/main\r\n"
                                     "Name: video_1\r\n";

const char* vorbis_message_headers = "Content-Type: audio/vorbis\r\n"
                                     "Role: audio/main\r\n"
                                     "Name: audio_1\r\n";
#ifdef HAVE_KATE
const char* kate_message_headers =   "Content-Type: application/x-kate\r\n\r\n"
                                     "Role: text/subtitle\r\n";
                                     /* Dynamically add name header... */
#endif

272
273
274
275
276
/*
 * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
 */
void add_fisbone_packet (oggmux_info *info) {
    ogg_packet op;
277
    size_t packet_size = 0;
278
    if (!info->audio_only) {
Jan Gerber's avatar
Jan Gerber committed
279
        memset (&op, 0, sizeof (op));
280
281
        packet_size = FISBONE_SIZE + strlen(theora_message_headers);
        op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
282
283
        if (op.packet == NULL) return;

284
        memset (op.packet, 0, packet_size);
Jan Gerber's avatar
Jan Gerber committed
285
        /* it will be the fisbone packet for the theora video */
286
        memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
287
288
289
        write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
        write32le(op.packet+12, info->to.serialno); /* serialno of the theora stream */
        write32le(op.packet+16, 3); /* number of header packets */
Jan Gerber's avatar
Jan Gerber committed
290
        /* granulerate, temporal resolution of the bitstream in samples/microsecond */
291
292
293
294
        write64le(op.packet+20, info->ti.fps_numerator); /* granulrate numerator */
        write64le(op.packet+28, info->ti.fps_denominator); /* granulrate denominator */
        write64le(op.packet+36, 0); /* start granule */
        write32le(op.packet+44, 0); /* preroll, for theora its 0 */
Jan Gerber's avatar
Jan Gerber committed
295
        *(op.packet+48) = info->ti.keyframe_granule_shift; /* granule shift */
296
297
        /* message header fields */
        memcpy(op.packet+FISBONE_SIZE, theora_message_headers, strlen(theora_message_headers));
Jan Gerber's avatar
Jan Gerber committed
298
299
300

        op.b_o_s = 0;
        op.e_o_s = 0;
301
        op.bytes = packet_size; /* size of the packet in bytes */
Jan Gerber's avatar
Jan Gerber committed
302

303
        ogg_stream_packetin (&info->so, &op);
Jan Gerber's avatar
Jan Gerber committed
304
        _ogg_free (op.packet);
305
306
307
    }

    if (!info->video_only) {
Jan Gerber's avatar
Jan Gerber committed
308
        memset (&op, 0, sizeof (op));
309
310
        packet_size = FISBONE_SIZE + strlen(vorbis_message_headers);
        op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
311
312
        if (op.packet == NULL) return;

313
        memset (op.packet, 0, packet_size);
314
        /* it will be the fisbone packet for the vorbis audio */
Jan Gerber's avatar
Jan Gerber committed
315
        memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
316
317
318
        write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
        write32le(op.packet+12, info->vo.serialno); /* serialno of the vorbis stream */
        write32le(op.packet+16, 3); /* number of header packet */
Jan Gerber's avatar
Jan Gerber committed
319
        /* granulerate, temporal resolution of the bitstream in Hz */
320
321
322
323
        write64le(op.packet+20, info->sample_rate); /* granulerate numerator */
        write64le(op.packet+28, (ogg_int64_t)1); /* granulerate denominator */
        write64le(op.packet+36, 0); /* start granule */
        write32le(op.packet+44, 2); /* preroll, for vorbis its 2 */
Jan Gerber's avatar
Jan Gerber committed
324
        *(op.packet+48) = 0; /* granule shift, always 0 for vorbis */
325
326
        memcpy(op.packet+FISBONE_SIZE, vorbis_message_headers, strlen(vorbis_message_headers));

Jan Gerber's avatar
Jan Gerber committed
327
328
329
330
        /* Important: Check the case of Content-Type for correctness */

        op.b_o_s = 0;
        op.e_o_s = 0;
331
        op.bytes = packet_size;
Jan Gerber's avatar
Jan Gerber committed
332

333
        ogg_stream_packetin (&info->so, &op);
Jan Gerber's avatar
Jan Gerber committed
334
        _ogg_free (op.packet);
335
    }
336
337
338

#ifdef HAVE_KATE
    if (info->with_kate) {
Jan Gerber's avatar
Jan Gerber committed
339
        int n;
340
        char name[32];
341
        for (n=0; n<info->n_kate_streams; ++n) {
342
343
344
            size_t packet_size = 0;
            int message_headers_len = strlen(kate_message_headers);
            int name_len = 0;
345
            oggmux_kate_stream *ks=info->kate_streams+n;
346
347
348
349
350
351
            memset (&op, 0, sizeof (op));
            sprintf(name, "Name: %d\r\n", (n+1));
            name_len = strlen(name);
            packet_size = FISBONE_SIZE + message_headers_len + name_len;
            op.packet = _ogg_calloc (packet_size, sizeof(unsigned char));
            memset (op.packet, 0, packet_size);
352
            /* it will be the fisbone packet for the kate stream */
353
            memcpy (op.packet, FISBONE_IDENTIFIER, 8); /* identifier */
354
            write32le(op.packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
355
            write32le(op.packet+12, ks->ko.serialno); /* serialno of the vorbis stream */
356
            write32le(op.packet+16, ks->ki.num_headers); /* number of header packet */
357
358
            /* granulerate, temporal resolution of the bitstream in Hz */
            write64le(op.packet+20, ks->ki.gps_numerator); /* granulerate numerator */
359
            write64le(op.packet+28, ks->ki.gps_denominator); /* granulerate denominator */
360
            write64le(op.packet+36, 0); /* start granule */
361
            write32le(op.packet+44, 0); /* preroll, for kate it's 0 */
362
363
364
365
            *(op.packet+48) = ks->ki.granule_shift; /* granule shift */
            memcpy (op.packet+FISBONE_SIZE, kate_message_headers, message_headers_len);
            memcpy (op.packet+FISBONE_SIZE+message_headers_len, name, name_len);
            /* Important: Check the case of Content-Type for correctness */
Jan Gerber's avatar
Jan Gerber committed
366

367
368
369
            op.b_o_s = 0;
            op.e_o_s = 0;
            op.bytes = packet_size;
Jan Gerber's avatar
Jan Gerber committed
370

371
            ogg_stream_packetin (&info->so, &op);
372
            _ogg_free (op.packet);
373
374
375
        }
    }
#endif
376
377
}

378
379
380
381
382
383
static int keypoints_per_index(seek_index* index, double duration)
{
    double keypoints_per_second = (double)index->packet_interval / 1000.0;
    return !keypoints_per_second ? 0 : ((int)ceil(duration / keypoints_per_second) + 2);
}

Jan Gerber's avatar
Jan Gerber committed
384
385
/* Creates a new index packet, with |bytes| space set aside for index. */ 
static int create_index_packet(size_t bytes,
386
387
                               ogg_packet* op,
                               ogg_uint32_t serialno,
388
                               ogg_int64_t num_used_keypoints)
389
{
390
    size_t size = 42 + bytes;
391
    memset (op, 0, sizeof(*op));
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    op->packet = malloc(size);
    if (op->packet == NULL)
        return -1;
    memset(op->packet, -1, size);
    op->b_o_s = 0;
    op->e_o_s = 0;
    op->bytes = size;

    /* Write identifier bytes into packet. */
    memcpy(op->packet, "index", 6);

    /* Write the serialno into packet. */
    write32le(op->packet+6, serialno);

    /* Write number of valid keypoints in index into packet. */
407
    write64le(op->packet+10, num_used_keypoints);
408

409
    /* Write timestamp denominator, times are in milliseconds, so 1000. */
410
    write64le(op->packet+18, (ogg_int64_t)1000);
411

412
413
414
415
416
417
    /* Write first sample time numerator. */
    write64le(op->packet+26, (ogg_int64_t)0);

    /* Write last sample time numerator. */
    write64le(op->packet+34, (ogg_int64_t)0);

418
419
420
421
422
423
424
425
426
427
428
429
    return 0;
}

/*
 * Writes a placeholder index page for a particular stream.
 */
static int write_index_placeholder_for_stream (oggmux_info *info,
                                               seek_index* index,
                                               ogg_uint32_t serialno)
{
    ogg_packet op;
    ogg_page og;
Jan Gerber's avatar
Jan Gerber committed
430
    int num_keypoints = keypoints_per_index(index,
431
                                            info->duration);
Jan Gerber's avatar
Jan Gerber committed
432
433
434
435
436
437
438
439
    if (index->packet_size == -1) {
        index->packet_size = (int)(num_keypoints * 5.1);
    }
    if (create_index_packet(index->packet_size,
                            &op,
                            serialno,
                            0) == -1)
    {
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
        return -1;
    }

    seek_index_set_max_keypoints(index, num_keypoints);

    /* Remember where we wrote the index pages, so that we can overwrite them
       once we've encoded the entire file. */
    index->page_location = ftello(info->outfile);

    /* There should be no packets in the stream. */
    assert(ogg_stream_flush(&info->so, &og) == 0);

    ogg_stream_packetin(&info->so, &op);
    free(op.packet);

    while (ogg_stream_flush(&info->so, &og)) {
        assert(index->page_location != 0);
        write_page (info, &og);
    }

    return 0;
}

Jan Gerber's avatar
Jan Gerber committed
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
/* Counts number of bytes required to encode n with variable byte encoding. */
static int bytes_required(ogg_int64_t n) {
    int bits = 0;
    int bytes = 0;
    assert(n >= 0);
    /* Determine number of bits required. */
    while (n) {
        n = n >> 1;
        bits++;
    }
    /* 7 bits per byte, plus 1 if we spill over onto the next byte. */
    bytes = bits / 7;
    return bytes + (((bits % 7) != 0 || bits == 0) ? 1 : 0);
}

static unsigned char*
write_vl_int(unsigned char* p, const unsigned char* limit, ogg_int64_t n)
{
    ogg_int64_t k = n;
    unsigned char* x = p;
    assert(n >= 0);
    do {
        if (p >= limit) {
            return p;
        }
        unsigned char b = (unsigned char)(k & 0x7f);
        k >>= 7;
        if (k == 0) {
            // Last byte, add terminating bit.
            b |= 0x80;
        }
        *p = b;
        p++;
    } while (k && p < limit);
    assert(x + bytes_required(n) == p);

    return p;
}

502
503
504
505
506
507
508
typedef struct {
    ogg_int64_t offset;
    ogg_int64_t time;
} keypoint;

/* Overwrites pages on disk for a stream's index with actual index data. */
static int
509
write_index_pages (seek_index* index,
Jan Gerber's avatar
Jan Gerber committed
510
                   const char* name,
511
512
                   oggmux_info *info,
                   ogg_uint32_t serialno,
513
514
                   int target_packet,
                   int num_headers)
515
516
517
518
{
    ogg_packet op;
    ogg_page og;
    int i;
519
    int k = 0;
520
521
522
    int result;
    int num_keypoints;
    int packetno;
523
    int last_packetno = num_headers-1; /* Take into account header packets... */
524
525
    keypoint* keypoints = 0;
    int pageno = 0;
526
527
528
    int prev_keyframe_pageno = -INT_MAX;
    ogg_int64_t prev_keyframe_start_time = -INT_MAX;
    int packet_in_page = 0;
Jan Gerber's avatar
Jan Gerber committed
529
530
531
532
533
534
    unsigned char* p = 0;
    ogg_int64_t prev_offset = 0;
    ogg_int64_t prev_time = 0;
    const unsigned char* limit = 0;
    int index_bytes = 0;  
    int keypoints_cutoff = 0;
535

536
537
538
539
540
541
    /* Must have indexed keypoints to go on */
    if (index->max_keypoints == 0 || index->packet_num == 0) {
      fprintf(stderr, "WARNING: no key points for %s stream %08x\n", name, serialno);
      return 0;
    }

542
543
544
    /* Must have placeholder packet to rewrite. */
    assert(index->page_location > 0);

545
    num_keypoints = index->max_keypoints;
546
547
548
549
550
551
552
553

    /* Calculate and store the keypoints. */
    keypoints = (keypoint*)malloc(sizeof(keypoint) * num_keypoints);
    if (!keypoints) {
        fprintf(stderr, "ERROR: malloc failure in rewrite_index_page\n");
        return -1;
    }
    memset(keypoints, -1, sizeof(keypoint) * num_keypoints);
554

Jan Gerber's avatar
Jan Gerber committed
555
556
    prev_offset = 0;
    prev_time = 0;
557
    for (i=0; i < index->packet_num && k < num_keypoints; i++) {
558
        packetno = index->packets[i].packetno;
559
        /* Increment pageno until we find the page which contains the start of
560
561
         * the keyframe's packet. */
        while (pageno < index->pages_num &&
562
               last_packetno + index->pages[pageno].packet_start_num < packetno)
563
        {
564
            last_packetno += index->pages[pageno].packet_start_num;
565
566
567
            pageno++;
        }
        assert(pageno < index->pages_num);
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
    
        if (prev_keyframe_pageno != pageno) {
            /* First keyframe/sample in this page. */
            packet_in_page = 1;
            prev_keyframe_pageno = pageno;
        } else {
            packet_in_page++;
        }

        if (packet_in_page != target_packet ||
            index->packets[i].start_time <= (prev_keyframe_start_time + index->packet_interval)) {
            /* Either this isn't the keyframe we want to index on this page, or
               the keyframe occurs too close to the previously indexed one, so
               skip to the next one. */
            continue;
        }

        /* Add to final keyframe index. */        
        keypoints[k].offset = index->pages[pageno].offset;
        keypoints[k].time = index->packets[i].start_time;
Jan Gerber's avatar
Jan Gerber committed
588
589
590
591
592
593
594
        
        /* Count how many bytes is required to encode this keypoint. */
        index_bytes += bytes_required(keypoints[k].offset - prev_offset);
        prev_offset = keypoints[k].offset;
        index_bytes += bytes_required(keypoints[k].time - prev_time);
        prev_time = keypoints[k].time;

595
        k++;
Jan Gerber's avatar
Jan Gerber committed
596
597
598
599
600

        if (index_bytes < index->packet_size) {
            keypoints_cutoff = k;
        }

601
        prev_keyframe_start_time = index->packets[i].start_time;
602
    }
Jan Gerber's avatar
Jan Gerber committed
603
604
605
606
607
    if (index_bytes > index->packet_size) {
        printf("WARNING: Underestimated space for %s keyframe index, dropped %d keyframes, "
               "only part of the file may be indexed. Rerun with --%s-index-reserve %d to "
               "ensure a complete index, or use OggIndex to re-index.\n",
               name, (k - keypoints_cutoff), name, index_bytes);
608
609
610
611
    } else if (index_bytes < index->packet_size &&
               index->packet_size - index_bytes > 10000)
    {
        /* We over estimated the index size by 10,000 bytes or more. */
Jan Gerber's avatar
Jan Gerber committed
612
        printf("Allocated %d bytes for %s keyframe index, %d are unused. "
613
               "Index contains %d keyframes. "
Jan Gerber's avatar
Jan Gerber committed
614
615
616
               "Rerun with '--%s-index-reserve %d' to encode with the optimal sized %s index,"
               " or use OggIndex to re-index.\n",
               index->packet_size, name, (index->packet_size - index_bytes),
617
               keypoints_cutoff,
Jan Gerber's avatar
Jan Gerber committed
618
619
620
621
622
623
624
625
626
               name, index_bytes, name);
    }
    num_keypoints = keypoints_cutoff;

    if (create_index_packet(index->packet_size,
                            &op,
                            serialno,
                            num_keypoints) == -1)
    {
627
628
629
        free(keypoints);
        return -1;
    }
630
631
632
633
634
635

    /* Write first sample time numerator. */
    write64le(op.packet+26, index->start_time);

    /* Write last sample time numerator. */
    write64le(op.packet+34, index->end_time);
636
637
   
    /* Write keypoint data into packet. */
638
    p = op.packet + 42;
Jan Gerber's avatar
Jan Gerber committed
639
640
641
    limit = op.packet + op.bytes;
    prev_offset = 0;
    prev_time = 0;
642
643
    for (i=0; i<num_keypoints; i++) {
        keypoint* k = &keypoints[i];
Jan Gerber's avatar
Jan Gerber committed
644
645
646
647
648
649
        ogg_int64_t offset_diff = k->offset - prev_offset;
        ogg_int64_t time_diff = k->time - prev_time;
        p = write_vl_int(p, limit, offset_diff);
        p = write_vl_int(p, limit, time_diff);
        prev_offset = k->offset;
        prev_time = k->time;
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
    }
    free(keypoints);

    /* Skeleton stream must be empty. */
    assert(ogg_stream_flush(&info->so, &og) == 0);
    ogg_stream_packetin(&info->so, &op);
    free(op.packet);

    /* Seek to location of existing index pages. */
    if (fseeko(info->outfile, index->page_location, SEEK_SET) < 0) {
        fprintf(stderr, "ERROR: Can't seek output file to write index.!\n");
        return -1;
    }

    /* Overwrite pages to disk. */
    while (ogg_stream_pageout(&info->so, &og)) {
        /* Index pages should not be BOS or EOS. */
        assert(!(og.header[5] & 0x6));
        write_page (info, &og);
    }

    /* Ensure we're flushed to disk. */
    result = ogg_stream_flush(&info->so, &og);
    if (result != 0) {
        write_page (info, &og);
    }

    return 0;
}

/* Overwrites existing skeleton index placeholder packets with valid keyframe
   index data. Must only be called once we've constructed the index data after
   encoding the entire file. */
int write_seek_index (oggmux_info* info)
{
    ogg_uint32_t serialno;
    ogg_page og;

688
689
    /* We shouldn't write indexes for skeleton 3, it's a skeleton 4 feature. */
    assert(!info->skeleton_3);
690
691
692
693
694
695
696
697
698
699
700

    /* Mark that we're done indexing. This causes the header packets' fields
       to be filled with valid, non-unknown values. */
    info->indexing_complete = 1;

    /* Re-encode the entire skeleton track, to ensure the packet and page
       counts don't change. */
    serialno = info->so.serialno;
    ogg_stream_clear(&info->so);
    ogg_stream_init(&info->so, serialno);

701
    add_fishead_packet (info, 4, 0);
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
    if (ogg_stream_flush(&info->so, &og) != 1) {
        fprintf (stderr, "Internal Ogg library error.\n");
        exit (1);
    }

    /* Rewrite the skeleton BOS page. It will have changed to account for
       learning the start time, end time, and length. */
    if (fseeko(info->outfile, 0, SEEK_SET) < 0) {
        fprintf(stderr, "ERROR: Can't seek output file to rewrite skeleton BOS!\n");
        return -1;
    }
    write_page (info, &og);

    /* Encode and discard fisbone packets, their contents' doesn't need
       to be updated. */
    add_fisbone_packet(info);
    while (1) {
        int result = ogg_stream_flush(&info->so, &og);
        if (result < 0) {
            fprintf(stderr, "Internal Ogg library error.\n");
            exit (1);
        }
        if (result == 0)
            break;
    }

Jan Gerber's avatar
Jan Gerber committed
728
729
    /* Write a new line, so that when we print out indexing stats, it's on a new line. */
    printf("\n");
730
731
    if (!info->audio_only &&
        write_index_pages(&info->theora_index,
Jan Gerber's avatar
Jan Gerber committed
732
                          "theora",
733
                          info,
734
                          info->to.serialno,
735
736
                          1,
                          3) == -1)
737
738
739
740
741
    {
        return -1;
    }
    if (!info->video_only && 
        write_index_pages(&info->vorbis_index,
Jan Gerber's avatar
Jan Gerber committed
742
                          "vorbis",
743
                          info,
744
                          info->vo.serialno,
745
746
                          2,
                          3) == -1)
747
748
749
750
751
752
    {
        return -1;
    }

#ifdef HAVE_KATE
    if (info->with_kate) {
753
754
755
756
757
758
759
760
        int n;
        for (n=0; n<info->n_kate_streams; ++n) {
            oggmux_kate_stream *ks=info->kate_streams+n;
            if (write_index_pages(&ks->index, "kate", info, ks->ko.serialno, 1, ks->ki.num_headers) == -1)
            {
                return -1;
            }
        }
761
762
763
764
765
766
767
768
769
770
771
772
    }
#endif

    return 0;
}

/* Adds skeleton index packets to the output file at the current write cursor
   for every stream. We'll fill them with valid data later, we just fill them
   with placeholder data so that we don't need to rewrite the entire file
   after encode when we add the index. */
static int write_placeholder_index_pages (oggmux_info *info)
{
Jan Gerber's avatar
Jan Gerber committed
773
774
775
    if (info->theora_index_reserve != -1) {
        info->theora_index.packet_size = info->theora_index_reserve;
    }
776
777
778
779
780
781
782
    if (!info->audio_only &&
        write_index_placeholder_for_stream(info,
                                           &info->theora_index,
                                           info->to.serialno) == -1)
    {
        return -1;
    }
Jan Gerber's avatar
Jan Gerber committed
783
784
785
    if (info->vorbis_index_reserve != -1) {
        info->vorbis_index.packet_size = info->vorbis_index_reserve;
    }
786
787
788
789
790
791
792
793
794
795
    if (!info->video_only &&
        write_index_placeholder_for_stream(info,
                                           &info->vorbis_index,
                                           info->vo.serialno) == -1)
    {
        return -1;
    }

#ifdef HAVE_KATE
    if (info->with_kate) {
796
797
798
799
800
801
802
803
804
805
806
807
808
        int n;
        for (n=0; n<info->n_kate_streams; ++n) {
            oggmux_kate_stream *ks=info->kate_streams+n;
            if (info->kate_index_reserve != -1) {
                ks->index.packet_size = info->kate_index_reserve;
            }
            if (write_index_placeholder_for_stream(info,
                                                   &ks->index,
                                                   ks->ko.serialno) == -1)
            {
                return -1;
            }
        }
809
810
811
812
813
814
    }
#endif

    return 0;
}

Jan Gerber's avatar
Jan Gerber committed
815
void oggmux_init (oggmux_info *info) {
816
    ogg_page og;
Jan Gerber's avatar
Jan Gerber committed
817
    ogg_packet op;
Jan Gerber's avatar
Jan Gerber committed
818
    int ret;
Jan Gerber's avatar
Jan Gerber committed
819
820
821

    /* yayness.  Set up Ogg output stream */
    srand (time (NULL));
822
823
    info->serialno = rand();
    ogg_stream_init (&info->vo, info->serialno++);
Jan Gerber's avatar
Jan Gerber committed
824

825
826
827
828
829
830
831
    if (info->passno!=1) {
        th_comment_add_tag(&info->tc, "ENCODER", PACKAGE_STRING);
        vorbis_comment_add_tag(&info->vc, "ENCODER", PACKAGE_STRING);
        if (strcmp(info->oshash, "0000000000000000") > 0) {
            th_comment_add_tag(&info->tc, "SOURCE_OSHASH", info->oshash);
            vorbis_comment_add_tag(&info->vc, "SOURCE_OSHASH", info->oshash);
        }
832
833
    }

Jan Gerber's avatar
Jan Gerber committed
834
    if (!info->audio_only) {
835
        ogg_stream_init (&info->to, info->serialno++);
836
        seek_index_init(&info->theora_index, info->index_interval);
Jan Gerber's avatar
Jan Gerber committed
837
838
839
    }
    /* init theora done */
    /* initialize Vorbis too, if we have audio. */
Jan Gerber's avatar
Jan Gerber committed
840
    if (!info->video_only) {
841
        int ret;
Jan Gerber's avatar
Jan Gerber committed
842
843
        vorbis_info_init (&info->vi);
        /* Encoding using a VBR quality mode.  */
Jan Gerber's avatar
Jan Gerber committed
844
        if (info->vorbis_quality>-99)
Jan Gerber's avatar
Jan Gerber committed
845
846
            ret =vorbis_encode_init_vbr (&info->vi, info->channels,info->sample_rate,info->vorbis_quality);
        else
Jan Gerber's avatar
Jan Gerber committed
847
            ret=vorbis_encode_init(&info->vi,info->channels,info->sample_rate,-1,info->vorbis_bitrate,-1);
Jan Gerber's avatar
Jan Gerber committed
848

Jan Gerber's avatar
Jan Gerber committed
849
        if (ret) {
Jan Gerber's avatar
Jan Gerber committed
850
851
852
853
854
855
856
857
858
            fprintf (stderr,
                 "The Vorbis encoder could not set up a mode according to\n"
                 "the requested quality or bitrate.\n\n");
            exit (1);
        }

        /* set up the analysis state and auxiliary encoding storage */
        vorbis_analysis_init (&info->vd, &info->vi);
        vorbis_block_init (&info->vd, &info->vb);
859
860
        
        seek_index_init(&info->vorbis_index, info->index_interval);
861
        info->vorbis_granulepos = 0;
Jan Gerber's avatar
Jan Gerber committed
862
863
864
    }
    /* audio init done */

865
    /* initialize kate if we have subtitles */
Jan Gerber's avatar
Jan Gerber committed
866
    if (info->with_kate && info->passno!=1) {
867
#ifdef HAVE_KATE
Jan Gerber's avatar
Jan Gerber committed
868
        int ret, n;
869
870
        for (n=0; n<info->n_kate_streams; ++n) {
            oggmux_kate_stream *ks=info->kate_streams+n;
871
            ogg_stream_init (&ks->ko, info->serialno++);
872
            ret = kate_encode_init (&ks->k, &ks->ki);
873
            if (ret<0) {
Jan Gerber's avatar
Jan Gerber committed
874
875
                fprintf(stderr, "kate_encode_init: %d\n",ret);
                exit(1);
876
            }
877
            ret = kate_comment_init(&ks->kc);
878
            if (ret<0) {
Jan Gerber's avatar
Jan Gerber committed
879
880
                fprintf(stderr, "kate_comment_init: %d\n",ret);
                exit(1);
881
            }
882
            kate_comment_add_tag (&ks->kc, "ENCODER",PACKAGE_STRING);
883
884

            seek_index_init(&ks->index, info->index_interval);
885
886
887
888
889
        }
#endif
    }
    /* kate init done */

890
891
    if (info->with_skeleton &&
        !info->skeleton_3 &&
892
893
894
        info->duration == -1)
    {
        /* We've not got a duration, we can't index the keyframes. */
895
896
        fprintf(stderr, "WARNING: Can't get duration of media, not indexing, writing Skeleton 3 track.\n");
        info->skeleton_3 = 1;
897
898
    }

899
900
    /* first packet should be skeleton fishead packet, if skeleton is used */

Jan Gerber's avatar
Jan Gerber committed
901
    if (info->with_skeleton && info->passno!=1) {
902
903
        /* Sometimes the output file is not seekable. We can't write the seek
           index if the output is not seekable. So write a Skeleton3.0 header
904
           packet, which will in turn determine if the file is seekable. If it
905
           is, we can safely construct an index, so then overwrite the header
906
907
908
           page with a Skeleton4.0 header page. */
        int skeleton_3 = info->skeleton_3;
        info->skeleton_3 = 1;
909
        ogg_stream_init (&info->so, info->serialno++);
910
        add_fishead_packet (info, 3, 0);
Jan Gerber's avatar
Jan Gerber committed
911
        if (ogg_stream_pageout (&info->so, &og) != 1) {
912
913
914
            fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
        }
915
        write_page (info, &og);
916
917
        assert(info->output_seekable != MAYBE_SEEKABLE);

918
        if (info->output_seekable == NOT_SEEKABLE && !skeleton_3) {
919
            fprintf(stderr, "WARNING: Can't write keyframe-seek-index into "
920
                            "non-seekable output stream! Writing Skeleton3 track.\n");
921
922
        }

923
        info->skeleton_3 = skeleton_3 || info->output_seekable == NOT_SEEKABLE;
924

925
        if (!info->skeleton_3) {
926
            /* Output is seekable and we're indexing. Overwrite the
927
               Skeleton3.0 BOS page with a Skeleton4.0 BOS page. */
928
929
930
931
932
            if (fseeko (info->outfile, 0, SEEK_SET) < 0) {
                fprintf (stderr, "ERROR: failed to seek in seekable output file!?!\n");
                exit (1);
            }
            ogg_stream_clear (&info->so);
933
            ogg_stream_init (&info->so, info->serialno++);
934
            add_fishead_packet (info, 4, 0);
935
936
937
938
939
940
            if (ogg_stream_pageout (&info->so, &og) != 1) {
                fprintf (stderr, "Internal Ogg library error.\n");
                exit (1);
            }
            write_page (info, &og);
        }
941
942
    }

Jan Gerber's avatar
Jan Gerber committed
943
944
945
    /* write the bitstream header packets with proper page interleave */

    /* first packet will get its own page automatically */
Jan Gerber's avatar
Jan Gerber committed
946
    if (!info->audio_only) {
Jan Gerber's avatar
Jan Gerber committed
947
948
949
950
951
        /* write the bitstream header packets with proper page interleave */
        /* first packet will get its own page automatically */
        if(th_encode_flushheader(info->td, &info->tc, &op) <= 0) {
          fprintf(stderr, "Internal Theora library error.\n");
          exit(1);
Jan Gerber's avatar
Jan Gerber committed
952
        }
Jan Gerber's avatar
Jan Gerber committed
953
954
955
956
957
958
        if(info->passno!=1){
            ogg_stream_packetin(&info->to, &op);
            if(ogg_stream_pageout(&info->to, &og) != 1) {
                fprintf(stderr, "Internal Ogg library error.\n");
                exit(1);
            }
959
            write_page (info, &og);
Jan Gerber's avatar
Jan Gerber committed
960
        }
Jan Gerber's avatar
Jan Gerber committed
961
962

        /* create the remaining theora headers */
Jan Gerber's avatar
Jan Gerber committed
963
964
965
966
967
968
969
        for(;;){
          ret=th_encode_flushheader(info->td, &info->tc, &op);
          if(ret < 0) {
            fprintf(stderr,"Internal Theora library error.\n");
            exit(1);
          }
          else if(!ret) break;
Jan Gerber's avatar
Jan Gerber committed
970
971
          if(info->passno!=1)
            ogg_stream_packetin(&info->to, &op);
972
        }
Jan Gerber's avatar
Jan Gerber committed
973
    }
Jan Gerber's avatar
Jan Gerber committed
974
    if (!info->video_only && info->passno!=1) {
Jan Gerber's avatar
Jan Gerber committed
975
976
977
978
979
980
981
982
        ogg_packet header;
        ogg_packet header_comm;
        ogg_packet header_code;

        vorbis_analysis_headerout (&info->vd, &info->vc, &header,
                       &header_comm, &header_code);
        ogg_stream_packetin (&info->vo, &header);    /* automatically placed in its own
                                 * page */
Jan Gerber's avatar
Jan Gerber committed
983
        if (ogg_stream_pageout (&info->vo, &og) != 1) {
Jan Gerber's avatar
Jan Gerber committed
984
985
986
            fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
        }
987
        write_page (info, &og);
Jan Gerber's avatar
Jan Gerber committed
988
989
990
991
992
993

        /* remaining vorbis header packets */
        ogg_stream_packetin (&info->vo, &header_comm);
        ogg_stream_packetin (&info->vo, &header_code);
    }

994
#ifdef HAVE_KATE
Jan Gerber's avatar
Jan Gerber committed
995
    if (info->with_kate && info->passno!=1) {
996
997
998
999
1000
1001
1002
        int n;
        for (n=0; n<info->n_kate_streams; ++n) {
            oggmux_kate_stream *ks=info->kate_streams+n;
            int ret;
            while (1) {
                ret=kate_ogg_encode_headers(&ks->k,&ks->kc,&op);
                if (ret==0) {
Jan Gerber's avatar
Jan Gerber committed
1003
1004
                    ogg_stream_packetin(&ks->ko,&op);
                    ogg_packet_clear(&op);
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
                }
                if (ret<0) fprintf(stderr, "kate_encode_headers: %d\n",ret);
                if (ret>0) break;
            }

            /* first header is on a separate page - libogg will do it automatically */
            ret=ogg_stream_pageout (&ks->ko, &og);
            if (ret!=1) {
                fprintf (stderr, "Internal Ogg library error.\n");
                exit (1);
            }
1016
            write_page (info, &og);
1017
1018
1019
1020
        }
    }
#endif

1021
    /* output the appropriate fisbone packets */
Jan Gerber's avatar
Jan Gerber committed
1022
    if (info->with_skeleton && info->passno!=1) {
Jan Gerber's avatar
Jan Gerber committed
1023
1024
1025
        add_fisbone_packet (info);
        while (1) {
            int result = ogg_stream_flush (&info->so, &og);
Jan Gerber's avatar
Jan Gerber committed
1026
                if (result < 0) {
Jan Gerber's avatar
Jan Gerber committed
1027
1028
1029
1030
1031
1032
                /* can't get here */
                fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
                }
            if (result == 0)
                break;
1033
            write_page (info, &og);
Jan Gerber's avatar
Jan Gerber committed
1034
        }
1035
1036
    }

Jan Gerber's avatar
Jan Gerber committed
1037
1038
1039
    /* Flush the rest of our headers. This ensures
     * the actual data in each stream will start
     * on a new page, as per spec. */
Jan Gerber's avatar
Jan Gerber committed
1040
    while (1 && !info->audio_only && info->passno!=1) {
1041
        int result = ogg_stream_flush (&info->to, &og);
Jan Gerber's avatar
Jan Gerber committed
1042
        if (result < 0) {
Jan Gerber's avatar
Jan Gerber committed
1043
1044
1045
1046
1047
1048
            /* can't get here */
            fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
        }
        if (result == 0)
            break;
1049
        write_page (info, &og);
Jan Gerber's avatar
Jan Gerber committed
1050
    }
Jan Gerber's avatar
Jan Gerber committed
1051
    while (1 && !info->video_only && info->passno!=1) {
1052
        int result = ogg_stream_flush (&info->vo, &og);
Jan Gerber's avatar
Jan Gerber committed
1053
        if (result < 0) {
Jan Gerber's avatar
Jan Gerber committed
1054
1055
1056
1057
1058
1059
            /* can't get here */
            fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
        }
        if (result == 0)
            break;
1060
        write_page (info, &og);
Jan Gerber's avatar
Jan Gerber committed
1061
    }
1062
#ifdef HAVE_KATE
Jan Gerber's avatar
Jan Gerber committed
1063
    if (info->with_kate && info->passno!=1) {
1064
1065
1066
1067
1068
        int n;
        for (n=0; n<info->n_kate_streams; ++n) {
            oggmux_kate_stream *ks=info->kate_streams+n;
            while (1) {
                int result = ogg_stream_flush (&ks->ko, &og);
Jan Gerber's avatar
Jan Gerber committed
1069
                if (result < 0) {
1070
1071
1072
1073
1074
1075
                    /* can't get here */
                    fprintf (stderr, "Internal Ogg library error.\n");
                    exit (1);
                }
                if (result == 0)
                    break;
1076
                write_page (info, &og);
1077
1078
1079
            }
        }
    }
1080
#endif
1081

Jan Gerber's avatar
Jan Gerber committed
1082
    if (info->with_skeleton && info->passno!=1) {
Jan Gerber's avatar
Jan Gerber committed
1083
        int result;
1084

1085
        if (!info->skeleton_3) {
1086
1087
1088
1089
1090
            /* Add placeholder packets to reserve space for the index
             * at the start of file. */
            write_placeholder_index_pages (info);
        }

Jan Gerber's avatar
Jan Gerber committed
1091
1092
1093
1094
1095
1096
1097
        /* build and add the e_o_s packet */
        memset (&op, 0, sizeof (op));
            op.b_o_s = 0;
        op.e_o_s = 1; /* its the e_o_s packet */
            op.granulepos = 0;
        op.bytes = 0; /* e_o_s packet is an empty packet */
            ogg_stream_packetin (&info->so, &op);
1098

Jan Gerber's avatar
Jan Gerber committed
1099
        result = ogg_stream_flush (&info->so, &og);
Jan Gerber's avatar
Jan Gerber committed
1100
        if (result < 0) {
1101
1102
1103
1104
            /* can't get here */
            fprintf (stderr, "Internal Ogg library error.\n");
            exit (1);
        }
1105
        write_page (info, &og);
1106
1107
1108
1109
        
        /* Record the offset of the next page; it's the first non-header, or
         * content page. */
        info->content_offset = ftello(info->outfile);
1110
    }
1111
1112
}

Jan Gerber's avatar
Jan Gerber committed
1113
/**
1114
1115
 * adds a video frame to the encoding sink
 * if e_o_s is 1 the end of the logical bitstream will be marked.
Jan Gerber's avatar
Jan Gerber committed
1116
 * @param this ff2theora struct
Jan Gerber's avatar
Jan Gerber committed
1117
1118
 * @param info oggmux_info
 * @param yuv_buffer
1119
1120
 * @param e_o_s 1 indicates ond of stream
 */
Jan Gerber's avatar
Jan Gerber committed
1121
void oggmux_add_video (oggmux_info *info, th_ycbcr_buffer ycbcr, int e_o_s) {
Jan Gerber's avatar
Jan Gerber committed
1122
    ogg_packet op;
Jan Gerber's avatar
Jan Gerber committed
1123
    int ret;
Jan Gerber's avatar
Jan Gerber committed
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156

    if(info->passno==2){
        for(;;){
          static unsigned char buffer[80];
          static int buf_pos;
          int bytes;
          /*Ask the encoder how many bytes it would like.*/
          bytes=th_encode_ctl(info->td,TH_ENCCTL_2PASS_IN,NULL,0);
          if(bytes<0){
            fprintf(stderr,"Error submitting pass data in second pass.\n");
            exit(1);
          }
          /*If it's got enough, stop.*/
          if(bytes==0)break;
          /*Read in some more bytes, if necessary.*/
          if(bytes>80-buf_pos)bytes=80-buf_pos;
          if(bytes>0&&fread(buffer+buf_pos,1,bytes,info->twopass_file)<bytes){
            fprintf(stderr,"Could not read frame data from two-pass data file!\n");
            exit(1);
          }
          /*And pass them off.*/
          ret=th_encode_ctl(info->td,TH_ENCCTL_2PASS_IN,buffer,bytes);
          if(ret<0){
            fprintf(stderr,"Error submitting pass data in second pass.\n");
            exit(1);
          }
          /*If the encoder consumed the whole buffer, reset it.*/
          if(ret>=bytes)buf_pos=0;
          /*Otherwise remember how much it used.*/
          else buf_pos+=ret;
        }
    }

Jan Gerber's avatar
Jan Gerber committed
1157
    th_encode_ycbcr_in(info->td, ycbcr);
Jan Gerber's avatar
Jan Gerber committed
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169