theora.c 27.3 KB
Newer Older
gbazin's avatar
   
gbazin committed
1
2
3
/*****************************************************************************
 * theora.c: theora decoder module making use of libtheora.
 *****************************************************************************
4
 * Copyright (C) 1999-2001 the VideoLAN team
5
 * $Id$
gbazin's avatar
   
gbazin committed
6
 *
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
gbazin's avatar
   
gbazin committed
8
9
10
11
12
 *
 * 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
 * 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
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
gbazin's avatar
   
gbazin committed
22
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
28
29
30
#include <vlc_codec.h>
#include <vlc_vout.h>
#include <vlc_sout.h>
zorglub's avatar
zorglub committed
31
#include <vlc_input.h>
gbazin's avatar
   
gbazin committed
32
33
34
35
36
#include <ogg/ogg.h>

#include <theora/theora.h>

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

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

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

56
57
58
59
60
    /*
     * Decoding properties
     */
    vlc_bool_t b_decoded_first_keyframe;

gbazin's avatar
   
gbazin committed
61
62
63
64
65
    /*
     * Common properties
     */
    mtime_t i_pts;
};
gbazin's avatar
   
gbazin committed
66
67
68
69

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
70
71
72
static int  OpenDecoder   ( vlc_object_t * );
static int  OpenPacketizer( vlc_object_t * );
static void CloseDecoder  ( vlc_object_t * );
gbazin's avatar
   
gbazin committed
73

gbazin's avatar
   
gbazin committed
74
static void *DecodeBlock  ( decoder_t *, block_t ** );
75
static int  ProcessHeaders( decoder_t * );
gbazin's avatar
   
gbazin committed
76
static void *ProcessPacket ( decoder_t *, ogg_packet *, block_t ** );
gbazin's avatar
   
gbazin committed
77

gbazin's avatar
   
gbazin committed
78
static picture_t *DecodePacket( decoder_t *, ogg_packet * );
gbazin's avatar
   
gbazin committed
79

gbazin's avatar
   
gbazin committed
80
81
static void ParseTheoraComments( decoder_t * );
static void theora_CopyPicture( decoder_t *, picture_t *, yuv_buffer * );
82

gbazin's avatar
   
gbazin committed
83
84
85
86
static int  OpenEncoder( vlc_object_t *p_this );
static void CloseEncoder( vlc_object_t *p_this );
static block_t *Encode( encoder_t *p_enc, picture_t *p_pict );

gbazin's avatar
   
gbazin committed
87
88
89
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
90
91
#define ENC_QUALITY_TEXT N_("Encoding quality")
#define ENC_QUALITY_LONGTEXT N_( \
92
  "Enforce a quality between 1 (low) and 10 (high), instead " \
93
94
  "of specifying a particular bitrate. This will produce a VBR stream." )

gbazin's avatar
   
gbazin committed
95
vlc_module_begin();
zorglub's avatar
zorglub committed
96
97
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_VCODEC );
98
    set_shortname( "Theora" );
gbazin's avatar
   
gbazin committed
99
    set_description( _("Theora video decoder") );
gbazin's avatar
   
gbazin committed
100
    set_capability( "decoder", 100 );
gbazin's avatar
   
gbazin committed
101
    set_callbacks( OpenDecoder, CloseDecoder );
gbazin's avatar
   
gbazin committed
102
    add_shortcut( "theora" );
gbazin's avatar
   
gbazin committed
103
104
105
106

    add_submodule();
    set_description( _("Theora video packetizer") );
    set_capability( "packetizer", 100 );
gbazin's avatar
   
gbazin committed
107
    set_callbacks( OpenPacketizer, CloseDecoder );
gbazin's avatar
   
gbazin committed
108
109
110

    add_submodule();
    set_description( _("Theora video encoder") );
111
    set_capability( "encoder", 150 );
gbazin's avatar
   
gbazin committed
112
    set_callbacks( OpenEncoder, CloseEncoder );
113
114
115
116

#   define ENC_CFG_PREFIX "sout-theora-"
    add_integer( ENC_CFG_PREFIX "quality", 2, NULL, ENC_QUALITY_TEXT,
                 ENC_QUALITY_LONGTEXT, VLC_FALSE );
gbazin's avatar
   
gbazin committed
117
118
vlc_module_end();

119
120
121
122
static const char *ppsz_enc_options[] = {
    "quality", NULL
};

gbazin's avatar
   
gbazin committed
123
124
125
126
127
/*****************************************************************************
 * OpenDecoder: probe the decoder and return score
 *****************************************************************************/
static int OpenDecoder( vlc_object_t *p_this )
{
gbazin's avatar
   
gbazin committed
128
    decoder_t *p_dec = (decoder_t*)p_this;
gbazin's avatar
   
gbazin committed
129
    decoder_sys_t *p_sys;
gbazin's avatar
   
gbazin committed
130

gbazin's avatar
   
gbazin committed
131
    if( p_dec->fmt_in.i_codec != VLC_FOURCC('t','h','e','o') )
gbazin's avatar
   
gbazin committed
132
133
134
135
    {
        return VLC_EGENERIC;
    }

gbazin's avatar
   
gbazin committed
136
    /* Allocate the memory needed to store the decoder's structure */
gbazin's avatar
   
gbazin committed
137
    if( ( p_dec->p_sys = p_sys =
gbazin's avatar
   
gbazin committed
138
139
140
141
142
143
144
          (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
145
    p_sys->i_pts = 0;
146
    p_sys->b_decoded_first_keyframe = VLC_FALSE;
gbazin's avatar
   
gbazin committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

    /* 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
164
165
    return VLC_SUCCESS;
}
gbazin's avatar
   
gbazin committed
166
167
168
169
170
171
172

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
173
174
175
176
177
    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
178
179
180
181
182

    return i_ret;
}

/****************************************************************************
gbazin's avatar
   
gbazin committed
183
 * DecodeBlock: the whole thing
gbazin's avatar
   
gbazin committed
184
185
186
 ****************************************************************************
 * This function must be fed with ogg packets.
 ****************************************************************************/
gbazin's avatar
   
gbazin committed
187
static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
gbazin's avatar
   
gbazin committed
188
189
{
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
190
    block_t *p_block;
gbazin's avatar
   
gbazin committed
191
    ogg_packet oggpacket;
gbazin's avatar
   
gbazin committed
192
193
194
195

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

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

gbazin's avatar
   
gbazin committed
197
198
199
200
201
202
203
    /* 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
204

205
206
    /* Check for headers */
    if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra )
gbazin's avatar
   
gbazin committed
207
    {
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
        /* Headers already available as extra data */
        p_sys->i_headers = 3;
    }
    else if( oggpacket.bytes && p_sys->i_headers < 3 )
    {
        /* Backup headers as extra data */
        uint8_t *p_extra;

        p_dec->fmt_in.p_extra =
            realloc( p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra +
                     oggpacket.bytes + 2 );
        p_extra = p_dec->fmt_in.p_extra + p_dec->fmt_in.i_extra;
        *(p_extra++) = oggpacket.bytes >> 8;
        *(p_extra++) = oggpacket.bytes & 0xFF;

        memcpy( p_extra, oggpacket.packet, oggpacket.bytes );
        p_dec->fmt_in.i_extra += oggpacket.bytes + 2;

        block_Release( *pp_block );
        p_sys->i_headers++;
        return NULL;
    }
gbazin's avatar
   
gbazin committed
230

231
232
233
    if( p_sys->i_headers == 3 )
    {
        if( ProcessHeaders( p_dec ) != VLC_SUCCESS )
gbazin's avatar
   
gbazin committed
234
        {
235
236
237
            p_sys->i_headers = 0;
            p_dec->fmt_in.i_extra = 0;
            block_Release( *pp_block );
gbazin's avatar
   
gbazin committed
238
            return NULL;
gbazin's avatar
   
gbazin committed
239
        }
240
241
        else p_sys->i_headers++;
    }
gbazin's avatar
   
gbazin committed
242

243
244
    return ProcessPacket( p_dec, &oggpacket, pp_block );
}
Sam Hocevar's avatar
Sam Hocevar committed
245

246
247
248
249
250
251
252
253
254
/*****************************************************************************
 * ProcessHeaders: process Theora headers.
 *****************************************************************************/
static int ProcessHeaders( decoder_t *p_dec )
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    ogg_packet oggpacket;
    uint8_t *p_extra;
    int i_extra;
gbazin's avatar
   
gbazin committed
255

256
    if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
    oggpacket.granulepos = -1;
    oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
    oggpacket.e_o_s = 0;
    oggpacket.packetno = 0;
    p_extra = p_dec->fmt_in.p_extra;
    i_extra = p_dec->fmt_in.i_extra;

    /* Take care of the initial Vorbis header */
    oggpacket.bytes = *(p_extra++) << 8;
    oggpacket.bytes |= (*(p_extra++) & 0xFF);
    oggpacket.packet = p_extra;
    p_extra += oggpacket.bytes;
    i_extra -= (oggpacket.bytes + 2);
    if( i_extra < 0 )
    {
        msg_Err( p_dec, "header data corrupted");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
275
276
    }

277
    if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 )
gbazin's avatar
   
gbazin committed
278
    {
279
        msg_Err( p_dec, "this bitstream does not contain Theora video data" );
280
281
        return VLC_EGENERIC;
    }
Sam Hocevar's avatar
Sam Hocevar committed
282

283
284
285
    /* 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;
286
287
288
289
290
    if( p_sys->ti.frame_width && p_sys->ti.frame_height )
    {
        p_dec->fmt_out.video.i_width = p_sys->ti.frame_width;
        p_dec->fmt_out.video.i_height = p_sys->ti.frame_height;
    }
gbazin's avatar
   
gbazin committed
291

292
293
294
    if( p_sys->ti.aspect_denominator && p_sys->ti.aspect_numerator )
    {
        p_dec->fmt_out.video.i_aspect = ((int64_t)VOUT_ASPECT_FACTOR) *
295
296
            ( p_sys->ti.aspect_numerator * p_dec->fmt_out.video.i_width ) /
            ( p_sys->ti.aspect_denominator * p_dec->fmt_out.video.i_height );
gbazin's avatar
   
gbazin committed
297
    }
298
299
300
301
302
303
    else
    {
        p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR *
            p_sys->ti.frame_width / p_sys->ti.frame_height;
    }

304
305
306
307
308
309
    if( p_sys->ti.fps_numerator > 0 && p_sys->ti.fps_denominator > 0 )
    {
        p_dec->fmt_out.video.i_frame_rate = p_sys->ti.fps_numerator;
        p_dec->fmt_out.video.i_frame_rate_base = p_sys->ti.fps_denominator;
    }

310
311
312
313
314
315
    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
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    /* Sanity check that seems necessary for some corrupted files */
    if( p_sys->ti.width < p_sys->ti.frame_width ||
        p_sys->ti.height < p_sys->ti.frame_height )
    {
        msg_Warn( p_dec, "trying to correct invalid theora header "
                  "(frame size (%dx%d) is smaller than frame content (%d,%d))",
                  p_sys->ti.width, p_sys->ti.height,
                  p_sys->ti.frame_width, p_sys->ti.frame_height );

        if( p_sys->ti.width < p_sys->ti.frame_width )
            p_sys->ti.width = p_sys->ti.frame_width;
        if( p_sys->ti.height < p_sys->ti.frame_height )
            p_sys->ti.height = p_sys->ti.frame_height;
    }

332
333
334
335
336
337
338
339
    /* The next packet in order is the comments header */
    oggpacket.b_o_s = 0;
    oggpacket.bytes = *(p_extra++) << 8;
    oggpacket.bytes |= (*(p_extra++) & 0xFF);
    oggpacket.packet = p_extra;
    p_extra += oggpacket.bytes;
    i_extra -= (oggpacket.bytes + 2);
    if( i_extra < 0 )
gbazin's avatar
   
gbazin committed
340
    {
341
342
343
        msg_Err( p_dec, "header data corrupted");
        return VLC_EGENERIC;
    }
Sam Hocevar's avatar
Sam Hocevar committed
344

345
346
347
348
349
350
351
352
    /* 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" );
        return VLC_EGENERIC;
    }

    ParseTheoraComments( p_dec );
gbazin's avatar
   
gbazin committed
353

354
355
356
357
358
359
360
361
362
363
364
    /* 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. */
    oggpacket.bytes = *(p_extra++) << 8;
    oggpacket.bytes |= (*(p_extra++) & 0xFF);
    oggpacket.packet = p_extra;
    i_extra -= (oggpacket.bytes + 2);
    if( i_extra < 0 )
    {
        msg_Err( p_dec, "header data corrupted");
        return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
365
366
    }

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
    /* 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" );
        return VLC_EGENERIC;
    }

    if( !p_sys->b_packetizer )
    {
        /* We have all the headers, initialize decoder */
        theora_decode_init( &p_sys->td, &p_sys->ti );
    }
    else
    {
        p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra;
        p_dec->fmt_out.p_extra =
            realloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
        memcpy( p_dec->fmt_out.p_extra,
                p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra );
    }

    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
391
}
gbazin's avatar
   
gbazin committed
392

gbazin's avatar
   
gbazin committed
393
/*****************************************************************************
gbazin's avatar
   
gbazin committed
394
 * ProcessPacket: processes a theora packet.
gbazin's avatar
   
gbazin committed
395
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
396
397
static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
                            block_t **pp_block )
gbazin's avatar
   
gbazin committed
398
399
{
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
400
401
    block_t *p_block = *pp_block;
    void *p_buf;
gbazin's avatar
   
gbazin committed
402

Laurent Aimar's avatar
Laurent Aimar committed
403
    if( ( p_block->i_flags&(BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) != 0 )
404
405
406
407
408
409
410
    {
        /* Don't send the the first packet after a discontinuity to
         * theora_decode, otherwise we get purple/green display artifacts
         * appearing in the video output */
        return NULL;
    }

gbazin's avatar
   
gbazin committed
411
    /* Date management */
gbazin's avatar
   
gbazin committed
412
    if( p_block->i_pts > 0 && p_block->i_pts != p_sys->i_pts )
gbazin's avatar
   
gbazin committed
413
    {
gbazin's avatar
   
gbazin committed
414
        p_sys->i_pts = p_block->i_pts;
gbazin's avatar
   
gbazin committed
415
416
    }

417
418
    *pp_block = NULL; /* To avoid being fed the same packet again */

gbazin's avatar
   
gbazin committed
419
420
    if( p_sys->b_packetizer )
    {
gbazin's avatar
   
gbazin committed
421
422
423
424
425
426
427
428
429
        /* 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
430
431
432
    }
    else
    {
gbazin's avatar
   
gbazin committed
433
434
435
436
        if( p_sys->i_headers >= 3 )
            p_buf = DecodePacket( p_dec, p_oggpacket );
        else
            p_buf = NULL;
gbazin's avatar
   
gbazin committed
437

438
        if( p_block ) block_Release( p_block );
gbazin's avatar
   
gbazin committed
439
    }
gbazin's avatar
   
gbazin committed
440
441

    /* Date management */
gbazin's avatar
   
gbazin committed
442
443
    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
444

gbazin's avatar
   
gbazin committed
445
    return p_buf;
gbazin's avatar
   
gbazin committed
446
447
448
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
449
 * DecodePacket: decodes a Theora packet.
gbazin's avatar
   
gbazin committed
450
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
451
static picture_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket )
gbazin's avatar
   
gbazin committed
452
{
gbazin's avatar
   
gbazin committed
453
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
454
455
    picture_t *p_pic;
    yuv_buffer yuv;
gbazin's avatar
   
gbazin committed
456

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

459
460
461
    /* Check for keyframe */
    if( !(p_oggpacket->packet[0] & 0x80) /* data packet */ &&
        !(p_oggpacket->packet[0] & 0x40) /* intra frame */ )
462
463
464
465
466
467
468
469
470
471
472
        p_sys->b_decoded_first_keyframe = VLC_TRUE;

    /* If we haven't seen a single keyframe yet, don't let Theora decode
     * anything, otherwise we'll get display artifacts.  (This is impossible
     * in the general case, but can happen if e.g. we play a network stream
     * using a timed URL, such that the server doesn't start the video with a
     * keyframe). */
    if( p_sys->b_decoded_first_keyframe )
        theora_decode_YUVout( &p_sys->td, &yuv );
    else
        return NULL;
gbazin's avatar
   
gbazin committed
473

gbazin's avatar
   
gbazin committed
474
475
476
    /* Get a new picture */
    p_pic = p_dec->pf_vout_buffer_new( p_dec );
    if( !p_pic ) return NULL;
gbazin's avatar
   
gbazin committed
477

gbazin's avatar
   
gbazin committed
478
    theora_CopyPicture( p_dec, p_pic, &yuv );
gbazin's avatar
   
gbazin committed
479

gbazin's avatar
   
gbazin committed
480
    p_pic->date = p_sys->i_pts;
gbazin's avatar
   
gbazin committed
481

gbazin's avatar
   
gbazin committed
482
    return p_pic;
gbazin's avatar
   
gbazin committed
483
484
485
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
486
 * ParseTheoraComments: FIXME should be done in demuxer
gbazin's avatar
   
gbazin committed
487
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
488
static void ParseTheoraComments( decoder_t *p_dec )
gbazin's avatar
   
gbazin committed
489
{
gbazin's avatar
   
gbazin committed
490
491
    input_thread_t *p_input = (input_thread_t *)p_dec->p_parent;
    char *psz_name, *psz_value, *psz_comment;
492
493
494
495
    int i = 0;

    if( p_input->i_object_type != VLC_OBJECT_INPUT ) return;

gbazin's avatar
   
gbazin committed
496
    while ( i < p_dec->p_sys->tc.comments )
gbazin's avatar
   
gbazin committed
497
    {
gbazin's avatar
   
gbazin committed
498
499
500
        psz_comment = strdup( p_dec->p_sys->tc.user_comments[i] );
        if( !psz_comment )
        {
bigben's avatar
bigben committed
501
            msg_Warn( p_dec, "out of memory" );
gbazin's avatar
   
gbazin committed
502
503
504
505
506
507
508
509
            break;
        }
        psz_name = psz_comment;
        psz_value = strchr( psz_comment, '=' );
        if( psz_value )
        {
            *psz_value = '\0';
            psz_value++;
510
            input_Control( p_input, INPUT_ADD_INFO, _("Theora comment"),
511
                           psz_name, "%s", psz_value );
gbazin's avatar
   
gbazin committed
512
513
514
515
516
517
518
        }
        free( psz_comment );
        i++;
    }
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
519
 * CloseDecoder: theora decoder destruction
gbazin's avatar
   
gbazin committed
520
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
521
static void CloseDecoder( vlc_object_t *p_this )
gbazin's avatar
   
gbazin committed
522
{
gbazin's avatar
   
gbazin committed
523
    decoder_t *p_dec = (decoder_t *)p_this;
gbazin's avatar
   
gbazin committed
524
    decoder_sys_t *p_sys = p_dec->p_sys;
gbazin's avatar
   
gbazin committed
525

gbazin's avatar
   
gbazin committed
526
527
    theora_info_clear( &p_sys->ti );
    theora_comment_clear( &p_sys->tc );
gbazin's avatar
   
gbazin committed
528

gbazin's avatar
   
gbazin committed
529
    free( p_sys );
gbazin's avatar
   
gbazin committed
530
531
532
533
534
535
}

/*****************************************************************************
 * theora_CopyPicture: copy a picture from theora internal buffers to a
 *                     picture_t structure.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
536
static void theora_CopyPicture( decoder_t *p_dec, picture_t *p_pic,
gbazin's avatar
   
gbazin committed
537
538
539
                                yuv_buffer *yuv )
{
    int i_plane, i_line, i_width, i_dst_stride, i_src_stride;
gbazin's avatar
   
gbazin committed
540
    int i_src_xoffset, i_src_yoffset;
Sam Hocevar's avatar
Sam Hocevar committed
541
    uint8_t *p_dst, *p_src;
gbazin's avatar
   
gbazin committed
542
543
544
545
546
547

    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
548
549
550
551
552
553
554
555
        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
556
        }
gbazin's avatar
   
gbazin committed
557

558
        p_src += (i_src_yoffset * i_src_stride + i_src_xoffset);
gbazin's avatar
   
gbazin committed
559

560
        for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines; i_line++ )
gbazin's avatar
   
gbazin committed
561
        {
562
            p_dec->p_libvlc->pf_memcpy( p_dst, p_src + i_src_xoffset,
563
                                     i_plane ? yuv->uv_width : yuv->y_width );
gbazin's avatar
   
gbazin committed
564
565
566
567
568
            p_src += i_src_stride;
            p_dst += i_dst_stride;
        }
    }
}
gbazin's avatar
   
gbazin committed
569
570
571
572
573
574
575
576
577

/*****************************************************************************
 * encoder_sys_t : theora encoder descriptor
 *****************************************************************************/
struct encoder_sys_t
{
    /*
     * Input properties
     */
gbazin's avatar
   
gbazin committed
578
    vlc_bool_t b_headers;
gbazin's avatar
   
gbazin committed
579
580
581
582
583
584
585
586

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

587
    int i_width, i_height;
gbazin's avatar
   
gbazin committed
588
589
590
591
592
593
594
595
596
};

/*****************************************************************************
 * 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;
597
    ogg_packet header;
598
    uint8_t *p_extra;
599
    vlc_value_t val;
600
    int i_quality, i;
gbazin's avatar
   
gbazin committed
601

602
603
    if( p_enc->fmt_out.i_codec != VLC_FOURCC('t','h','e','o') &&
        !p_enc->b_force )
gbazin's avatar
   
gbazin committed
604
605
606
607
608
609
610
611
612
613
614
615
616
    {
        return VLC_EGENERIC;
    }

    /* 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_encode_video = Encode;
gbazin's avatar
   
gbazin committed
617
    p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
618
    p_enc->fmt_out.i_codec = VLC_FOURCC('t','h','e','o');
gbazin's avatar
   
gbazin committed
619

620
    config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg );
621
622
623
624
625
626

    var_Get( p_enc, ENC_CFG_PREFIX "quality", &val );
    i_quality = val.i_int;
    if( i_quality > 10 ) i_quality = 10;
    if( i_quality < 0 ) i_quality = 0;

gbazin's avatar
   
gbazin committed
627
628
    theora_info_init( &p_sys->ti );

gbazin's avatar
   
gbazin committed
629
630
    p_sys->ti.width = p_enc->fmt_in.video.i_width;
    p_sys->ti.height = p_enc->fmt_in.video.i_height;
631
632
633
634
635
636
637
638
639
640
641
642
643

    if( p_sys->ti.width % 16 || p_sys->ti.height % 16 )
    {
        /* Pictures from the transcoder should always have a pitch
         * which is a multiple of 16 */
        p_sys->ti.width = (p_sys->ti.width + 15) >> 4 << 4;
        p_sys->ti.height = (p_sys->ti.height + 15) >> 4 << 4;

        msg_Dbg( p_enc, "padding video from %dx%d to %dx%d",
                 p_enc->fmt_in.video.i_width, p_enc->fmt_in.video.i_height,
                 p_sys->ti.width, p_sys->ti.height );
    }

gbazin's avatar
   
gbazin committed
644
645
    p_sys->ti.frame_width = p_enc->fmt_in.video.i_width;
    p_sys->ti.frame_height = p_enc->fmt_in.video.i_height;
646
    p_sys->ti.offset_x = 0 /*frame_x_offset*/;
647
    p_sys->ti.offset_y = 0 /*frame_y_offset*/;
648

649
650
651
    p_sys->i_width = p_sys->ti.width;
    p_sys->i_height = p_sys->ti.height;

652
653
654
655
656
657
658
659
660
661
662
    if( !p_enc->fmt_in.video.i_frame_rate ||
        !p_enc->fmt_in.video.i_frame_rate_base )
    {
        p_sys->ti.fps_numerator = 25;
        p_sys->ti.fps_denominator = 1;
    }
    else
    {
        p_sys->ti.fps_numerator = p_enc->fmt_in.video.i_frame_rate;
        p_sys->ti.fps_denominator = p_enc->fmt_in.video.i_frame_rate_base;
    }
gbazin's avatar
   
gbazin committed
663
664
665

    if( p_enc->fmt_in.video.i_aspect )
    {
666
667
        uint64_t i_num, i_den;
        unsigned i_dst_num, i_dst_den;
668
669
670

        i_num = p_enc->fmt_in.video.i_aspect * (int64_t)p_sys->ti.height;
        i_den = VOUT_ASPECT_FACTOR * p_sys->ti.width;
671
        vlc_ureduce( &i_dst_num, &i_dst_den, i_num, i_den, 0 );
672
673
        p_sys->ti.aspect_numerator = i_dst_num;
        p_sys->ti.aspect_denominator = i_dst_den;
gbazin's avatar
   
gbazin committed
674
675
676
677
678
679
680
    }
    else
    {
        p_sys->ti.aspect_numerator = 4;
        p_sys->ti.aspect_denominator = 3;
    }

gbazin's avatar
   
gbazin committed
681
    p_sys->ti.target_bitrate = p_enc->fmt_out.i_bitrate;
682
    p_sys->ti.quality = ((float)i_quality) * 6.3;
gbazin's avatar
   
gbazin committed
683
684
685
686
687
688

    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
689
    p_sys->ti.keyframe_data_target_bitrate = p_enc->fmt_out.i_bitrate * 1.5;
gbazin's avatar
   
gbazin committed
690
691
692
693
694
695
    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
696
    theora_comment_init( &p_sys->tc );
gbazin's avatar
   
gbazin committed
697

698
    /* Create and store headers */
699
    p_enc->fmt_out.i_extra = 3 * 2;
700
    for( i = 0; i < 3; i++ )
gbazin's avatar
   
gbazin committed
701
    {
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
        if( i == 0 ) theora_encode_header( &p_sys->td, &header );
        else if( i == 1 ) theora_encode_comment( &p_sys->tc, &header );
        else if( i == 2 ) theora_encode_tables( &p_sys->td, &header );

        p_enc->fmt_out.p_extra =
            realloc( p_enc->fmt_out.p_extra,
                     p_enc->fmt_out.i_extra + header.bytes );
        p_extra = p_enc->fmt_out.p_extra;
        p_extra += p_enc->fmt_out.i_extra + (i-3)*2;
        p_enc->fmt_out.i_extra += header.bytes;

        *(p_extra++) = header.bytes >> 8;
        *(p_extra++) = header.bytes & 0xFF;

        memcpy( p_extra, header.packet, header.bytes );
gbazin's avatar
   
gbazin committed
717
718
    }

719
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
720
721
722
723
724
725
726
727
728
729
730
731
732
}

/****************************************************************************
 * 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;
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
    int i;

    /* Sanity check */
    if( p_pict->p[0].i_pitch < (int)p_sys->i_width ||
        p_pict->p[0].i_lines < (int)p_sys->i_height )
    {
        msg_Warn( p_enc, "frame is smaller than encoding size"
                  "(%ix%i->%ix%i) -> dropping frame",
                  p_pict->p[0].i_pitch, p_pict->p[0].i_lines,
                  p_sys->i_width, p_sys->i_height );
        return NULL;
    }

    /* Fill padding */
    if( p_pict->p[0].i_visible_pitch < (int)p_sys->i_width )
    {
        for( i = 0; i < p_sys->i_height; i++ )
        {
            memset( p_pict->p[0].p_pixels + i * p_pict->p[0].i_pitch +
                    p_pict->p[0].i_visible_pitch,
                    *( p_pict->p[0].p_pixels + i * p_pict->p[0].i_pitch +
                       p_pict->p[0].i_visible_pitch - 1 ),
                    p_sys->i_width - p_pict->p[0].i_visible_pitch );
        }
        for( i = 0; i < p_sys->i_height / 2; i++ )
        {
            memset( p_pict->p[1].p_pixels + i * p_pict->p[1].i_pitch +
                    p_pict->p[1].i_visible_pitch,
                    *( p_pict->p[1].p_pixels + i * p_pict->p[1].i_pitch +
                       p_pict->p[1].i_visible_pitch - 1 ),
                    p_sys->i_width / 2 - p_pict->p[1].i_visible_pitch );
            memset( p_pict->p[2].p_pixels + i * p_pict->p[2].i_pitch +
                    p_pict->p[2].i_visible_pitch,
                    *( p_pict->p[2].p_pixels + i * p_pict->p[2].i_pitch +
                       p_pict->p[2].i_visible_pitch - 1 ),
                    p_sys->i_width / 2 - p_pict->p[2].i_visible_pitch );
        }
    }

    if( p_pict->p[0].i_visible_lines < (int)p_sys->i_height )
    {
        for( i = p_pict->p[0].i_visible_lines; i < p_sys->i_height; i++ )
        {
            memset( p_pict->p[0].p_pixels + i * p_pict->p[0].i_pitch, 0,
                    p_sys->i_width );
        }
        for( i = p_pict->p[1].i_visible_lines; i < p_sys->i_height / 2; i++ )
        {
            memset( p_pict->p[1].p_pixels + i * p_pict->p[1].i_pitch, 0x80,
                    p_sys->i_width / 2 );
            memset( p_pict->p[2].p_pixels + i * p_pict->p[2].i_pitch, 0x80,
                    p_sys->i_width / 2 );
        }
    }
gbazin's avatar
   
gbazin committed
787
788
789
790

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

791
792
    yuv.y_width  = p_sys->i_width;
    yuv.y_height = p_sys->i_height;
gbazin's avatar
   
gbazin committed
793
794
    yuv.y_stride = p_pict->p[0].i_pitch;

795
796
    yuv.uv_width  = p_sys->i_width / 2;
    yuv.uv_height = p_sys->i_height / 2;
gbazin's avatar
   
gbazin committed
797
798
799
800
801
802
    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
803
804
805
806
807
    if( theora_encode_YUVin( &p_sys->td, &yuv ) < 0 )
    {
        msg_Warn( p_enc, "failed encoding a frame" );
        return NULL;
    }
gbazin's avatar
   
gbazin committed
808
809
810
811
812

    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
813
    memcpy( p_block->p_buffer, oggpacket.packet, oggpacket.bytes );
814
    p_block->i_dts = p_block->i_pts = p_pict->date;
gbazin's avatar
   
gbazin committed
815
816
817
818
819
820
821
822
823
824
825
826
827

    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 );
828
    theora_comment_clear( &p_sys->tc );
gbazin's avatar
   
gbazin committed
829
830
831

    free( p_sys );
}