X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcodec%2Ftheora.c;h=03510dc7a1bc4a4193a534873c7838230fb45be0;hb=cd6b6f2762e5b4c4a0257103a8473e604a3cf844;hp=dda5000b3e17a200d17466c71e7179ce25593f5c;hpb=ec6a7b8ec1fc9f070d1d5242836b1d643f9f2dd8;p=vlc diff --git a/modules/codec/theora.c b/modules/codec/theora.c index dda5000b3e..03510dc7a1 100644 --- a/modules/codec/theora.c +++ b/modules/codec/theora.c @@ -1,24 +1,24 @@ /***************************************************************************** * theora.c: theora decoder module making use of libtheora. ***************************************************************************** - * Copyright (C) 1999-2001 the VideoLAN team + * Copyright (C) 1999-2012 VLC authors and VideoLAN * $Id$ * * Authors: Gildas Bazin * - * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -31,12 +31,18 @@ #include #include #include -#include #include #include +#include "../demux/xiph.h" + #include -#include +#include +#include +#include + +#include +#include /***************************************************************************** * decoder_sys_t : theora decoder descriptor @@ -49,14 +55,14 @@ struct decoder_sys_t /* * Input properties */ - int i_headers; + bool b_has_headers; /* * Theora properties */ - theora_info ti; /* theora bitstream settings */ - theora_comment tc; /* theora comment header */ - theora_state td; /* theora bitstream user comments */ + th_info ti; /* theora bitstream settings */ + th_comment tc; /* theora comment information */ + th_dec_ctx *tcx; /* theora decoder context */ /* * Decoding properties @@ -83,11 +89,13 @@ static void *ProcessPacket ( decoder_t *, ogg_packet *, block_t ** ); static picture_t *DecodePacket( decoder_t *, ogg_packet * ); static void ParseTheoraComments( decoder_t * ); -static void theora_CopyPicture( decoder_t *, picture_t *, yuv_buffer * ); +static void theora_CopyPicture( picture_t *, th_ycbcr_buffer ); +#ifdef ENABLE_SOUT 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 ); +#endif /***************************************************************************** * Module descriptor @@ -97,6 +105,8 @@ static block_t *Encode( encoder_t *p_enc, picture_t *p_pict ); "Enforce a quality between 1 (low) and 10 (high), instead " \ "of specifying a particular bitrate. This will produce a VBR stream." ) +#define ENC_POSTPROCESS_TEXT N_("Post processing quality") + vlc_module_begin () set_category( CAT_INPUT ) set_subcategory( SUBCAT_INPUT_VCODEC ) @@ -105,6 +115,8 @@ vlc_module_begin () set_capability( "decoder", 100 ) set_callbacks( OpenDecoder, CloseDecoder ) add_shortcut( "theora" ) +# define DEC_CFG_PREFIX "theora-" + add_integer( DEC_CFG_PREFIX "postproc", -1, ENC_POSTPROCESS_TEXT, NULL, true ) add_submodule () set_description( N_("Theora video packetizer") ) @@ -112,6 +124,7 @@ vlc_module_begin () set_callbacks( OpenPacketizer, CloseDecoder ) add_shortcut( "theora" ) +#ifdef ENABLE_SOUT add_submodule () set_description( N_("Theora video encoder") ) set_capability( "encoder", 150 ) @@ -119,8 +132,9 @@ vlc_module_begin () add_shortcut( "theora" ) # define ENC_CFG_PREFIX "sout-theora-" - add_integer( ENC_CFG_PREFIX "quality", 2, NULL, ENC_QUALITY_TEXT, + add_integer( ENC_CFG_PREFIX "quality", 2, ENC_QUALITY_TEXT, ENC_QUALITY_LONGTEXT, false ) +#endif vlc_module_end () static const char *const ppsz_enc_options[] = { @@ -144,9 +158,10 @@ static int OpenDecoder( vlc_object_t *p_this ) if( ( p_dec->p_sys = p_sys = malloc(sizeof(*p_sys)) ) == NULL ) return VLC_ENOMEM; p_dec->p_sys->b_packetizer = false; - - p_sys->i_pts = 0; + p_sys->b_has_headers = false; + p_sys->i_pts = VLC_TS_INVALID; p_sys->b_decoded_first_keyframe = false; + p_sys->tcx = NULL; /* Set output properties */ p_dec->fmt_out.i_cat = VIDEO_ES; @@ -159,10 +174,8 @@ static int OpenDecoder( vlc_object_t *p_this ) 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; + th_comment_init( &p_sys->tc ); + th_info_init( &p_sys->ti ); return VLC_SUCCESS; } @@ -206,41 +219,14 @@ static void *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) oggpacket.packetno = 0; /* Check for headers */ - if( p_sys->i_headers == 0 && p_dec->fmt_in.i_extra ) - { - /* Headers already available as extra data */ - p_sys->i_headers = 3; - } - else if( oggpacket.bytes && p_sys->i_headers < 3 ) + if( !p_sys->b_has_headers ) { - /* 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 = ((uint8_t *)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; - } - - if( p_sys->i_headers == 3 ) - { - if( ProcessHeaders( p_dec ) != VLC_SUCCESS ) + if( ProcessHeaders( p_dec ) ) { - p_sys->i_headers = 0; - p_dec->fmt_in.i_extra = 0; - block_Release( *pp_block ); + block_Release( p_block ); return NULL; } - else p_sys->i_headers++; + p_sys->b_has_headers = true; } return ProcessPacket( p_dec, &oggpacket, pp_block ); @@ -253,76 +239,75 @@ 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; - - if( !p_dec->fmt_in.i_extra ) return VLC_EGENERIC; + th_setup_info *ts = NULL; /* theora setup information */ + int i_max_pp, i_pp; + + unsigned pi_size[XIPH_MAX_HEADER_COUNT]; + void *pp_data[XIPH_MAX_HEADER_COUNT]; + unsigned i_count; + if( xiph_SplitHeaders( pi_size, pp_data, &i_count, + p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) ) + return VLC_EGENERIC; + if( i_count < 3 ) + return VLC_EGENERIC; 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; - } - - if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 ) + oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */ + oggpacket.bytes = pi_size[0]; + oggpacket.packet = pp_data[0]; + if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 ) { msg_Err( p_dec, "this bitstream does not contain Theora video data" ); - return VLC_EGENERIC; + goto error; } /* Set output properties */ - switch( p_sys->ti.pixelformat ) + if( !p_sys->b_packetizer ) + + switch( p_sys->ti.pixel_fmt ) { - case OC_PF_420: + case TH_PF_420: p_dec->fmt_out.i_codec = VLC_CODEC_I420; break; - case OC_PF_422: + case TH_PF_422: p_dec->fmt_out.i_codec = VLC_CODEC_I422; break; - case OC_PF_444: + case TH_PF_444: p_dec->fmt_out.i_codec = VLC_CODEC_I444; break; - case OC_PF_RSVD: + case TH_PF_RSVD: default: msg_Err( p_dec, "unknown chroma in theora sample" ); break; } - p_dec->fmt_out.video.i_width = p_sys->ti.width; - p_dec->fmt_out.video.i_height = p_sys->ti.height; - 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; + if( p_sys->ti.pic_width && p_sys->ti.pic_height ) { - p_dec->fmt_out.video.i_visible_width = p_sys->ti.frame_width; - p_dec->fmt_out.video.i_visible_height = p_sys->ti.frame_height; - if( p_sys->ti.offset_x || p_sys->ti.offset_y ) + p_dec->fmt_out.video.i_visible_width = p_sys->ti.pic_width; + p_dec->fmt_out.video.i_visible_height = p_sys->ti.pic_height; + + if( p_sys->ti.pic_x || p_sys->ti.pic_y ) { - p_dec->fmt_out.video.i_x_offset = p_sys->ti.offset_x; - p_dec->fmt_out.video.i_y_offset = p_sys->ti.offset_y; + p_dec->fmt_out.video.i_x_offset = p_sys->ti.pic_x; + p_dec->fmt_out.video.i_y_offset = p_sys->ti.pic_y; } } if( p_sys->ti.aspect_denominator && p_sys->ti.aspect_numerator ) { - p_dec->fmt_out.video.i_aspect = ((int64_t)VOUT_ASPECT_FACTOR) * - ( p_sys->ti.aspect_numerator * p_dec->fmt_out.video.i_width ) / - ( p_sys->ti.aspect_denominator * p_dec->fmt_out.video.i_height ); + p_dec->fmt_out.video.i_sar_num = p_sys->ti.aspect_numerator; + p_dec->fmt_out.video.i_sar_den = p_sys->ti.aspect_denominator; } else { - p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR * - p_sys->ti.frame_width / p_sys->ti.frame_height; + p_dec->fmt_out.video.i_sar_num = 1; + p_dec->fmt_out.video.i_sar_den = 1; } if( p_sys->ti.fps_numerator > 0 && p_sys->ti.fps_denominator > 0 ) @@ -333,44 +318,43 @@ static int ProcessHeaders( decoder_t *p_dec ) 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 ); + (double)p_sys->ti.fps_numerator/p_sys->ti.fps_denominator, + p_sys->ti.pic_width, p_sys->ti.pic_height, + p_sys->ti.pic_x, p_sys->ti.pic_y ); + + /* Some assertions based on the documentation. These are mandatory restrictions. */ + assert( p_sys->ti.frame_height % 16 == 0 && p_sys->ti.frame_height < 1048576 ); + assert( p_sys->ti.frame_width % 16 == 0 && p_sys->ti.frame_width < 1048576 ); + assert( p_sys->ti.keyframe_granule_shift >= 0 && p_sys->ti.keyframe_granule_shift <= 31 ); + assert( p_sys->ti.pic_x <= __MIN( p_sys->ti.frame_width - p_sys->ti.pic_width, 255 ) ); + assert( p_sys->ti.pic_y <= p_sys->ti.frame_height - p_sys->ti.pic_height); + assert( p_sys->ti.frame_height - p_sys->ti.pic_height - p_sys->ti.pic_y <= 255 ); /* 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 ) + if( p_sys->ti.frame_width < p_sys->ti.pic_width || + p_sys->ti.frame_height < p_sys->ti.pic_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 ); + p_sys->ti.frame_width, p_sys->ti.frame_height, + p_sys->ti.pic_width, p_sys->ti.pic_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; + if( p_sys->ti.frame_width < p_sys->ti.pic_width ) + p_sys->ti.frame_width = p_sys->ti.pic_width; + if( p_sys->ti.frame_height < p_sys->ti.pic_height ) + p_sys->ti.frame_height = p_sys->ti.pic_height; } /* 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 ) - { - msg_Err( p_dec, "header data corrupted"); - return VLC_EGENERIC; - } + oggpacket.b_o_s = 0; + oggpacket.bytes = pi_size[1]; + oggpacket.packet = pp_data[1]; - /* The next packet in order is the comments header */ - if( theora_decode_header( &p_sys->ti, &p_sys->tc, &oggpacket ) < 0 ) + if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 ) { msg_Err( p_dec, "2nd Theora header is corrupted" ); - return VLC_EGENERIC; + goto error; } ParseTheoraComments( p_dec ); @@ -378,40 +362,56 @@ static int ProcessHeaders( decoder_t *p_dec ) /* 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; - } - - /* 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 ) + oggpacket.b_o_s = 0; + oggpacket.bytes = pi_size[2]; + oggpacket.packet = pp_data[2]; + if( th_decode_headerin( &p_sys->ti, &p_sys->tc, &ts, &oggpacket ) < 0 ) { msg_Err( p_dec, "3rd Theora header is corrupted" ); - return VLC_EGENERIC; + goto error; } if( !p_sys->b_packetizer ) { /* We have all the headers, initialize decoder */ - theora_decode_init( &p_sys->td, &p_sys->ti ); + if ( ( p_sys->tcx = th_decode_alloc( &p_sys->ti, ts ) ) == NULL ) + { + msg_Err( p_dec, "Could not allocate Theora decoder" ); + goto error; + } + + i_pp = var_InheritInteger( p_dec, DEC_CFG_PREFIX "postproc" ); + if ( i_pp >= 0 && !th_decode_ctl( p_sys->tcx, + TH_DECCTL_GET_PPLEVEL_MAX, &i_max_pp, sizeof(int) ) ) + { + i_pp = __MIN( i_pp, i_max_pp ); + if ( th_decode_ctl( p_sys->tcx, TH_DECCTL_SET_PPLEVEL, + &i_pp, sizeof(int) ) ) + msg_Err( p_dec, "Failed to set post processing level to %d", + i_pp ); + else + msg_Dbg( p_dec, "Set post processing level to %d / %d", + i_pp, i_max_pp ); + } + } 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 ); + p_dec->fmt_out.p_extra = xrealloc( 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 ); } + /* Clean up the decoder setup info... we're done with it */ + th_setup_free( ts ); return VLC_SUCCESS; + +error: + /* Clean up the decoder setup info... we're done with it */ + th_setup_free( ts ); + return VLC_EGENERIC; } /***************************************************************************** @@ -429,11 +429,12 @@ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket, /* 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 */ + block_Release(p_block); return NULL; } /* Date management */ - if( p_block->i_pts > 0 && p_block->i_pts != p_sys->i_pts ) + if( p_block->i_pts > VLC_TS_INVALID && p_block->i_pts != p_sys->i_pts ) { p_sys->i_pts = p_block->i_pts; } @@ -445,25 +446,18 @@ static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket, /* 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_block->i_length = p_sys->i_pts - p_block->i_pts; p_buf = p_block; } else { - if( p_sys->i_headers >= 3 ) - p_buf = DecodePacket( p_dec, p_oggpacket ); - else - p_buf = NULL; - - if( p_block ) block_Release( p_block ); + p_buf = DecodePacket( p_dec, p_oggpacket ); + block_Release( p_block ); } /* Date management */ - p_sys->i_pts += ( INT64_C(1000000) * p_sys->ti.fps_denominator / + p_sys->i_pts += ( CLOCK_FREQ * p_sys->ti.fps_denominator / p_sys->ti.fps_numerator ); /* 1 frame per packet */ return p_buf; @@ -476,9 +470,14 @@ static picture_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket ) { decoder_sys_t *p_sys = p_dec->p_sys; picture_t *p_pic; - yuv_buffer yuv; + th_ycbcr_buffer ycbcr; - theora_decode_packetin( &p_sys->td, p_oggpacket ); + /* TODO: Implement _granpos (3rd parameter here) and add the + * call to TH_DECCTL_SET_GRANDPOS after seek */ + /* TODO: If the return is TH_DUPFRAME, we don't need to display a new + * frame, but we do need to keep displaying the previous one. */ + if (th_decode_packetin( p_sys->tcx, p_oggpacket, NULL ) < 0) + return NULL; /* bad packet */ /* Check for keyframe */ if( !(p_oggpacket->packet[0] & 0x80) /* data packet */ && @@ -490,16 +489,17 @@ static picture_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket ) * 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 + if( !p_sys->b_decoded_first_keyframe ) + return NULL; /* Wait until we've decoded the first keyframe */ + + if( th_decode_ycbcr_out( p_sys->tcx, ycbcr ) ) /* returns 0 on success */ return NULL; /* Get a new picture */ p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) return NULL; - theora_CopyPicture( p_dec, p_pic, &yuv ); + theora_CopyPicture( p_pic, ycbcr ); p_pic->date = p_sys->i_pts; @@ -513,12 +513,29 @@ static void ParseTheoraComments( decoder_t *p_dec ) { char *psz_name, *psz_value, *psz_comment; int i = 0; - + /* Regarding the th_comment structure: */ + + /* The metadata is stored as a series of (tag, value) pairs, in + length-encoded string vectors. The first occurrence of the '=' + character delimits the tag and value. A particular tag may + occur more than once, and order is significant. The character + set encoding for the strings is always UTF-8, but the tag names + are limited to ASCII, and treated as case-insensitive. See the + Theora specification, Section 6.3.3 for details. */ + + /* In filling in this structure, th_decode_headerin() will + null-terminate the user_comment strings for safety. However, + the bitstream format itself treats them as 8-bit clean vectors, + possibly containing null characters, and so the length array + should be treated as their authoritative length. */ while ( i < p_dec->p_sys->tc.comments ) { - psz_comment = strdup( p_dec->p_sys->tc.user_comments[i] ); + int clen = p_dec->p_sys->tc.comment_lengths[i]; + if ( clen <= 0 || clen >= INT_MAX ) { i++; continue; } + psz_comment = (char *)malloc( clen + 1 ); if( !psz_comment ) break; + memcpy( (void*)psz_comment, (void*)p_dec->p_sys->tc.user_comments[i], clen + 1 ); psz_name = psz_comment; psz_value = strchr( psz_comment, '=' ); if( psz_value ) @@ -528,6 +545,8 @@ static void ParseTheoraComments( decoder_t *p_dec ) if( !p_dec->p_description ) p_dec->p_description = vlc_meta_New(); + /* TODO: Since psz_value can contain NULLs see if there is an + * instance where we need to preserve the full length of this string */ if( p_dec->p_description ) vlc_meta_AddExtra( p_dec->p_description, psz_name, psz_value ); } @@ -544,9 +563,9 @@ static void CloseDecoder( vlc_object_t *p_this ) decoder_t *p_dec = (decoder_t *)p_this; decoder_sys_t *p_sys = p_dec->p_sys; - theora_info_clear( &p_sys->ti ); - theora_comment_clear( &p_sys->tc ); - + th_info_clear(&p_sys->ti); + th_comment_clear(&p_sys->tc); + th_decode_free(p_sys->tcx); free( p_sys ); } @@ -554,29 +573,51 @@ static void CloseDecoder( vlc_object_t *p_this ) * theora_CopyPicture: copy a picture from theora internal buffers to a * picture_t structure. *****************************************************************************/ -static void theora_CopyPicture( decoder_t *p_dec, picture_t *p_pic, - yuv_buffer *yuv ) +static void theora_CopyPicture( picture_t *p_pic, + th_ycbcr_buffer ycbcr ) { - int i_plane, i_line, i_dst_stride, i_src_stride; + int i_plane, i_planes, i_line, i_dst_stride, i_src_stride; uint8_t *p_dst, *p_src; + /* th_img_plane + int width The width of this plane. + int height The height of this plane. + int stride The offset in bytes between successive rows. + unsigned char *data A pointer to the beginning of the first row. + + Detailed Description + + A buffer for a single color plane in an uncompressed image. - for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ ) + This contains the image data in a left-to-right, top-down + format. Each row of pixels is stored contiguously in memory, + but successive rows need not be. Use stride to compute the + offset of the next row. The encoder accepts both positive + stride values (top-down in memory) and negative (bottom-up in + memory). The decoder currently always generates images with + positive strides. + + typedef th_img_plane th_ycbcr_buffer[3] + */ + + i_planes = p_pic->i_planes < 3 ? p_pic->i_planes : 3; + for( i_plane = 0; i_plane < 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; + p_src = ycbcr[i_plane].data; i_dst_stride = p_pic->p[i_plane].i_pitch; - i_src_stride = i_plane ? yuv->uv_stride : yuv->y_stride; - - for( i_line = 0; i_line < p_pic->p[i_plane].i_lines; i_line++ ) + i_src_stride = ycbcr[i_plane].stride; + for( i_line = 0; + i_line < __MIN(p_pic->p[i_plane].i_lines, ycbcr[i_plane].height); + i_line++ ) { - vlc_memcpy( p_dst, p_src, - i_plane ? yuv->uv_width : yuv->y_width ); + memcpy( p_dst, p_src, ycbcr[i_plane].width ); p_src += i_src_stride; p_dst += i_dst_stride; } } } +#ifdef ENABLE_SOUT /***************************************************************************** * encoder_sys_t : theora encoder descriptor *****************************************************************************/ @@ -590,11 +631,9 @@ struct encoder_sys_t /* * Theora properties */ - theora_info ti; /* theora bitstream settings */ - theora_comment tc; /* theora comment header */ - theora_state td; /* theora bitstream user comments */ - - int i_width, i_height; + th_info ti; /* theora bitstream settings */ + th_comment tc; /* theora comment header */ + th_enc_ctx *tcx; /* theora context */ }; /***************************************************************************** @@ -603,11 +642,13 @@ struct encoder_sys_t 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; + encoder_sys_t *p_sys; + int i_quality; + int t_flags; + int max_enc_level = 0; + int keyframe_freq_force = 64; ogg_packet header; - uint8_t *p_extra; - vlc_value_t val; - int i_quality, i; + int status; if( p_enc->fmt_out.i_codec != VLC_CODEC_THEORA && !p_enc->b_force ) @@ -615,8 +656,8 @@ static int OpenEncoder( vlc_object_t *p_this ) 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 ) + /* Allocate the memory needed to store the encoder's structure */ + if( ( p_sys = malloc(sizeof(encoder_sys_t)) ) == NULL ) return VLC_ENOMEM; p_enc->p_sys = p_sys; @@ -626,35 +667,31 @@ static int OpenEncoder( vlc_object_t *p_this ) config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); - var_Get( p_enc, ENC_CFG_PREFIX "quality", &val ); - i_quality = val.i_int; + i_quality = var_GetInteger( p_enc, ENC_CFG_PREFIX "quality" ); if( i_quality > 10 ) i_quality = 10; if( i_quality < 0 ) i_quality = 0; - theora_info_init( &p_sys->ti ); + th_info_init( &p_sys->ti ); - 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_visible_width; + p_sys->ti.frame_height = p_enc->fmt_in.video.i_visible_height; - if( p_sys->ti.width % 16 || p_sys->ti.height % 16 ) + if( p_sys->ti.frame_width % 16 || p_sys->ti.frame_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; + p_sys->ti.frame_width = (p_sys->ti.frame_width + 15) >> 4 << 4; + p_sys->ti.frame_height = (p_sys->ti.frame_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 ); + p_enc->fmt_in.video.i_visible_width, p_enc->fmt_in.video.i_visible_height, + p_sys->ti.frame_width, p_sys->ti.frame_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; - p_sys->ti.offset_x = 0 /*frame_x_offset*/; - p_sys->ti.offset_y = 0 /*frame_y_offset*/; - - p_sys->i_width = p_sys->ti.width; - p_sys->i_height = p_sys->ti.height; + p_sys->ti.pic_width = p_enc->fmt_in.video.i_visible_width; + p_sys->ti.pic_height = p_enc->fmt_in.video.i_visible_height; + p_sys->ti.pic_x = 0 /*frame_x_offset*/; + p_sys->ti.pic_y = 0 /*frame_y_offset*/; if( !p_enc->fmt_in.video.i_frame_rate || !p_enc->fmt_in.video.i_frame_rate_base ) @@ -668,14 +705,12 @@ static int OpenEncoder( vlc_object_t *p_this ) p_sys->ti.fps_denominator = p_enc->fmt_in.video.i_frame_rate_base; } - if( p_enc->fmt_in.video.i_aspect ) + if( p_enc->fmt_in.video.i_sar_num > 0 && p_enc->fmt_in.video.i_sar_den > 0 ) { - uint64_t i_num, i_den; unsigned i_dst_num, i_dst_den; - - i_num = p_enc->fmt_in.video.i_aspect * (int64_t)p_sys->ti.height; - i_den = VOUT_ASPECT_FACTOR * p_sys->ti.width; - vlc_ureduce( &i_dst_num, &i_dst_den, i_num, i_den, 0 ); + vlc_ureduce( &i_dst_num, &i_dst_den, + p_enc->fmt_in.video.i_sar_num, + p_enc->fmt_in.video.i_sar_den, 0 ); p_sys->ti.aspect_numerator = i_dst_num; p_sys->ti.aspect_denominator = i_dst_den; } @@ -686,8 +721,14 @@ static int OpenEncoder( vlc_object_t *p_this ) } p_sys->ti.target_bitrate = p_enc->fmt_out.i_bitrate; - p_sys->ti.quality = ((float)i_quality) * 6.3; + p_sys->ti.quality = ((float)i_quality) * 6.3f; + + + p_sys->tcx = th_encode_alloc( &p_sys->ti ); + th_comment_init( &p_sys->tc ); + /* These are no longer supported here: */ + /* p_sys->ti.dropframes_p = 0; p_sys->ti.quick_p = 1; p_sys->ti.keyframe_auto_p = 1; @@ -697,32 +738,32 @@ static int OpenEncoder( vlc_object_t *p_this ) 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 ); - theora_comment_init( &p_sys->tc ); + t_flags = TH_RATECTL_CAP_OVERFLOW; /* default is TH_RATECTL_CAP_OVERFLOW | TL_RATECTL_DROP_FRAMES */ + /* Turn off dropframes */ + th_encode_ctl( p_sys->tcx, TH_ENCCTL_SET_RATE_FLAGS, &t_flags, sizeof(t_flags) ); - /* Create and store headers */ - p_enc->fmt_out.i_extra = 3 * 2; - for( i = 0; i < 3; i++ ) - { - 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 ); + /* turn on fast encoding */ + if ( !th_encode_ctl( p_sys->tcx, TH_ENCCTL_GET_SPLEVEL_MAX, &max_enc_level, + sizeof(max_enc_level) ) ) /* returns 0 on success */ + th_encode_ctl( p_sys->tcx, TH_ENCCTL_SET_SPLEVEL, &max_enc_level, sizeof(max_enc_level) ); - 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; + /* Set forced distance between key frames */ + th_encode_ctl( p_sys->tcx, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, + &keyframe_freq_force, sizeof(keyframe_freq_force) ); - *(p_extra++) = header.bytes >> 8; - *(p_extra++) = header.bytes & 0xFF; - - memcpy( p_extra, header.packet, header.bytes ); + /* Create and store headers */ + while ( ( status = th_encode_flushheader( p_sys->tcx, &p_sys->tc, &header ) ) ) + { + if ( status < 0 ) return VLC_EGENERIC; + if( xiph_AppendHeaders( &p_enc->fmt_out.i_extra, &p_enc->fmt_out.p_extra, + header.bytes, header.packet ) ) + { + p_enc->fmt_out.i_extra = 0; + p_enc->fmt_out.p_extra = NULL; + } } - return VLC_SUCCESS; } @@ -736,90 +777,99 @@ 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; - int i; + th_ycbcr_buffer ycbcr; + unsigned i; + if( !p_pict ) return NULL; /* 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 ) + if( p_pict->p[0].i_pitch < (int)p_sys->ti.frame_width || + p_pict->p[0].i_lines < (int)p_sys->ti.frame_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 ); + p_sys->ti.frame_width, p_sys->ti.frame_height ); return NULL; } /* Fill padding */ - if( p_pict->p[0].i_visible_pitch < (int)p_sys->i_width ) + if( p_pict->p[0].i_visible_pitch < (int)p_sys->ti.frame_width ) { - for( i = 0; i < p_sys->i_height; i++ ) + for( i = 0; i < p_sys->ti.frame_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 ); + p_sys->ti.frame_width - p_pict->p[0].i_visible_pitch ); } - for( i = 0; i < p_sys->i_height / 2; i++ ) + for( i = 0; i < p_sys->ti.frame_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 ); + p_sys->ti.frame_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 ); + p_sys->ti.frame_width / 2 - p_pict->p[2].i_visible_pitch ); } } - if( p_pict->p[0].i_visible_lines < (int)p_sys->i_height ) + if( p_pict->p[0].i_visible_lines < (int)p_sys->ti.frame_height ) { - for( i = p_pict->p[0].i_visible_lines; i < p_sys->i_height; i++ ) + for( i = p_pict->p[0].i_visible_lines; i < p_sys->ti.frame_height; i++ ) { memset( p_pict->p[0].p_pixels + i * p_pict->p[0].i_pitch, 0, - p_sys->i_width ); + p_sys->ti.frame_width ); } - for( i = p_pict->p[1].i_visible_lines; i < p_sys->i_height / 2; i++ ) + for( i = p_pict->p[1].i_visible_lines; i < p_sys->ti.frame_height / 2; i++ ) { memset( p_pict->p[1].p_pixels + i * p_pict->p[1].i_pitch, 0x80, - p_sys->i_width / 2 ); + p_sys->ti.frame_width / 2 ); memset( p_pict->p[2].p_pixels + i * p_pict->p[2].i_pitch, 0x80, - p_sys->i_width / 2 ); + p_sys->ti.frame_width / 2 ); } } /* Theora is a one-frame-in, one-frame-out system. Submit a frame * for compression and pull out the packet. */ - yuv.y_width = p_sys->i_width; - yuv.y_height = p_sys->i_height; - yuv.y_stride = p_pict->p[0].i_pitch; + ycbcr[0].width = p_sys->ti.frame_width; + ycbcr[0].height = p_sys->ti.frame_height; + ycbcr[0].stride = p_pict->p[0].i_pitch; + ycbcr[0].data = p_pict->p[0].p_pixels; - yuv.uv_width = p_sys->i_width / 2; - yuv.uv_height = p_sys->i_height / 2; - yuv.uv_stride = p_pict->p[1].i_pitch; + ycbcr[1].width = p_sys->ti.frame_width / 2; + ycbcr[1].height = p_sys->ti.frame_height / 2; + ycbcr[1].stride = p_pict->p[1].i_pitch; + ycbcr[1].data = p_pict->p[1].p_pixels; - yuv.y = p_pict->p[0].p_pixels; - yuv.u = p_pict->p[1].p_pixels; - yuv.v = p_pict->p[2].p_pixels; + ycbcr[2].width = p_sys->ti.frame_width / 2; + ycbcr[2].height = p_sys->ti.frame_height / 2; + ycbcr[2].stride = p_pict->p[1].i_pitch; + ycbcr[2].data = p_pict->p[2].p_pixels; - if( theora_encode_YUVin( &p_sys->td, &yuv ) < 0 ) + if( th_encode_ycbcr_in( p_sys->tcx, ycbcr ) < 0 ) { msg_Warn( p_enc, "failed encoding a frame" ); return NULL; } - theora_encode_packetout( &p_sys->td, 0, &oggpacket ); + th_encode_packetout( p_sys->tcx, 0, &oggpacket ); /* Ogg packet to block */ - p_block = block_New( p_enc, oggpacket.bytes ); + p_block = block_Alloc( oggpacket.bytes ); memcpy( p_block->p_buffer, oggpacket.packet, oggpacket.bytes ); p_block->i_dts = p_block->i_pts = p_pict->date; + if( th_packet_iskeyframe( &oggpacket ) ) + { + p_block->i_flags |= BLOCK_FLAG_TYPE_I; + } + return p_block; } @@ -831,8 +881,9 @@ 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 ); - theora_comment_clear( &p_sys->tc ); - + th_info_clear(&p_sys->ti); + th_comment_clear(&p_sys->tc); + th_encode_free(p_sys->tcx); free( p_sys ); } +#endif