theora.c 19.5 KB
Newer Older
gbazin's avatar
 
gbazin committed
1
2
3
4
/*****************************************************************************
 * theora.c: theora decoder module making use of libtheora.
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
Eric Petit's avatar
Eric Petit committed
5
 * $Id: theora.c,v 1.20 2003/12/14 16:08:32 titer Exp $
gbazin's avatar
 
gbazin committed
6
7
8
9
10
11
12
 *
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Sam Hocevar's avatar
Sam Hocevar committed
13
 *
gbazin's avatar
 
gbazin committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/decoder.h>
gbazin's avatar
   
gbazin committed
29
#include "input_ext-plugins.h"
gbazin's avatar
 
gbazin committed
30
31
32
33
34
35

#include <ogg/ogg.h>

#include <theora/theora.h>

/*****************************************************************************
gbazin's avatar
   
gbazin committed
36
 * decoder_sys_t : theora decoder descriptor
gbazin's avatar
 
gbazin committed
37
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
38
struct decoder_sys_t
gbazin's avatar
 
gbazin committed
39
{
gbazin's avatar
   
gbazin committed
40
41
42
    /* Module mode */
    vlc_bool_t b_packetizer;

gbazin's avatar
 
gbazin committed
43
    /*
gbazin's avatar
   
gbazin committed
44
     * Input properties
gbazin's avatar
 
gbazin committed
45
     */
gbazin's avatar
   
gbazin committed
46
    int i_headers;
gbazin's avatar
 
gbazin committed
47
48
49
50
51

    /*
     * Theora properties
     */
    theora_info      ti;                        /* theora bitstream settings */
gbazin's avatar
   
gbazin committed
52
    theora_comment   tc;                            /* theora comment header */
gbazin's avatar
 
gbazin committed
53
54
    theora_state     td;                   /* theora bitstream user comments */

gbazin's avatar
   
gbazin committed
55
56
57
58
59
    /*
     * Common properties
     */
    mtime_t i_pts;
};
gbazin's avatar
 
gbazin committed
60
61
62
63

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
64
65
66
static int  OpenDecoder   ( vlc_object_t * );
static int  OpenPacketizer( vlc_object_t * );
static void CloseDecoder  ( vlc_object_t * );
gbazin's avatar
   
gbazin committed
67

gbazin's avatar
   
gbazin committed
68
69
static void *DecodeBlock  ( decoder_t *, block_t ** );
static void *ProcessPacket ( decoder_t *, ogg_packet *, block_t ** );
gbazin's avatar
 
gbazin committed
70

gbazin's avatar
   
gbazin committed
71
static picture_t *DecodePacket( decoder_t *, ogg_packet * );
gbazin's avatar
 
gbazin committed
72

gbazin's avatar
   
gbazin committed
73
74
static void ParseTheoraComments( decoder_t * );
static void theora_CopyPicture( decoder_t *, picture_t *, yuv_buffer * );
75

gbazin's avatar
   
gbazin committed
76
77
78
79
80
static int  OpenEncoder( vlc_object_t *p_this );
static void CloseEncoder( vlc_object_t *p_this );
static block_t *Headers( encoder_t *p_enc );
static block_t *Encode( encoder_t *p_enc, picture_t *p_pict );

gbazin's avatar
 
gbazin committed
81
82
83
84
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
gbazin's avatar
   
gbazin committed
85
    set_description( _("Theora video decoder") );
gbazin's avatar
 
gbazin committed
86
    set_capability( "decoder", 100 );
gbazin's avatar
   
gbazin committed
87
    set_callbacks( OpenDecoder, CloseDecoder );
gbazin's avatar
 
gbazin committed
88
    add_shortcut( "theora" );
gbazin's avatar
   
gbazin committed
89
90
91
92

    add_submodule();
    set_description( _("Theora video packetizer") );
    set_capability( "packetizer", 100 );
gbazin's avatar
   
gbazin committed
93
    set_callbacks( OpenPacketizer, CloseDecoder );
gbazin's avatar
   
gbazin committed
94
    add_shortcut( "theora" );
gbazin's avatar
   
gbazin committed
95
96
97

    add_submodule();
    set_description( _("Theora video encoder") );
gbazin's avatar
   
gbazin committed
98
    set_capability( "encoder", 100 );
gbazin's avatar
   
gbazin committed
99
100
    set_callbacks( OpenEncoder, CloseEncoder );
    add_shortcut( "theora" );
gbazin's avatar
 
gbazin committed
101
102
103
104
105
106
107
vlc_module_end();

/*****************************************************************************
 * OpenDecoder: probe the decoder and return score
 *****************************************************************************/
static int OpenDecoder( vlc_object_t *p_this )
{
gbazin's avatar
   
gbazin committed
108
    decoder_t *p_dec = (decoder_t*)p_this;
gbazin's avatar
   
gbazin committed
109
    decoder_sys_t *p_sys;
gbazin's avatar
 
gbazin committed
110

gbazin's avatar
   
gbazin committed
111
    if( p_dec->fmt_in.i_codec != VLC_FOURCC('t','h','e','o') )
gbazin's avatar
 
gbazin committed
112
113
114
115
    {
        return VLC_EGENERIC;
    }

gbazin's avatar
   
gbazin committed
116
    /* Allocate the memory needed to store the decoder's structure */
gbazin's avatar
   
gbazin committed
117
    if( ( p_dec->p_sys = p_sys =
gbazin's avatar
   
gbazin committed
118
119
120
121
122
123
124
          (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
    {
        msg_Err( p_dec, "out of memory" );
        return VLC_EGENERIC;
    }
    p_dec->p_sys->b_packetizer = VLC_FALSE;

gbazin's avatar
   
gbazin committed
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    p_sys->i_pts = 0;

    /* Set output properties */
    p_dec->fmt_out.i_cat = VIDEO_ES;
    p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0');

    /* Set callbacks */
    p_dec->pf_decode_video = (picture_t *(*)(decoder_t *, block_t **))
        DecodeBlock;
    p_dec->pf_packetize    = (block_t *(*)(decoder_t *, block_t **))
        DecodeBlock;

    /* Init supporting Theora structures needed in header parsing */
    theora_comment_init( &p_sys->tc );
    theora_info_init( &p_sys->ti );

    p_sys->i_headers = 0;

gbazin's avatar
 
gbazin committed
143
144
    return VLC_SUCCESS;
}
gbazin's avatar
   
gbazin committed
145
146
147
148
149
150
151

static int OpenPacketizer( vlc_object_t *p_this )
{
    decoder_t *p_dec = (decoder_t*)p_this;

    int i_ret = OpenDecoder( p_this );

gbazin's avatar
   
gbazin committed
152
153
154
155
156
    if( i_ret == VLC_SUCCESS )
    {
        p_dec->p_sys->b_packetizer = VLC_TRUE;
        p_dec->fmt_out.i_codec = VLC_FOURCC( 't', 'h', 'e', 'o' );
    }
gbazin's avatar
   
gbazin committed
157
158
159
160
161

    return i_ret;
}

/****************************************************************************
gbazin's avatar
   
gbazin committed
162
 * DecodeBlock: the whole thing
gbazin's avatar
   
gbazin committed
163
164
165
 ****************************************************************************
 * This function must be fed with ogg packets.
 ****************************************************************************/
gbazin's avatar
   
gbazin committed
166
static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
gbazin's avatar
   
gbazin committed
167
168
{
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
169
    block_t *p_block;
gbazin's avatar
   
gbazin committed
170
    ogg_packet oggpacket;
gbazin's avatar
   
gbazin committed
171
172
173
174

    if( !pp_block || !*pp_block ) return NULL;

    p_block = *pp_block;
gbazin's avatar
 
gbazin committed
175

gbazin's avatar
   
gbazin committed
176
177
178
179
180
181
182
    /* Block to Ogg packet */
    oggpacket.packet = p_block->p_buffer;
    oggpacket.bytes = p_block->i_buffer;
    oggpacket.granulepos = p_block->i_dts;
    oggpacket.b_o_s = 0;
    oggpacket.e_o_s = 0;
    oggpacket.packetno = 0;
gbazin's avatar
   
gbazin committed
183

gbazin's avatar
   
gbazin committed
184
    if( p_sys->i_headers == 0 )
gbazin's avatar
   
gbazin committed
185
    {
gbazin's avatar
   
gbazin committed
186
        /* Take care of the initial Theora header */
gbazin's avatar
   
gbazin committed
187

gbazin's avatar
   
gbazin committed
188
189
190
        oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
        if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
        {
gbazin's avatar
   
gbazin committed
191
            msg_Err( p_dec, "This bitstream does not contain Theora "
gbazin's avatar
   
gbazin committed
192
193
                     "video data" );
            block_Release( p_block );
gbazin's avatar
   
gbazin committed
194
            return NULL;
gbazin's avatar
   
gbazin committed
195
196
        }
        p_sys->i_headers++;
gbazin's avatar
 
gbazin committed
197

gbazin's avatar
   
gbazin committed
198
199
200
        /* Set output properties */
        p_dec->fmt_out.video.i_width = p_sys->ti.width;
        p_dec->fmt_out.video.i_height = p_sys->ti.height;
Sam Hocevar's avatar
Sam Hocevar committed
201

gbazin's avatar
   
gbazin committed
202
        if( p_sys->ti.aspect_denominator )
gbazin's avatar
   
gbazin committed
203
            p_dec->fmt_out.video.i_aspect = ((int64_t)VOUT_ASPECT_FACTOR) *
gbazin's avatar
   
gbazin committed
204
                p_sys->ti.aspect_numerator / p_sys->ti.aspect_denominator;
gbazin's avatar
   
gbazin committed
205
        else
gbazin's avatar
   
gbazin committed
206
207
            p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR *
                p_sys->ti.frame_width / p_sys->ti.frame_height;
gbazin's avatar
 
gbazin committed
208

gbazin's avatar
   
gbazin committed
209
210
211
212
213
214
        msg_Dbg( p_dec, "%dx%d %.02f fps video, frame content "
                 "is %dx%d with offset (%d,%d)",
                 p_sys->ti.width, p_sys->ti.height,
                 (double)p_sys->ti.fps_numerator/p_sys->ti.fps_denominator,
                 p_sys->ti.frame_width, p_sys->ti.frame_height,
                 p_sys->ti.offset_x, p_sys->ti.offset_y );
gbazin's avatar
 
gbazin committed
215

gbazin's avatar
   
gbazin committed
216
        return ProcessPacket( p_dec, &oggpacket, pp_block );
gbazin's avatar
   
gbazin committed
217
218
219
    }

    if( p_sys->i_headers == 1 )
gbazin's avatar
 
gbazin committed
220
    {
gbazin's avatar
   
gbazin committed
221
222
223
224
        /* The next packet in order is the comments header */
        if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
        {
            msg_Err( p_dec, "2nd Theora header is corrupted" );
gbazin's avatar
   
gbazin committed
225
            return NULL;
gbazin's avatar
   
gbazin committed
226
227
        }
        p_sys->i_headers++;
Sam Hocevar's avatar
Sam Hocevar committed
228

gbazin's avatar
   
gbazin committed
229
230
        ParseTheoraComments( p_dec );

gbazin's avatar
   
gbazin committed
231
        return ProcessPacket( p_dec, &oggpacket, pp_block );
gbazin's avatar
 
gbazin committed
232
233
    }

gbazin's avatar
   
gbazin committed
234
    if( p_sys->i_headers == 2 )
gbazin's avatar
 
gbazin committed
235
    {
gbazin's avatar
   
gbazin committed
236
237
238
239
240
241
        /* The next packet in order is the codebooks header
           We need to watch out that this packet is not missing as a
           missing or corrupted header is fatal. */
        if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
        {
            msg_Err( p_dec, "3rd Theora header is corrupted" );
gbazin's avatar
   
gbazin committed
242
            return NULL;
gbazin's avatar
   
gbazin committed
243
244
        }
        p_sys->i_headers++;
Sam Hocevar's avatar
Sam Hocevar committed
245

gbazin's avatar
   
gbazin committed
246
247
248
249
250
251
        if( !p_sys->b_packetizer )
        {
            /* We have all the headers, initialize decoder */
            theora_decode_init( &p_sys->td, &p_sys->ti );
        }

gbazin's avatar
   
gbazin committed
252
        return ProcessPacket( p_dec, &oggpacket, pp_block );
gbazin's avatar
 
gbazin committed
253
254
    }

gbazin's avatar
   
gbazin committed
255
    return ProcessPacket( p_dec, &oggpacket, pp_block );
gbazin's avatar
   
gbazin committed
256
}
gbazin's avatar
 
gbazin committed
257

gbazin's avatar
   
gbazin committed
258
/*****************************************************************************
gbazin's avatar
   
gbazin committed
259
 * ProcessPacket: processes a theora packet.
gbazin's avatar
   
gbazin committed
260
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
261
262
static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
                            block_t **pp_block )
gbazin's avatar
   
gbazin committed
263
264
{
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
265
266
    block_t *p_block = *pp_block;
    void *p_buf;
gbazin's avatar
 
gbazin committed
267

gbazin's avatar
   
gbazin committed
268
    /* Date management */
gbazin's avatar
   
gbazin committed
269
    if( p_block->i_pts > 0 && p_block->i_pts != p_sys->i_pts )
gbazin's avatar
 
gbazin committed
270
    {
gbazin's avatar
   
gbazin committed
271
        p_sys->i_pts = p_block->i_pts;
gbazin's avatar
 
gbazin committed
272
273
    }

gbazin's avatar
   
gbazin committed
274
275
    if( p_sys->b_packetizer )
    {
gbazin's avatar
   
gbazin committed
276
277
278
279
280
281
282
283
284
        /* Date management */
        p_block->i_dts = p_block->i_pts = p_sys->i_pts;

        if( p_sys->i_headers >= 3 )
            p_block->i_length = p_sys->i_pts - p_block->i_pts;
        else
            p_block->i_length = 0;

        p_buf = p_block;
gbazin's avatar
   
gbazin committed
285
286
287
    }
    else
    {
gbazin's avatar
   
gbazin committed
288
289
290
291
        if( p_sys->i_headers >= 3 )
            p_buf = DecodePacket( p_dec, p_oggpacket );
        else
            p_buf = NULL;
gbazin's avatar
 
gbazin committed
292

gbazin's avatar
   
gbazin committed
293
        if( p_block )
gbazin's avatar
 
gbazin committed
294
        {
gbazin's avatar
   
gbazin committed
295
296
            block_Release( p_block );
            *pp_block = NULL;
gbazin's avatar
 
gbazin committed
297
298
        }
    }
gbazin's avatar
   
gbazin committed
299
300

    /* Date management */
gbazin's avatar
   
gbazin committed
301
302
    p_sys->i_pts += ( I64C(1000000) * p_sys->ti.fps_denominator /
                      p_sys->ti.fps_numerator ); /* 1 frame per packet */
gbazin's avatar
   
gbazin committed
303

gbazin's avatar
   
gbazin committed
304
    return p_buf;
gbazin's avatar
 
gbazin committed
305
306
307
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
308
 * DecodePacket: decodes a Theora packet.
gbazin's avatar
 
gbazin committed
309
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
310
static picture_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
gbazin's avatar
 
gbazin committed
311
{
gbazin's avatar
   
gbazin committed
312
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
313
314
    picture_t *p_pic;
    yuv_buffer yuv;
gbazin's avatar
   
gbazin committed
315

gbazin's avatar
   
gbazin committed
316
    theora_decode_packetin( &p_sys->td, p_oggpacket );
gbazin's avatar
 
gbazin committed
317

gbazin's avatar
   
gbazin committed
318
319
    /* Decode */
    theora_decode_YUVout( &p_sys->td, &yuv );
gbazin's avatar
 
gbazin committed
320

gbazin's avatar
   
gbazin committed
321
322
323
    /* Get a new picture */
    p_pic = p_dec->pf_vout_buffer_new( p_dec );
    if( !p_pic ) return NULL;
gbazin's avatar
   
gbazin committed
324

gbazin's avatar
   
gbazin committed
325
    theora_CopyPicture( p_dec, p_pic, &yuv );
gbazin's avatar
   
gbazin committed
326

gbazin's avatar
   
gbazin committed
327
    p_pic->date = p_sys->i_pts;
gbazin's avatar
 
gbazin committed
328

gbazin's avatar
   
gbazin committed
329
    return p_pic;
gbazin's avatar
 
gbazin committed
330
331
332
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
333
 * ParseTheoraComments: FIXME should be done in demuxer
gbazin's avatar
 
gbazin committed
334
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
335
static void ParseTheoraComments( decoder_t *p_dec )
gbazin's avatar
 
gbazin committed
336
{
gbazin's avatar
   
gbazin committed
337
338
339
340
341
342
    input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
    input_info_category_t *p_cat =
        input_InfoCategory( p_input, _("Theora Comment") );
    int i = 0;
    char *psz_name, *psz_value, *psz_comment;
    while ( i < p_dec->p_sys->tc.comments )
gbazin's avatar
 
gbazin committed
343
    {
gbazin's avatar
   
gbazin committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
        psz_comment = strdup( p_dec->p_sys->tc.user_comments[i] );
        if( !psz_comment )
        {
            msg_Warn( p_dec, "Out of memory" );
            break;
        }
        psz_name = psz_comment;
        psz_value = strchr( psz_comment, '=' );
        if( psz_value )
        {
            *psz_value = '\0';
            psz_value++;
            input_AddInfo( p_cat, psz_name, psz_value );
        }
        free( psz_comment );
        i++;
    }
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
364
 * CloseDecoder: theora decoder destruction
gbazin's avatar
   
gbazin committed
365
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
366
static void CloseDecoder( vlc_object_t *p_this )
gbazin's avatar
   
gbazin committed
367
{
gbazin's avatar
   
gbazin committed
368
    decoder_t *p_dec = (decoder_t *)p_this;
gbazin's avatar
   
gbazin committed
369
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
 
gbazin committed
370

gbazin's avatar
   
gbazin committed
371
372
    theora_info_clear( &p_sys->ti );
    theora_comment_clear( &p_sys->tc );
gbazin's avatar
   
gbazin committed
373

gbazin's avatar
   
gbazin committed
374
    free( p_sys );
gbazin's avatar
 
gbazin committed
375
376
377
378
379
380
}

/*****************************************************************************
 * theora_CopyPicture: copy a picture from theora internal buffers to a
 *                     picture_t structure.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
381
static void theora_CopyPicture( decoder_t *p_dec, picture_t *p_pic,
gbazin's avatar
 
gbazin committed
382
383
384
                                yuv_buffer *yuv )
{
    int i_plane, i_line, i_width, i_dst_stride, i_src_stride;
gbazin's avatar
   
gbazin committed
385
    int i_src_xoffset, i_src_yoffset;
Sam Hocevar's avatar
Sam Hocevar committed
386
    uint8_t *p_dst, *p_src;
gbazin's avatar
 
gbazin committed
387
388
389
390
391
392

    for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
    {
        p_dst = p_pic->p[i_plane].p_pixels;
        p_src = i_plane ? (i_plane - 1 ? yuv->v : yuv->u ) : yuv->y;
        i_width = p_pic->p[i_plane].i_visible_pitch;
gbazin's avatar
   
gbazin committed
393
394
395
396
397
398
399
400
        i_dst_stride  = p_pic->p[i_plane].i_pitch;
        i_src_stride  = i_plane ? yuv->uv_stride : yuv->y_stride;
        i_src_xoffset = p_dec->p_sys->ti.offset_x;
        i_src_yoffset = p_dec->p_sys->ti.offset_y;
        if( i_plane )
        {
            i_src_xoffset /= 2;
            i_src_yoffset /= 2;
Sam Hocevar's avatar
Sam Hocevar committed
401
        }
gbazin's avatar
   
gbazin committed
402
403

        p_src += (i_src_yoffset * i_src_stride + i_src_yoffset);
gbazin's avatar
 
gbazin committed
404
405
406

        for( i_line = 0; i_line < p_pic->p[i_plane].i_lines; i_line++ )
        {
gbazin's avatar
   
gbazin committed
407
            p_dec->p_vlc->pf_memcpy( p_dst, p_src, i_width );
gbazin's avatar
 
gbazin committed
408
409
410
411
412
            p_src += i_src_stride;
            p_dst += i_dst_stride;
        }
    }
}
gbazin's avatar
   
gbazin committed
413
414
415
416
417
418
419
420
421

/*****************************************************************************
 * encoder_sys_t : theora encoder descriptor
 *****************************************************************************/
struct encoder_sys_t
{
    /*
     * Input properties
     */
gbazin's avatar
   
gbazin committed
422
    vlc_bool_t b_headers;
gbazin's avatar
   
gbazin committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444

    /*
     * Theora properties
     */
    theora_info      ti;                        /* theora bitstream settings */
    theora_comment   tc;                            /* theora comment header */
    theora_state     td;                   /* theora bitstream user comments */

    /*
     * Common properties
     */
    mtime_t i_pts;
};

/*****************************************************************************
 * OpenEncoder: probe the encoder and return score
 *****************************************************************************/
static int OpenEncoder( vlc_object_t *p_this )
{
    encoder_t *p_enc = (encoder_t *)p_this;
    encoder_sys_t *p_sys = p_enc->p_sys;

gbazin's avatar
   
gbazin committed
445
    if( p_enc->fmt_out.i_codec != VLC_FOURCC('t','h','e','o') )
gbazin's avatar
   
gbazin committed
446
447
448
449
    {
        return VLC_EGENERIC;
    }

450
451
452
453
454
455
456
457
458
    if( p_enc->fmt_in.video.i_width % 16 ||
        p_enc->fmt_in.video.i_height % 16 )
    {
        msg_Err( p_enc, "Theora video encoding requires dimensions which are "
                 "multiples of 16. Which is not the case here (%dx%d)",
                 p_enc->fmt_in.video.i_width, p_enc->fmt_in.video.i_height );
        return VLC_EGENERIC;
    }

gbazin's avatar
   
gbazin committed
459
460
461
462
463
464
465
466
467
468
    /* Allocate the memory needed to store the decoder's structure */
    if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL )
    {
        msg_Err( p_enc, "out of memory" );
        return VLC_EGENERIC;
    }
    p_enc->p_sys = p_sys;

    p_enc->pf_header = Headers;
    p_enc->pf_encode_video = Encode;
gbazin's avatar
   
gbazin committed
469
    p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
gbazin's avatar
   
gbazin committed
470
471
472
473
474
475
476
477
478

#define frame_x_offset 0
#define frame_y_offset 0
#define video_hzn 25
#define video_hzd 1
#define video_q 5

    theora_info_init( &p_sys->ti );

gbazin's avatar
   
gbazin committed
479
480
481
482
    p_sys->ti.width = p_enc->fmt_in.video.i_width;
    p_sys->ti.height = p_enc->fmt_in.video.i_height;
    p_sys->ti.frame_width = p_enc->fmt_in.video.i_width;
    p_sys->ti.frame_height = p_enc->fmt_in.video.i_height;
gbazin's avatar
   
gbazin committed
483
484
485
486
    p_sys->ti.offset_x = frame_x_offset;
    p_sys->ti.offset_y = frame_y_offset;
    p_sys->ti.fps_numerator = video_hzn;
    p_sys->ti.fps_denominator = video_hzd;
gbazin's avatar
   
gbazin committed
487
488
489
490
491
492
493
494
495
496
497
498

    if( p_enc->fmt_in.video.i_aspect )
    {
        p_sys->ti.aspect_numerator = p_enc->fmt_in.video.i_aspect;
        p_sys->ti.aspect_denominator = VOUT_ASPECT_FACTOR;
    }
    else
    {
        p_sys->ti.aspect_numerator = 4;
        p_sys->ti.aspect_denominator = 3;
    }

gbazin's avatar
   
gbazin committed
499
    p_sys->ti.target_bitrate = p_enc->fmt_out.i_bitrate;
gbazin's avatar
   
gbazin committed
500
501
502
503
504
505
506
    p_sys->ti.quality = video_q;

    p_sys->ti.dropframes_p = 0;
    p_sys->ti.quick_p = 1;
    p_sys->ti.keyframe_auto_p = 1;
    p_sys->ti.keyframe_frequency = 64;
    p_sys->ti.keyframe_frequency_force = 64;
gbazin's avatar
   
gbazin committed
507
    p_sys->ti.keyframe_data_target_bitrate = p_enc->fmt_out.i_bitrate * 1.5;
gbazin's avatar
   
gbazin committed
508
509
510
511
512
513
    p_sys->ti.keyframe_auto_threshold = 80;
    p_sys->ti.keyframe_mindistance = 8;
    p_sys->ti.noise_sensitivity = 1;

    theora_encode_init( &p_sys->td, &p_sys->ti );
    theora_info_clear( &p_sys->ti );
gbazin's avatar
   
gbazin committed
514
    theora_comment_init( &p_sys->tc );
gbazin's avatar
   
gbazin committed
515

gbazin's avatar
   
gbazin committed
516
    p_sys->b_headers = VLC_FALSE;
gbazin's avatar
   
gbazin committed
517
518
519
520
521
522
523
524
525
526
527
528

    return VLC_SUCCESS;
}

/****************************************************************************
 * Encode: the whole thing
 ****************************************************************************
 * This function spits out ogg packets.
 ****************************************************************************/
static block_t *Headers( encoder_t *p_enc )
{
    encoder_sys_t *p_sys = p_enc->p_sys;
gbazin's avatar
   
gbazin committed
529
    block_t *p_chain = NULL;
gbazin's avatar
   
gbazin committed
530
531

    /* Create theora headers */
gbazin's avatar
   
gbazin committed
532
    if( !p_sys->b_headers )
gbazin's avatar
   
gbazin committed
533
    {
gbazin's avatar
   
gbazin committed
534
        ogg_packet oggpackets;
gbazin's avatar
   
gbazin committed
535
        int i;
Eric Petit's avatar
Eric Petit committed
536
        block_t *p_block;
gbazin's avatar
   
gbazin committed
537

gbazin's avatar
   
gbazin committed
538
539
540
        /* Ogg packet to block */
        for( i = 0; i < 3; i++ )
        {
gbazin's avatar
   
gbazin committed
541
542
543
544
545
546
547
548
549
550
551
552
553
            switch( i )
            {
            case 0:
                theora_encode_header( &p_sys->td, &oggpackets );
                break;
            case 1:
                theora_encode_comment( &p_sys->tc, &oggpackets );
                break;
            case 2:
                theora_encode_tables( &p_sys->td, &oggpackets );
                break;
            }

Eric Petit's avatar
Eric Petit committed
554
            p_block = block_New( p_enc, oggpackets.bytes );
gbazin's avatar
   
gbazin committed
555
            memcpy( p_block->p_buffer, oggpackets.packet, oggpackets.bytes );
gbazin's avatar
   
gbazin committed
556
557
558
            p_block->i_dts = p_block->i_pts = p_block->i_length = 0;
            block_ChainAppend( &p_chain, p_block );
        }
gbazin's avatar
   
gbazin committed
559

gbazin's avatar
   
gbazin committed
560
561
562
563
        p_sys->b_headers = VLC_TRUE;
    }

    return p_chain;
gbazin's avatar
   
gbazin committed
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
}

/****************************************************************************
 * Encode: the whole thing
 ****************************************************************************
 * This function spits out ogg packets.
 ****************************************************************************/
static block_t *Encode( encoder_t *p_enc, picture_t *p_pict )
{
    encoder_sys_t *p_sys = p_enc->p_sys;
    ogg_packet oggpacket;
    block_t *p_block;
    yuv_buffer yuv;

    /* Theora is a one-frame-in, one-frame-out system. Submit a frame
     * for compression and pull out the packet. */

    yuv.y_width  = p_pict->p[0].i_visible_pitch;
    yuv.y_height = p_pict->p[0].i_lines;
    yuv.y_stride = p_pict->p[0].i_pitch;

    yuv.uv_width  = p_pict->p[1].i_visible_pitch;
    yuv.uv_height = p_pict->p[1].i_lines;
    yuv.uv_stride = p_pict->p[1].i_pitch;

    yuv.y = p_pict->p[0].p_pixels;
    yuv.u = p_pict->p[1].p_pixels;
    yuv.v = p_pict->p[2].p_pixels;

gbazin's avatar
   
gbazin committed
593
594
595
596
597
    if( theora_encode_YUVin( &p_sys->td, &yuv ) < 0 )
    {
        msg_Warn( p_enc, "failed encoding a frame" );
        return NULL;
    }
gbazin's avatar
   
gbazin committed
598
599
600
601
602

    theora_encode_packetout( &p_sys->td, 0, &oggpacket );

    /* Ogg packet to block */
    p_block = block_New( p_enc, oggpacket.bytes );
gbazin's avatar
   
gbazin committed
603
    memcpy( p_block->p_buffer, oggpacket.packet, oggpacket.bytes );
gbazin's avatar
   
gbazin committed
604
    p_block->i_dts = p_block->i_pts = p_pict->date;;
gbazin's avatar
   
gbazin committed
605
606
607
608
609
610
611
612
613
614
615
616
617

    return p_block;
}

/*****************************************************************************
 * CloseEncoder: theora encoder destruction
 *****************************************************************************/
static void CloseEncoder( vlc_object_t *p_this )
{
    encoder_t *p_enc = (encoder_t *)p_this;
    encoder_sys_t *p_sys = p_enc->p_sys;

    theora_info_clear( &p_sys->ti );
618
    theora_comment_clear( &p_sys->tc );
gbazin's avatar
   
gbazin committed
619
620
621

    free( p_sys );
}