-/*****************************************************************************
- * video.c: video decoder using the ffmpeg library
- *****************************************************************************
- * Copyright (C) 1999-2001 VideoLAN
- * $Id$
- *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- * Gildas Bazin <gbazin@videolan.org>
- *
- * 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.
- *
- * 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>
-
-/* ffmpeg header */
-#ifdef HAVE_FFMPEG_AVCODEC_H
-# include <ffmpeg/avcodec.h>
-#else
-# include <avcodec.h>
-#endif
-
-#include "ffmpeg.h"
-
-/*****************************************************************************
- * decoder_sys_t : decoder descriptor
- *****************************************************************************/
-struct decoder_sys_t
-{
- /* Common part between video and audio decoder */
- int i_cat;
- int i_codec_id;
- char *psz_namecodec;
-
- AVCodecContext *p_context;
- AVCodec *p_codec;
-
- /* Video decoder specific part */
- mtime_t input_pts;
- mtime_t input_dts;
- mtime_t i_pts;
-
- AVFrame *p_ff_pic;
- BITMAPINFOHEADER *p_format;
-
- /* for frame skipping algo */
- int b_hurry_up;
- int i_frame_skip;
-
- /* how many decoded frames are late */
- int i_late_frames;
- mtime_t i_late_frames_start;
-
- /* for direct rendering */
- int b_direct_rendering;
-
- vlc_bool_t b_has_b_frames;
-
- /* Hack to force display of still pictures */
- vlc_bool_t b_first_frame;
-
- int i_buffer_orig, i_buffer;
- char *p_buffer_orig, *p_buffer;
-
- /* Postprocessing handle */
- void *p_pp;
- vlc_bool_t b_pp;
- vlc_bool_t b_pp_async;
- vlc_bool_t b_pp_init;
-};
-
-/* FIXME (dummy palette for now) */
-static AVPaletteControl palette_control;
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-static void ffmpeg_CopyPicture ( decoder_t *, picture_t *, AVFrame * );
-static int ffmpeg_GetFrameBuf ( struct AVCodecContext *, AVFrame * );
-static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * );
-
-static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
-{
- uint8_t *p = (uint8_t*)&fcc;
- return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
-}
-
-/*****************************************************************************
- * Local Functions
- *****************************************************************************/
-static uint32_t ffmpeg_PixFmtToChroma( int i_ff_chroma )
-{
- switch( i_ff_chroma )
- {
- case PIX_FMT_YUV420P:
- return VLC_FOURCC('I','4','2','0');
- case PIX_FMT_YUV422P:
- return VLC_FOURCC('I','4','2','2');
- case PIX_FMT_YUV444P:
- return VLC_FOURCC('I','4','4','4');
-
- case PIX_FMT_YUV422:
- return VLC_FOURCC('Y','U','Y','2');
-
- case PIX_FMT_RGB555:
- return VLC_FOURCC('R','V','1','5');
- case PIX_FMT_RGB565:
- return VLC_FOURCC('R','V','1','6');
- case PIX_FMT_RGB24:
- return VLC_FOURCC('R','V','2','4');
- case PIX_FMT_RGBA32:
- return VLC_FOURCC('R','V','3','2');
- case PIX_FMT_GRAY8:
- return VLC_FOURCC('G','R','E','Y');
-
- case PIX_FMT_YUV410P:
- case PIX_FMT_YUV411P:
- case PIX_FMT_BGR24:
- default:
- return 0;
- }
-}
-
-/* Returns a new picture buffer */
-static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec,
- AVCodecContext *p_context )
-{
- decoder_sys_t *p_sys = p_dec->p_sys;
- picture_t *p_pic;
-
- p_dec->fmt_out.video.i_width = p_context->width;
- p_dec->fmt_out.video.i_height = p_context->height;
- p_dec->fmt_out.i_codec = ffmpeg_PixFmtToChroma( p_context->pix_fmt );
-
- if( !p_context->width || !p_context->height )
- {
- return NULL; /* invalid display size */
- }
-
- if( !p_dec->fmt_out.i_codec )
- {
- /* we make conversion if possible*/
- p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0');
- }
-
- /* If an aspect-ratio was specified in the input format then force it */
- if( p_dec->fmt_in.video.i_aspect )
- {
- p_dec->fmt_out.video.i_aspect = p_dec->fmt_in.video.i_aspect;
- }
- else
- {
-#if LIBAVCODEC_BUILD >= 4687
- p_dec->fmt_out.video.i_aspect =
- VOUT_ASPECT_FACTOR * ( av_q2d(p_context->sample_aspect_ratio) *
- p_context->width / p_context->height );
-#else
- p_dec->fmt_out.video.i_aspect =
- VOUT_ASPECT_FACTOR * p_context->aspect_ratio;
-#endif
- if( p_dec->fmt_out.video.i_aspect == 0 )
- {
- p_dec->fmt_out.video.i_aspect =
- VOUT_ASPECT_FACTOR * p_context->width / p_context->height;
- }
- }
-
- if( p_context->frame_rate > 0 && p_context->frame_rate_base > 0 )
- {
- p_dec->fmt_out.video.i_frame_rate = p_context->frame_rate;
- p_dec->fmt_out.video.i_frame_rate_base = p_context->frame_rate_base;
- }
-
- p_pic = p_dec->pf_vout_buffer_new( p_dec );
-
-#ifdef LIBAVCODEC_PP
- if( p_sys->p_pp && p_sys->b_pp && !p_sys->b_pp_init )
- {
- E_(InitPostproc)( p_dec, p_sys->p_pp, p_context->width,
- p_context->height, p_context->pix_fmt );
- p_sys->b_pp_init = VLC_TRUE;
- }
-#endif
-
- return p_pic;
-}
-
-/*****************************************************************************
- * InitVideo: initialize the video decoder
- *****************************************************************************
- * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
- * opened (done after the first decoded frame).
- *****************************************************************************/
-int E_(InitVideoDec)( decoder_t *p_dec, AVCodecContext *p_context,
- AVCodec *p_codec, int i_codec_id, char *psz_namecodec )
-{
- decoder_sys_t *p_sys;
- vlc_value_t lockval;
- vlc_value_t val;
-
- var_Get( p_dec->p_libvlc, "avcodec", &lockval );
-
- /* Allocate the memory needed to store the decoder's structure */
- if( ( p_dec->p_sys = p_sys =
- (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
- {
- msg_Err( p_dec, "out of memory" );
- return VLC_EGENERIC;
- }
-
- p_dec->p_sys->p_context = p_context;
- p_dec->p_sys->p_codec = p_codec;
- p_dec->p_sys->i_codec_id = i_codec_id;
- p_dec->p_sys->psz_namecodec = psz_namecodec;
- p_sys->p_ff_pic = avcodec_alloc_frame();
-
- /* ***** Fill p_context with init values ***** */
- /* FIXME: remove when ffmpeg deals properly with avc1 */
- if( p_dec->fmt_in.i_codec != VLC_FOURCC('a','v','c','1') )
- /* End FIXME */
- p_sys->p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_codec );
- p_sys->p_context->width = p_dec->fmt_in.video.i_width;
- p_sys->p_context->height = p_dec->fmt_in.video.i_height;
- p_sys->p_context->bits_per_sample = p_dec->fmt_in.video.i_bits_per_pixel;
-
- /* ***** Get configuration of ffmpeg plugin ***** */
- p_sys->p_context->workaround_bugs =
- config_GetInt( p_dec, "ffmpeg-workaround-bugs" );
- p_sys->p_context->error_resilience =
- config_GetInt( p_dec, "ffmpeg-error-resilience" );
-
- var_Create( p_dec, "grayscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_dec, "grayscale", &val );
- if( val.b_bool ) p_sys->p_context->flags |= CODEC_FLAG_GRAY;
-
- var_Create( p_dec, "ffmpeg-vismv", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
- var_Get( p_dec, "ffmpeg-vismv", &val );
-#if LIBAVCODEC_BUILD >= 4698
- if( val.i_int ) p_sys->p_context->debug_mv = val.i_int;
-#endif
-
- var_Create( p_dec, "ffmpeg-lowres", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
- var_Get( p_dec, "ffmpeg-lowres", &val );
-#if LIBAVCODEC_BUILD >= 4723
- if( val.i_int > 0 && val.i_int <= 2 ) p_sys->p_context->lowres = val.i_int;
-#endif
-
- /* ***** ffmpeg frame skipping ***** */
- var_Create( p_dec, "ffmpeg-hurry-up", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_dec, "ffmpeg-hurry-up", &val );
- p_sys->b_hurry_up = val.b_bool;
-
- /* ***** ffmpeg direct rendering ***** */
- p_sys->b_direct_rendering = 0;
- var_Create( p_dec, "ffmpeg-dr", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
- var_Get( p_dec, "ffmpeg-dr", &val );
- if( val.b_bool && (p_sys->p_codec->capabilities & CODEC_CAP_DR1) &&
- ffmpeg_PixFmtToChroma( p_sys->p_context->pix_fmt ) &&
- /* Apparently direct rendering doesn't work with YUV422P */
- p_sys->p_context->pix_fmt != PIX_FMT_YUV422P &&
- /* H264 uses too many reference frames */
- p_sys->i_codec_id != CODEC_ID_H264 &&
- !(p_sys->p_context->width % 16) && !(p_sys->p_context->height % 16) &&
-#if LIBAVCODEC_BUILD >= 4698
- !p_sys->p_context->debug_mv )
-#else
- 1 )
-#endif
- {
- /* Some codecs set pix_fmt only after the 1st frame has been decoded,
- * so we need to do another check in ffmpeg_GetFrameBuf() */
- p_sys->b_direct_rendering = 1;
- }
-
-#ifdef LIBAVCODEC_PP
- p_sys->p_pp = NULL;
- p_sys->b_pp = p_sys->b_pp_async = p_sys->b_pp_init = VLC_FALSE;
- p_sys->p_pp = E_(OpenPostproc)( p_dec, &p_sys->b_pp_async );
-#endif
-
- /* ffmpeg doesn't properly release old pictures when frames are skipped */
- //if( p_sys->b_hurry_up ) p_sys->b_direct_rendering = 0;
- if( p_sys->b_direct_rendering )
- {
- msg_Dbg( p_dec, "using direct rendering" );
- p_sys->p_context->flags |= CODEC_FLAG_EMU_EDGE;
- }
-
- /* Always use our get_buffer wrapper so we can calculate the
- * PTS correctly */
- p_sys->p_context->get_buffer = ffmpeg_GetFrameBuf;
- p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
- p_sys->p_context->opaque = p_dec;
-
- /* ***** init this codec with special data ***** */
- if( p_dec->fmt_in.i_extra )
- {
- int i_size = p_dec->fmt_in.i_extra;
-
- if( p_sys->i_codec_id == CODEC_ID_SVQ3 )
- {
- uint8_t *p;
-
- p_sys->p_context->extradata_size = i_size + 12;
- p = p_sys->p_context->extradata =
- malloc( p_sys->p_context->extradata_size );
-
- memcpy( &p[0], "SVQ3", 4 );
- memset( &p[4], 0, 8 );
- memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
-
- /* Now remove all atoms before the SMI one */
- if( p_sys->p_context->extradata_size > 0x5a &&
- strncmp( &p[0x56], "SMI ", 4 ) )
- {
- uint8_t *psz = &p[0x52];
-
- while( psz < &p[p_sys->p_context->extradata_size - 8] )
- {
- int i_size = GetDWBE( psz );
- if( i_size <= 1 )
- {
- /* FIXME handle 1 as long size */
- break;
- }
- if( !strncmp( &psz[4], "SMI ", 4 ) )
- {
- memmove( &p[0x52], psz,
- &p[p_sys->p_context->extradata_size] - psz );
- break;
- }
-
- psz += i_size;
- }
- }
- }
- else if( p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '0' ) ||
- p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '3' ) ||
- p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '2', '0' ) )
- {
- if( p_dec->fmt_in.i_extra == 8 )
- {
- p_sys->p_context->extradata_size = 8;
- p_sys->p_context->extradata = malloc( 8 );
-
- memcpy( p_sys->p_context->extradata,
- p_dec->fmt_in.p_extra,
- p_dec->fmt_in.i_extra );
- p_sys->p_context->sub_id= ((uint32_t*)p_dec->fmt_in.p_extra)[1];
-
- msg_Warn( p_dec, "using extra data for RV codec sub_id=%08x",
- p_sys->p_context->sub_id );
- }
- }
- /* FIXME: remove when ffmpeg deals properly with avc1 */
- else if( p_dec->fmt_in.i_codec == VLC_FOURCC('a','v','c','1') )
- {
- ;
- }
- /* End FIXME */
- else
- {
- p_sys->p_context->extradata_size = i_size;
- p_sys->p_context->extradata =
- malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
- memcpy( p_sys->p_context->extradata,
- p_dec->fmt_in.p_extra, i_size );
- memset( &((uint8_t*)p_sys->p_context->extradata)[i_size],
- 0, FF_INPUT_BUFFER_PADDING_SIZE );
- }
- }
-
- /* ***** misc init ***** */
- p_sys->input_pts = p_sys->input_dts = 0;
- p_sys->i_pts = 0;
- p_sys->b_has_b_frames = VLC_FALSE;
- p_sys->b_first_frame = VLC_TRUE;
- p_sys->i_late_frames = 0;
- p_sys->i_buffer = 0;
- p_sys->i_buffer_orig = 1;
- p_sys->p_buffer_orig = p_sys->p_buffer = malloc( p_sys->i_buffer_orig );
-
- /* Set output properties */
- p_dec->fmt_out.i_cat = VIDEO_ES;
- p_dec->fmt_out.i_codec = ffmpeg_PixFmtToChroma( p_context->pix_fmt );
-
- /* Setup palette */
-#if LIBAVCODEC_BUILD >= 4688
- if( p_dec->fmt_in.video.p_palette )
- p_sys->p_context->palctrl =
- (AVPaletteControl *)p_dec->fmt_in.video.p_palette;
- else
- p_sys->p_context->palctrl = &palette_control;
-#endif
-
- /* ***** Open the codec ***** */
- vlc_mutex_lock( lockval.p_address );
- if( avcodec_open( p_sys->p_context, p_sys->p_codec ) < 0 )
- {
- vlc_mutex_unlock( lockval.p_address );
- msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec );
- free( p_sys );
- return VLC_EGENERIC;
- }
- vlc_mutex_unlock( lockval.p_address );
- msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec );
-
-
- return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * DecodeVideo: Called to decode one or more frames
- *****************************************************************************/
-picture_t *E_(DecodeVideo)( decoder_t *p_dec, block_t **pp_block )
-{
- decoder_sys_t *p_sys = p_dec->p_sys;
- int b_drawpicture;
- int b_null_size = VLC_FALSE;
- block_t *p_block;
-
- if( !pp_block || !*pp_block ) return NULL;
-
- p_block = *pp_block;
-
- if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
- {
- p_sys->i_buffer = 0;
- p_sys->i_pts = 0; /* To make sure we recover properly */
-
- p_sys->input_pts = p_sys->input_dts = 0;
- p_sys->i_late_frames = 0;
-
- block_Release( p_block );
- return NULL;
- }
-
- if( p_block->i_flags & BLOCK_FLAG_PREROLL )
- {
- /* Do not care about late frames when prerolling
- * TODO avoid decoding of non reference frame
- * (ie all B except for H264 where it depends only on nal_ref_idc) */
- p_sys->i_late_frames = 0;
- }
-
- if( !p_dec->b_pace_control && p_sys->i_late_frames > 0 &&
- mdate() - p_sys->i_late_frames_start > I64C(5000000) )
- {
- if( p_sys->i_pts )
- {
- msg_Err( p_dec, "more than 5 seconds of late video -> "
- "dropping frame (computer too slow ?)" );
- p_sys->i_pts = 0; /* To make sure we recover properly */
- }
- block_Release( p_block );
- p_sys->i_late_frames--;
- return NULL;
- }
-
- if( p_block->i_pts > 0 || p_block->i_dts > 0 )
- {
- p_sys->input_pts = p_block->i_pts;
- p_sys->input_dts = p_block->i_dts;
-
- /* Make sure we don't reuse the same timestamps twice */
- p_block->i_pts = p_block->i_dts = 0;
- }
-
- /* TODO implement it in a better way */
- /* A good idea could be to decode all I pictures and see for the other */
- if( !p_dec->b_pace_control &&
- p_sys->b_hurry_up && p_sys->i_late_frames > 4 )
- {
- b_drawpicture = 0;
- if( p_sys->i_late_frames < 8 )
- {
- p_sys->p_context->hurry_up = 2;
- }
- else
- {
- /* picture too late, won't decode
- * but break picture until a new I, and for mpeg4 ...*/
-
- p_sys->i_late_frames--; /* needed else it will never be decrease */
- block_Release( p_block );
- p_sys->i_buffer = 0;
- return NULL;
- }
- }
- else
- {
- b_drawpicture = 1;
- p_sys->p_context->hurry_up = 0;
- }
-
-
- if( p_sys->p_context->width <= 0 || p_sys->p_context->height <= 0 )
- {
- p_sys->p_context->hurry_up = 5;
- b_null_size = VLC_TRUE;
- }
-
- /*
- * Do the actual decoding now
- */
-
- /* Check if post-processing was enabled */
- p_sys->b_pp = p_sys->b_pp_async;
-
- /* Don't forget that ffmpeg requires a little more bytes
- * that the real frame size */
- if( p_block->i_buffer > 0 )
- {
- p_sys->i_buffer = p_block->i_buffer;
- if( p_sys->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE >
- p_sys->i_buffer_orig )
- {
- free( p_sys->p_buffer_orig );
- p_sys->i_buffer_orig =
- p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE;
- p_sys->p_buffer_orig = malloc( p_sys->i_buffer_orig );
- }
- p_sys->p_buffer = p_sys->p_buffer_orig;
- p_sys->i_buffer = p_block->i_buffer;
- p_dec->p_vlc->pf_memcpy( p_sys->p_buffer, p_block->p_buffer,
- p_block->i_buffer );
- memset( p_sys->p_buffer + p_block->i_buffer, 0,
- FF_INPUT_BUFFER_PADDING_SIZE );
-
- p_block->i_buffer = 0;
- }
-
- while( p_sys->i_buffer > 0 )
- {
- int i_used, b_gotpicture;
- picture_t *p_pic;
-
- i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic,
- &b_gotpicture,
- p_sys->p_buffer, p_sys->i_buffer );
- if( b_null_size && p_sys->p_context->width > 0 &&
- p_sys->p_context->height > 0 )
- {
- /* Reparse it to not drop the I frame */
- b_null_size = VLC_FALSE;
- p_sys->p_context->hurry_up = 0;
- i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic,
- &b_gotpicture,
- p_sys->p_buffer, p_sys->i_buffer );
- }
-
- if( i_used < 0 )
- {
- msg_Warn( p_dec, "cannot decode one frame (%d bytes)",
- p_sys->i_buffer );
- block_Release( p_block );
- return NULL;
- }
- else if( i_used > p_sys->i_buffer )
- {
- i_used = p_sys->i_buffer;
- }
-
- /* Consumed bytes */
- p_sys->i_buffer -= i_used;
- p_sys->p_buffer += i_used;
-
- /* Nothing to display */
- if( !b_gotpicture )
- {
- if( i_used == 0 ) break;
- continue;
- }
-
- /* Update frame late count (except when doing preroll) */
- if( p_sys->i_pts && p_sys->i_pts <= mdate() &&
- !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
- {
- p_sys->i_late_frames++;
- if( p_sys->i_late_frames == 1 )
- p_sys->i_late_frames_start = mdate();
- }
- else
- {
- p_sys->i_late_frames = 0;
- }
-
- if( !b_drawpicture || !p_sys->p_ff_pic->linesize[0] )
- {
- /* Do not display the picture */
- continue;
- }
-
- if( !p_sys->p_ff_pic->opaque )
- {
- /* Get a new picture */
- p_pic = ffmpeg_NewPictBuf( p_dec, p_sys->p_context );
- if( !p_pic )
- {
- block_Release( p_block );
- return NULL;
- }
-
- /* Fill p_picture_t from AVVideoFrame and do chroma conversion
- * if needed */
- ffmpeg_CopyPicture( p_dec, p_pic, p_sys->p_ff_pic );
- }
- else
- {
- p_pic = (picture_t *)p_sys->p_ff_pic->opaque;
- }
-
- /* Set the PTS */
- if( p_sys->p_ff_pic->pts ) p_sys->i_pts = p_sys->p_ff_pic->pts;
-
- /* Sanity check (seems to be needed for some streams ) */
- if( p_sys->p_ff_pic->pict_type == FF_B_TYPE )
- {
- p_sys->b_has_b_frames = VLC_TRUE;
- }
-
- /* Send decoded frame to vout */
- if( p_sys->i_pts )
- {
- p_pic->date = p_sys->i_pts;
-
- /* interpolate the next PTS */
- if( p_sys->p_context->frame_rate > 0 )
- {
- p_sys->i_pts += I64C(1000000) *
- (2 + p_sys->p_ff_pic->repeat_pict) *
- p_sys->p_context->frame_rate_base /
- (2 * p_sys->p_context->frame_rate);
- }
-
- if( p_sys->b_first_frame )
- {
- /* Hack to force display of still pictures */
- p_sys->b_first_frame = VLC_FALSE;
- p_pic->b_force = VLC_TRUE;
- }
-
- p_pic->i_nb_fields = 2 + p_sys->p_ff_pic->repeat_pict;
-#if LIBAVCODEC_BUILD >= 4685
- p_pic->b_progressive = !p_sys->p_ff_pic->interlaced_frame;
- p_pic->b_top_field_first = p_sys->p_ff_pic->top_field_first;
-#endif
-
- return p_pic;
- }
- else
- {
- p_dec->pf_vout_buffer_del( p_dec, p_pic );
- }
- }
-
- block_Release( p_block );
- return NULL;
-}
-
-/*****************************************************************************
- * EndVideo: decoder destruction
- *****************************************************************************
- * This function is called when the thread ends after a sucessful
- * initialization.
- *****************************************************************************/
-void E_(EndVideoDec)( decoder_t *p_dec )
-{
- decoder_sys_t *p_sys = p_dec->p_sys;
-
- if( p_sys->p_ff_pic ) av_free( p_sys->p_ff_pic );
-
-#ifdef LIBAVCODEC_PP
- E_(ClosePostproc)( p_dec, p_sys->p_pp );
-#endif
-
- free( p_sys->p_buffer_orig );
-}
-
-/*****************************************************************************
- * ffmpeg_CopyPicture: copy a picture from ffmpeg internal buffers to a
- * picture_t structure (when not in direct rendering mode).
- *****************************************************************************/
-static void ffmpeg_CopyPicture( decoder_t *p_dec,
- picture_t *p_pic, AVFrame *p_ff_pic )
-{
- decoder_sys_t *p_sys = p_dec->p_sys;
-
- if( ffmpeg_PixFmtToChroma( p_sys->p_context->pix_fmt ) )
- {
- int i_plane, i_size, i_line;
- uint8_t *p_dst, *p_src;
- int i_src_stride, i_dst_stride;
-
-#ifdef LIBAVCODEC_PP
- if( p_sys->p_pp && p_sys->b_pp )
- E_(PostprocPict)( p_dec, p_sys->p_pp, p_pic, p_ff_pic );
- else
-#endif
- for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
- {
- p_src = p_ff_pic->data[i_plane];
- p_dst = p_pic->p[i_plane].p_pixels;
- i_src_stride = p_ff_pic->linesize[i_plane];
- i_dst_stride = p_pic->p[i_plane].i_pitch;
-
- i_size = __MIN( i_src_stride, i_dst_stride );
- for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines;
- i_line++ )
- {
- p_dec->p_vlc->pf_memcpy( p_dst, p_src, i_size );
- p_src += i_src_stride;
- p_dst += i_dst_stride;
- }
- }
- }
- else
- {
- AVPicture dest_pic;
- int i;
-
- /* we need to convert to I420 */
- switch( p_sys->p_context->pix_fmt )
- {
- case PIX_FMT_YUV410P:
- case PIX_FMT_YUV411P:
- case PIX_FMT_PAL8:
- for( i = 0; i < p_pic->i_planes; i++ )
- {
- dest_pic.data[i] = p_pic->p[i].p_pixels;
- dest_pic.linesize[i] = p_pic->p[i].i_pitch;
- }
- img_convert( &dest_pic, PIX_FMT_YUV420P,
- (AVPicture *)p_ff_pic,
- p_sys->p_context->pix_fmt,
- p_sys->p_context->width,
- p_sys->p_context->height );
- break;
- default:
- msg_Err( p_dec, "don't know how to convert chroma %i",
- p_sys->p_context->pix_fmt );
- p_dec->b_error = 1;
- break;
- }
- }
-}
-
-/*****************************************************************************
- * ffmpeg_GetFrameBuf: callback used by ffmpeg to get a frame buffer.
- *****************************************************************************
- * It is used for direct rendering as well as to get the right PTS for each
- * decoded picture (even in indirect rendering mode).
- *****************************************************************************/
-static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
- AVFrame *p_ff_pic )
-{
- decoder_t *p_dec = (decoder_t *)p_context->opaque;
- decoder_sys_t *p_sys = p_dec->p_sys;
- picture_t *p_pic;
-
- /* Set picture PTS */
- if( p_sys->input_pts )
- {
- p_ff_pic->pts = p_sys->input_pts;
- }
- else if( p_sys->input_dts )
- {
- /* Some demuxers only set the dts so let's try to find a useful
- * timestamp from this */
- if( !p_context->has_b_frames || !p_sys->b_has_b_frames ||
- !p_ff_pic->reference || !p_sys->i_pts )
- {
- p_ff_pic->pts = p_sys->input_dts;
- }
- else p_ff_pic->pts = 0;
- }
- else p_ff_pic->pts = 0;
-
- if( p_sys->i_pts ) /* make sure 1st frame has a pts > 0 */
- {
- p_sys->input_pts = p_sys->input_dts = 0;
- }
-
- p_ff_pic->opaque = 0;
-
- /* Not much to do in indirect rendering mode */
- if( !p_sys->b_direct_rendering || p_sys->b_pp )
- {
- return avcodec_default_get_buffer( p_context, p_ff_pic );
- }
-
- /* Some codecs set pix_fmt only after the 1st frame has been decoded,
- * so this check is necessary. */
- if( !ffmpeg_PixFmtToChroma( p_context->pix_fmt ) ||
- p_sys->p_context->width % 16 || p_sys->p_context->height % 16 )
- {
- msg_Dbg( p_dec, "disabling direct rendering" );
- p_sys->b_direct_rendering = 0;
- return avcodec_default_get_buffer( p_context, p_ff_pic );
- }
-
- /* Get a new picture */
- //p_sys->p_vout->render.b_allow_modify_pics = 0;
- p_pic = ffmpeg_NewPictBuf( p_dec, p_sys->p_context );
- if( !p_pic )
- {
- p_sys->b_direct_rendering = 0;
- return avcodec_default_get_buffer( p_context, p_ff_pic );
- }
- p_sys->p_context->draw_horiz_band = NULL;
-
- p_ff_pic->opaque = (void*)p_pic;
- p_ff_pic->type = FF_BUFFER_TYPE_USER;
- p_ff_pic->data[0] = p_pic->p[0].p_pixels;
- p_ff_pic->data[1] = p_pic->p[1].p_pixels;
- p_ff_pic->data[2] = p_pic->p[2].p_pixels;
- p_ff_pic->data[3] = NULL; /* alpha channel but I'm not sure */
-
- p_ff_pic->linesize[0] = p_pic->p[0].i_pitch;
- p_ff_pic->linesize[1] = p_pic->p[1].i_pitch;
- p_ff_pic->linesize[2] = p_pic->p[2].i_pitch;
- p_ff_pic->linesize[3] = 0;
-
- if( p_ff_pic->reference != 0 )
- {
- p_dec->pf_picture_link( p_dec, p_pic );
- }
-
- /* FIXME what is that, should give good value */
- p_ff_pic->age = 256*256*256*64; // FIXME FIXME from ffmpeg
-
- return 0;
-}
-
-static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,
- AVFrame *p_ff_pic )
-{
- decoder_t *p_dec = (decoder_t *)p_context->opaque;
- picture_t *p_pic;
-
- if( !p_ff_pic->opaque )
- {
- avcodec_default_release_buffer( p_context, p_ff_pic );
- return;
- }
-
- p_pic = (picture_t*)p_ff_pic->opaque;
-
- p_ff_pic->data[0] = NULL;
- p_ff_pic->data[1] = NULL;
- p_ff_pic->data[2] = NULL;
- p_ff_pic->data[3] = NULL;
-
- if( p_ff_pic->reference != 0 )
- {
- p_dec->pf_picture_unlink( p_dec, p_pic );
- }
-}
+/*****************************************************************************\r
+ * video.c: video decoder using the ffmpeg library\r
+ *****************************************************************************\r
+ * Copyright (C) 1999-2001 VideoLAN\r
+ * $Id$\r
+ *\r
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>\r
+ * Gildas Bazin <gbazin@videolan.org>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <vlc/vlc.h>\r
+#include <vlc/decoder.h>\r
+\r
+/* ffmpeg header */\r
+#ifdef HAVE_FFMPEG_AVCODEC_H\r
+# include <ffmpeg/avcodec.h>\r
+#else\r
+# include <avcodec.h>\r
+#endif\r
+\r
+#include "ffmpeg.h"\r
+\r
+/*****************************************************************************\r
+ * decoder_sys_t : decoder descriptor\r
+ *****************************************************************************/\r
+struct decoder_sys_t\r
+{\r
+ /* Common part between video and audio decoder */\r
+ int i_cat;\r
+ int i_codec_id;\r
+ char *psz_namecodec;\r
+\r
+ AVCodecContext *p_context;\r
+ AVCodec *p_codec;\r
+\r
+ /* Video decoder specific part */\r
+ mtime_t input_pts;\r
+ mtime_t input_dts;\r
+ mtime_t i_pts;\r
+\r
+ AVFrame *p_ff_pic;\r
+ BITMAPINFOHEADER *p_format;\r
+\r
+ /* for frame skipping algo */\r
+ int b_hurry_up;\r
+ int i_frame_skip;\r
+\r
+ /* how many decoded frames are late */\r
+ int i_late_frames;\r
+ mtime_t i_late_frames_start;\r
+\r
+ /* for direct rendering */\r
+ int b_direct_rendering;\r
+\r
+ vlc_bool_t b_has_b_frames;\r
+\r
+ /* Hack to force display of still pictures */\r
+ vlc_bool_t b_first_frame;\r
+\r
+ int i_buffer_orig, i_buffer;\r
+ char *p_buffer_orig, *p_buffer;\r
+\r
+ /* Postprocessing handle */\r
+ void *p_pp;\r
+ vlc_bool_t b_pp;\r
+ vlc_bool_t b_pp_async;\r
+ vlc_bool_t b_pp_init;\r
+};\r
+\r
+/* FIXME (dummy palette for now) */\r
+static AVPaletteControl palette_control;\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+static void ffmpeg_CopyPicture ( decoder_t *, picture_t *, AVFrame * );\r
+static int ffmpeg_GetFrameBuf ( struct AVCodecContext *, AVFrame * );\r
+static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * );\r
+\r
+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )\r
+{\r
+ uint8_t *p = (uint8_t*)&fcc;\r
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);\r
+}\r
+\r
+/*****************************************************************************\r
+ * Local Functions\r
+ *****************************************************************************/\r
+static uint32_t ffmpeg_PixFmtToChroma( int i_ff_chroma )\r
+{\r
+ switch( i_ff_chroma )\r
+ {\r
+ case PIX_FMT_YUV420P:\r
+ return VLC_FOURCC('I','4','2','0');\r
+ case PIX_FMT_YUV422P:\r
+ return VLC_FOURCC('I','4','2','2');\r
+ case PIX_FMT_YUV444P:\r
+ return VLC_FOURCC('I','4','4','4');\r
+\r
+ case PIX_FMT_YUV422:\r
+ return VLC_FOURCC('Y','U','Y','2');\r
+\r
+ case PIX_FMT_RGB555:\r
+ return VLC_FOURCC('R','V','1','5');\r
+ case PIX_FMT_RGB565:\r
+ return VLC_FOURCC('R','V','1','6');\r
+ case PIX_FMT_RGB24:\r
+ return VLC_FOURCC('R','V','2','4');\r
+ case PIX_FMT_RGBA32:\r
+ return VLC_FOURCC('R','V','3','2');\r
+ case PIX_FMT_GRAY8:\r
+ return VLC_FOURCC('G','R','E','Y');\r
+\r
+ case PIX_FMT_YUV410P:\r
+ case PIX_FMT_YUV411P:\r
+ case PIX_FMT_BGR24:\r
+ default:\r
+ return 0;\r
+ }\r
+}\r
+\r
+/* Returns a new picture buffer */\r
+static inline picture_t *ffmpeg_NewPictBuf( decoder_t *p_dec,\r
+ AVCodecContext *p_context )\r
+{\r
+ decoder_sys_t *p_sys = p_dec->p_sys;\r
+ picture_t *p_pic;\r
+\r
+ p_dec->fmt_out.video.i_width = p_context->width;\r
+ p_dec->fmt_out.video.i_height = p_context->height;\r
+ p_dec->fmt_out.i_codec = ffmpeg_PixFmtToChroma( p_context->pix_fmt );\r
+\r
+ if( !p_context->width || !p_context->height )\r
+ {\r
+ return NULL; /* invalid display size */\r
+ }\r
+\r
+ if( !p_dec->fmt_out.i_codec )\r
+ {\r
+ /* we make conversion if possible*/\r
+ p_dec->fmt_out.i_codec = VLC_FOURCC('I','4','2','0');\r
+ }\r
+\r
+ /* If an aspect-ratio was specified in the input format then force it */\r
+ if( p_dec->fmt_in.video.i_aspect )\r
+ {\r
+ p_dec->fmt_out.video.i_aspect = p_dec->fmt_in.video.i_aspect;\r
+ }\r
+ else\r
+ {\r
+#if LIBAVCODEC_BUILD >= 4687\r
+ p_dec->fmt_out.video.i_aspect =\r
+ VOUT_ASPECT_FACTOR * ( av_q2d(p_context->sample_aspect_ratio) *\r
+ p_context->width / p_context->height );\r
+#else\r
+ p_dec->fmt_out.video.i_aspect =\r
+ VOUT_ASPECT_FACTOR * p_context->aspect_ratio;\r
+#endif\r
+ if( p_dec->fmt_out.video.i_aspect == 0 )\r
+ {\r
+ p_dec->fmt_out.video.i_aspect =\r
+ VOUT_ASPECT_FACTOR * p_context->width / p_context->height;\r
+ }\r
+ }\r
+\r
+ if( p_context->frame_rate > 0 && p_context->frame_rate_base > 0 )\r
+ {\r
+ p_dec->fmt_out.video.i_frame_rate = p_context->frame_rate;\r
+ p_dec->fmt_out.video.i_frame_rate_base = p_context->frame_rate_base;\r
+ }\r
+\r
+ p_pic = p_dec->pf_vout_buffer_new( p_dec );\r
+\r
+#ifdef LIBAVCODEC_PP\r
+ if( p_sys->p_pp && p_sys->b_pp && !p_sys->b_pp_init )\r
+ {\r
+ E_(InitPostproc)( p_dec, p_sys->p_pp, p_context->width,\r
+ p_context->height, p_context->pix_fmt );\r
+ p_sys->b_pp_init = VLC_TRUE;\r
+ }\r
+#endif\r
+\r
+ return p_pic;\r
+}\r
+\r
+/*****************************************************************************\r
+ * InitVideo: initialize the video decoder\r
+ *****************************************************************************\r
+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet\r
+ * opened (done after the first decoded frame).\r
+ *****************************************************************************/\r
+int E_(InitVideoDec)( decoder_t *p_dec, AVCodecContext *p_context,\r
+ AVCodec *p_codec, int i_codec_id, char *psz_namecodec )\r
+{\r
+ decoder_sys_t *p_sys;\r
+ vlc_value_t lockval;\r
+ vlc_value_t val;\r
+\r
+ var_Get( p_dec->p_libvlc, "avcodec", &lockval );\r
+\r
+ /* Allocate the memory needed to store the decoder's structure */\r
+ if( ( p_dec->p_sys = p_sys =\r
+ (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )\r
+ {\r
+ msg_Err( p_dec, "out of memory" );\r
+ return VLC_EGENERIC;\r
+ }\r
+\r
+ p_dec->p_sys->p_context = p_context;\r
+ p_dec->p_sys->p_codec = p_codec;\r
+ p_dec->p_sys->i_codec_id = i_codec_id;\r
+ p_dec->p_sys->psz_namecodec = psz_namecodec;\r
+ p_sys->p_ff_pic = avcodec_alloc_frame();\r
+\r
+ /* ***** Fill p_context with init values ***** */\r
+ /* FIXME: remove when ffmpeg deals properly with avc1 */\r
+ if( p_dec->fmt_in.i_codec != VLC_FOURCC('a','v','c','1') )\r
+ /* End FIXME */\r
+ p_sys->p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_codec );\r
+ p_sys->p_context->width = p_dec->fmt_in.video.i_width;\r
+ p_sys->p_context->height = p_dec->fmt_in.video.i_height;\r
+ p_sys->p_context->bits_per_sample = p_dec->fmt_in.video.i_bits_per_pixel;\r
+\r
+ /* ***** Get configuration of ffmpeg plugin ***** */\r
+ p_sys->p_context->workaround_bugs =\r
+ config_GetInt( p_dec, "ffmpeg-workaround-bugs" );\r
+ p_sys->p_context->error_resilience =\r
+ config_GetInt( p_dec, "ffmpeg-error-resilience" );\r
+\r
+ var_Create( p_dec, "grayscale", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );\r
+ var_Get( p_dec, "grayscale", &val );\r
+ if( val.b_bool ) p_sys->p_context->flags |= CODEC_FLAG_GRAY;\r
+\r
+ var_Create( p_dec, "ffmpeg-vismv", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );\r
+ var_Get( p_dec, "ffmpeg-vismv", &val );\r
+#if LIBAVCODEC_BUILD >= 4698\r
+ if( val.i_int ) p_sys->p_context->debug_mv = val.i_int;\r
+#endif\r
+\r
+ var_Create( p_dec, "ffmpeg-lowres", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );\r
+ var_Get( p_dec, "ffmpeg-lowres", &val );\r
+#if LIBAVCODEC_BUILD >= 4723\r
+ if( val.i_int > 0 && val.i_int <= 2 ) p_sys->p_context->lowres = val.i_int;\r
+#endif\r
+\r
+ /* ***** ffmpeg frame skipping ***** */\r
+ var_Create( p_dec, "ffmpeg-hurry-up", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );\r
+ var_Get( p_dec, "ffmpeg-hurry-up", &val );\r
+ p_sys->b_hurry_up = val.b_bool;\r
+\r
+ /* ***** ffmpeg direct rendering ***** */\r
+ p_sys->b_direct_rendering = 0;\r
+ var_Create( p_dec, "ffmpeg-dr", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );\r
+ var_Get( p_dec, "ffmpeg-dr", &val );\r
+ if( val.b_bool && (p_sys->p_codec->capabilities & CODEC_CAP_DR1) &&\r
+ ffmpeg_PixFmtToChroma( p_sys->p_context->pix_fmt ) &&\r
+ /* Apparently direct rendering doesn't work with YUV422P */\r
+ p_sys->p_context->pix_fmt != PIX_FMT_YUV422P &&\r
+ /* H264 uses too many reference frames */\r
+ p_sys->i_codec_id != CODEC_ID_H264 &&\r
+ !(p_sys->p_context->width % 16) && !(p_sys->p_context->height % 16) &&\r
+#if LIBAVCODEC_BUILD >= 4698\r
+ !p_sys->p_context->debug_mv )\r
+#else\r
+ 1 )\r
+#endif\r
+ {\r
+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,\r
+ * so we need to do another check in ffmpeg_GetFrameBuf() */\r
+ p_sys->b_direct_rendering = 1;\r
+ }\r
+\r
+#ifdef LIBAVCODEC_PP\r
+ p_sys->p_pp = NULL;\r
+ p_sys->b_pp = p_sys->b_pp_async = p_sys->b_pp_init = VLC_FALSE;\r
+ p_sys->p_pp = E_(OpenPostproc)( p_dec, &p_sys->b_pp_async );\r
+#endif\r
+\r
+ /* ffmpeg doesn't properly release old pictures when frames are skipped */\r
+ //if( p_sys->b_hurry_up ) p_sys->b_direct_rendering = 0;\r
+ if( p_sys->b_direct_rendering )\r
+ {\r
+ msg_Dbg( p_dec, "using direct rendering" );\r
+ p_sys->p_context->flags |= CODEC_FLAG_EMU_EDGE;\r
+ }\r
+\r
+ /* Always use our get_buffer wrapper so we can calculate the\r
+ * PTS correctly */\r
+ p_sys->p_context->get_buffer = ffmpeg_GetFrameBuf;\r
+ p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;\r
+ p_sys->p_context->opaque = p_dec;\r
+\r
+ /* ***** init this codec with special data ***** */\r
+ if( p_dec->fmt_in.i_extra )\r
+ {\r
+ int i_size = p_dec->fmt_in.i_extra;\r
+\r
+ if( p_sys->i_codec_id == CODEC_ID_SVQ3 )\r
+ {\r
+ uint8_t *p;\r
+\r
+ p_sys->p_context->extradata_size = i_size + 12;\r
+ p = p_sys->p_context->extradata =\r
+ malloc( p_sys->p_context->extradata_size );\r
+\r
+ memcpy( &p[0], "SVQ3", 4 );\r
+ memset( &p[4], 0, 8 );\r
+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );\r
+\r
+ /* Now remove all atoms before the SMI one */\r
+ if( p_sys->p_context->extradata_size > 0x5a &&\r
+ strncmp( &p[0x56], "SMI ", 4 ) )\r
+ {\r
+ uint8_t *psz = &p[0x52];\r
+\r
+ while( psz < &p[p_sys->p_context->extradata_size - 8] )\r
+ {\r
+ int i_size = GetDWBE( psz );\r
+ if( i_size <= 1 )\r
+ {\r
+ /* FIXME handle 1 as long size */\r
+ break;\r
+ }\r
+ if( !strncmp( &psz[4], "SMI ", 4 ) )\r
+ {\r
+ memmove( &p[0x52], psz,\r
+ &p[p_sys->p_context->extradata_size] - psz );\r
+ break;\r
+ }\r
+\r
+ psz += i_size;\r
+ }\r
+ }\r
+ }\r
+ else if( p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '0' ) ||\r
+ p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '1', '3' ) ||\r
+ p_dec->fmt_in.i_codec == VLC_FOURCC( 'R', 'V', '2', '0' ) )\r
+ {\r
+ if( p_dec->fmt_in.i_extra == 8 )\r
+ {\r
+ p_sys->p_context->extradata_size = 8;\r
+ p_sys->p_context->extradata = malloc( 8 );\r
+\r
+ memcpy( p_sys->p_context->extradata,\r
+ p_dec->fmt_in.p_extra,\r
+ p_dec->fmt_in.i_extra );\r
+ p_sys->p_context->sub_id= ((uint32_t*)p_dec->fmt_in.p_extra)[1];\r
+\r
+ msg_Warn( p_dec, "using extra data for RV codec sub_id=%08x",\r
+ p_sys->p_context->sub_id );\r
+ }\r
+ }\r
+ /* FIXME: remove when ffmpeg deals properly with avc1 */\r
+ else if( p_dec->fmt_in.i_codec == VLC_FOURCC('a','v','c','1') )\r
+ {\r
+ ;\r
+ }\r
+ /* End FIXME */\r
+ else\r
+ {\r
+ p_sys->p_context->extradata_size = i_size;\r
+ p_sys->p_context->extradata =\r
+ malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );\r
+ memcpy( p_sys->p_context->extradata,\r
+ p_dec->fmt_in.p_extra, i_size );\r
+ memset( &((uint8_t*)p_sys->p_context->extradata)[i_size],\r
+ 0, FF_INPUT_BUFFER_PADDING_SIZE );\r
+ }\r
+ }\r
+\r
+ /* ***** misc init ***** */\r
+ p_sys->input_pts = p_sys->input_dts = 0;\r
+ p_sys->i_pts = 0;\r
+ p_sys->b_has_b_frames = VLC_FALSE;\r
+ p_sys->b_first_frame = VLC_TRUE;\r
+ p_sys->i_late_frames = 0;\r
+ p_sys->i_buffer = 0;\r
+ p_sys->i_buffer_orig = 1;\r
+ p_sys->p_buffer_orig = p_sys->p_buffer = malloc( p_sys->i_buffer_orig );\r
+\r
+ /* Set output properties */\r
+ p_dec->fmt_out.i_cat = VIDEO_ES;\r
+ p_dec->fmt_out.i_codec = ffmpeg_PixFmtToChroma( p_context->pix_fmt );\r
+\r
+ /* Setup palette */\r
+#if LIBAVCODEC_BUILD >= 4688\r
+ if( p_dec->fmt_in.video.p_palette )\r
+ p_sys->p_context->palctrl =\r
+ (AVPaletteControl *)p_dec->fmt_in.video.p_palette;\r
+ else\r
+ p_sys->p_context->palctrl = &palette_control;\r
+#endif\r
+\r
+ /* ***** Open the codec ***** */\r
+ vlc_mutex_lock( lockval.p_address );\r
+ if( avcodec_open( p_sys->p_context, p_sys->p_codec ) < 0 )\r
+ {\r
+ vlc_mutex_unlock( lockval.p_address );\r
+ msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec );\r
+ free( p_sys );\r
+ return VLC_EGENERIC;\r
+ }\r
+ vlc_mutex_unlock( lockval.p_address );\r
+ msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec );\r
+\r
+\r
+ return VLC_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************\r
+ * DecodeVideo: Called to decode one or more frames\r
+ *****************************************************************************/\r
+picture_t *E_(DecodeVideo)( decoder_t *p_dec, block_t **pp_block )\r
+{\r
+ decoder_sys_t *p_sys = p_dec->p_sys;\r
+ int b_drawpicture;\r
+ int b_null_size = VLC_FALSE;\r
+ block_t *p_block;\r
+\r
+ if( !pp_block || !*pp_block ) return NULL;\r
+\r
+ p_block = *pp_block;\r
+\r
+ if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )\r
+ {\r
+ p_sys->i_buffer = 0;\r
+ p_sys->i_pts = 0; /* To make sure we recover properly */\r
+\r
+ p_sys->input_pts = p_sys->input_dts = 0;\r
+ p_sys->i_late_frames = 0;\r
+\r
+ block_Release( p_block );\r
+ return NULL;\r
+ }\r
+\r
+ if( p_block->i_flags & BLOCK_FLAG_PREROLL )\r
+ {\r
+ /* Do not care about late frames when prerolling\r
+ * TODO avoid decoding of non reference frame\r
+ * (ie all B except for H264 where it depends only on nal_ref_idc) */\r
+ p_sys->i_late_frames = 0;\r
+ }\r
+\r
+ if( !p_dec->b_pace_control && p_sys->i_late_frames > 0 &&\r
+ mdate() - p_sys->i_late_frames_start > I64C(5000000) )\r
+ {\r
+ if( p_sys->i_pts )\r
+ {\r
+ msg_Err( p_dec, "more than 5 seconds of late video -> "\r
+ "dropping frame (computer too slow ?)" );\r
+ p_sys->i_pts = 0; /* To make sure we recover properly */\r
+ }\r
+ block_Release( p_block );\r
+ p_sys->i_late_frames--;\r
+ return NULL;\r
+ }\r
+\r
+ if( p_block->i_pts > 0 || p_block->i_dts > 0 )\r
+ {\r
+ p_sys->input_pts = p_block->i_pts;\r
+ p_sys->input_dts = p_block->i_dts;\r
+\r
+ /* Make sure we don't reuse the same timestamps twice */\r
+ p_block->i_pts = p_block->i_dts = 0;\r
+ }\r
+\r
+ /* TODO implement it in a better way */\r
+ /* A good idea could be to decode all I pictures and see for the other */\r
+ if( !p_dec->b_pace_control &&\r
+ p_sys->b_hurry_up && p_sys->i_late_frames > 4 )\r
+ {\r
+ b_drawpicture = 0;\r
+ if( p_sys->i_late_frames < 8 )\r
+ {\r
+ p_sys->p_context->hurry_up = 2;\r
+ }\r
+ else\r
+ {\r
+ /* picture too late, won't decode\r
+ * but break picture until a new I, and for mpeg4 ...*/\r
+\r
+ p_sys->i_late_frames--; /* needed else it will never be decrease */\r
+ block_Release( p_block );\r
+ p_sys->i_buffer = 0;\r
+ return NULL;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (!(p_block->i_flags & BLOCK_FLAG_PREROLL))\r
+ {\r
+ b_drawpicture = 1;\r
+ p_sys->p_context->hurry_up = 0;\r
+ }\r
+ else\r
+ {\r
+ b_drawpicture = 0;\r
+ p_sys->p_context->hurry_up = 1;\r
+ }\r
+ }\r
+\r
+\r
+ if( p_sys->p_context->width <= 0 || p_sys->p_context->height <= 0 )\r
+ {\r
+ p_sys->p_context->hurry_up = 5;\r
+ b_null_size = VLC_TRUE;\r
+ }\r
+\r
+ /*\r
+ * Do the actual decoding now\r
+ */\r
+\r
+ /* Check if post-processing was enabled */\r
+ p_sys->b_pp = p_sys->b_pp_async;\r
+\r
+ /* Don't forget that ffmpeg requires a little more bytes\r
+ * that the real frame size */\r
+ if( p_block->i_buffer > 0 )\r
+ {\r
+ p_sys->i_buffer = p_block->i_buffer;\r
+ if( p_sys->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE >\r
+ p_sys->i_buffer_orig )\r
+ {\r
+ free( p_sys->p_buffer_orig );\r
+ p_sys->i_buffer_orig =\r
+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE;\r
+ p_sys->p_buffer_orig = malloc( p_sys->i_buffer_orig );\r
+ }\r
+ p_sys->p_buffer = p_sys->p_buffer_orig;\r
+ p_sys->i_buffer = p_block->i_buffer;\r
+ p_dec->p_vlc->pf_memcpy( p_sys->p_buffer, p_block->p_buffer,\r
+ p_block->i_buffer );\r
+ memset( p_sys->p_buffer + p_block->i_buffer, 0,\r
+ FF_INPUT_BUFFER_PADDING_SIZE );\r
+\r
+ p_block->i_buffer = 0;\r
+ }\r
+\r
+ while( p_sys->i_buffer > 0 )\r
+ {\r
+ int i_used, b_gotpicture;\r
+ picture_t *p_pic;\r
+\r
+ i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic,\r
+ &b_gotpicture,\r
+ p_sys->p_buffer, p_sys->i_buffer );\r
+ if( b_null_size && p_sys->p_context->width > 0 &&\r
+ p_sys->p_context->height > 0 )\r
+ {\r
+ /* Reparse it to not drop the I frame */\r
+ b_null_size = VLC_FALSE;\r
+ p_sys->p_context->hurry_up = 0;\r
+ i_used = avcodec_decode_video( p_sys->p_context, p_sys->p_ff_pic,\r
+ &b_gotpicture,\r
+ p_sys->p_buffer, p_sys->i_buffer );\r
+ }\r
+\r
+ if( i_used < 0 )\r
+ {\r
+ msg_Warn( p_dec, "cannot decode one frame (%d bytes)",\r
+ p_sys->i_buffer );\r
+ block_Release( p_block );\r
+ return NULL;\r
+ }\r
+ else if( i_used > p_sys->i_buffer )\r
+ {\r
+ i_used = p_sys->i_buffer;\r
+ }\r
+\r
+ /* Consumed bytes */\r
+ p_sys->i_buffer -= i_used;\r
+ p_sys->p_buffer += i_used;\r
+\r
+ /* Nothing to display */\r
+ if( !b_gotpicture )\r
+ {\r
+ if( i_used == 0 ) break;\r
+ continue;\r
+ }\r
+\r
+ /* Update frame late count (except when doing preroll) */\r
+ if( p_sys->i_pts && p_sys->i_pts <= mdate() &&\r
+ !(p_block->i_flags & BLOCK_FLAG_PREROLL) )\r
+ {\r
+ p_sys->i_late_frames++;\r
+ if( p_sys->i_late_frames == 1 )\r
+ p_sys->i_late_frames_start = mdate();\r
+ }\r
+ else\r
+ {\r
+ p_sys->i_late_frames = 0;\r
+ }\r
+\r
+ if( !b_drawpicture || !p_sys->p_ff_pic->linesize[0] )\r
+ {\r
+ /* Do not display the picture */\r
+ continue;\r
+ }\r
+\r
+ if( !p_sys->p_ff_pic->opaque )\r
+ {\r
+ /* Get a new picture */\r
+ p_pic = ffmpeg_NewPictBuf( p_dec, p_sys->p_context );\r
+ if( !p_pic )\r
+ {\r
+ block_Release( p_block );\r
+ return NULL;\r
+ }\r
+\r
+ /* Fill p_picture_t from AVVideoFrame and do chroma conversion\r
+ * if needed */\r
+ ffmpeg_CopyPicture( p_dec, p_pic, p_sys->p_ff_pic );\r
+ }\r
+ else\r
+ {\r
+ p_pic = (picture_t *)p_sys->p_ff_pic->opaque;\r
+ }\r
+\r
+ /* Set the PTS */\r
+ if( p_sys->p_ff_pic->pts ) p_sys->i_pts = p_sys->p_ff_pic->pts;\r
+\r
+ /* Sanity check (seems to be needed for some streams ) */\r
+ if( p_sys->p_ff_pic->pict_type == FF_B_TYPE )\r
+ {\r
+ p_sys->b_has_b_frames = VLC_TRUE;\r
+ }\r
+\r
+ /* Send decoded frame to vout */\r
+ if( p_sys->i_pts )\r
+ {\r
+ p_pic->date = p_sys->i_pts;\r
+\r
+ /* interpolate the next PTS */\r
+ if( p_sys->p_context->frame_rate > 0 )\r
+ {\r
+ p_sys->i_pts += I64C(1000000) *\r
+ (2 + p_sys->p_ff_pic->repeat_pict) *\r
+ p_sys->p_context->frame_rate_base /\r
+ (2 * p_sys->p_context->frame_rate);\r
+ }\r
+\r
+ if( p_sys->b_first_frame )\r
+ {\r
+ /* Hack to force display of still pictures */\r
+ p_sys->b_first_frame = VLC_FALSE;\r
+ p_pic->b_force = VLC_TRUE;\r
+ }\r
+\r
+ p_pic->i_nb_fields = 2 + p_sys->p_ff_pic->repeat_pict;\r
+#if LIBAVCODEC_BUILD >= 4685\r
+ p_pic->b_progressive = !p_sys->p_ff_pic->interlaced_frame;\r
+ p_pic->b_top_field_first = p_sys->p_ff_pic->top_field_first;\r
+#endif\r
+\r
+ return p_pic;\r
+ }\r
+ else\r
+ {\r
+ p_dec->pf_vout_buffer_del( p_dec, p_pic );\r
+ }\r
+ }\r
+\r
+ block_Release( p_block );\r
+ return NULL;\r
+}\r
+\r
+/*****************************************************************************\r
+ * EndVideo: decoder destruction\r
+ *****************************************************************************\r
+ * This function is called when the thread ends after a sucessful\r
+ * initialization.\r
+ *****************************************************************************/\r
+void E_(EndVideoDec)( decoder_t *p_dec )\r
+{\r
+ decoder_sys_t *p_sys = p_dec->p_sys;\r
+\r
+ if( p_sys->p_ff_pic ) av_free( p_sys->p_ff_pic );\r
+\r
+#ifdef LIBAVCODEC_PP\r
+ E_(ClosePostproc)( p_dec, p_sys->p_pp );\r
+#endif\r
+\r
+ free( p_sys->p_buffer_orig );\r
+}\r
+\r
+/*****************************************************************************\r
+ * ffmpeg_CopyPicture: copy a picture from ffmpeg internal buffers to a\r
+ * picture_t structure (when not in direct rendering mode).\r
+ *****************************************************************************/\r
+static void ffmpeg_CopyPicture( decoder_t *p_dec,\r
+ picture_t *p_pic, AVFrame *p_ff_pic )\r
+{\r
+ decoder_sys_t *p_sys = p_dec->p_sys;\r
+\r
+ if( ffmpeg_PixFmtToChroma( p_sys->p_context->pix_fmt ) )\r
+ {\r
+ int i_plane, i_size, i_line;\r
+ uint8_t *p_dst, *p_src;\r
+ int i_src_stride, i_dst_stride;\r
+\r
+#ifdef LIBAVCODEC_PP\r
+ if( p_sys->p_pp && p_sys->b_pp )\r
+ E_(PostprocPict)( p_dec, p_sys->p_pp, p_pic, p_ff_pic );\r
+ else\r
+#endif\r
+ for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )\r
+ {\r
+ p_src = p_ff_pic->data[i_plane];\r
+ p_dst = p_pic->p[i_plane].p_pixels;\r
+ i_src_stride = p_ff_pic->linesize[i_plane];\r
+ i_dst_stride = p_pic->p[i_plane].i_pitch;\r
+\r
+ i_size = __MIN( i_src_stride, i_dst_stride );\r
+ for( i_line = 0; i_line < p_pic->p[i_plane].i_visible_lines;\r
+ i_line++ )\r
+ {\r
+ p_dec->p_vlc->pf_memcpy( p_dst, p_src, i_size );\r
+ p_src += i_src_stride;\r
+ p_dst += i_dst_stride;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ AVPicture dest_pic;\r
+ int i;\r
+\r
+ /* we need to convert to I420 */\r
+ switch( p_sys->p_context->pix_fmt )\r
+ {\r
+ case PIX_FMT_YUV410P:\r
+ case PIX_FMT_YUV411P:\r
+ case PIX_FMT_PAL8:\r
+ for( i = 0; i < p_pic->i_planes; i++ )\r
+ {\r
+ dest_pic.data[i] = p_pic->p[i].p_pixels;\r
+ dest_pic.linesize[i] = p_pic->p[i].i_pitch;\r
+ }\r
+ img_convert( &dest_pic, PIX_FMT_YUV420P,\r
+ (AVPicture *)p_ff_pic,\r
+ p_sys->p_context->pix_fmt,\r
+ p_sys->p_context->width,\r
+ p_sys->p_context->height );\r
+ break;\r
+ default:\r
+ msg_Err( p_dec, "don't know how to convert chroma %i",\r
+ p_sys->p_context->pix_fmt );\r
+ p_dec->b_error = 1;\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/*****************************************************************************\r
+ * ffmpeg_GetFrameBuf: callback used by ffmpeg to get a frame buffer.\r
+ *****************************************************************************\r
+ * It is used for direct rendering as well as to get the right PTS for each\r
+ * decoded picture (even in indirect rendering mode).\r
+ *****************************************************************************/\r
+static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,\r
+ AVFrame *p_ff_pic )\r
+{\r
+ decoder_t *p_dec = (decoder_t *)p_context->opaque;\r
+ decoder_sys_t *p_sys = p_dec->p_sys;\r
+ picture_t *p_pic;\r
+\r
+ /* Set picture PTS */\r
+ if( p_sys->input_pts )\r
+ {\r
+ p_ff_pic->pts = p_sys->input_pts;\r
+ }\r
+ else if( p_sys->input_dts )\r
+ {\r
+ /* Some demuxers only set the dts so let's try to find a useful\r
+ * timestamp from this */\r
+ if( !p_context->has_b_frames || !p_sys->b_has_b_frames ||\r
+ !p_ff_pic->reference || !p_sys->i_pts )\r
+ {\r
+ p_ff_pic->pts = p_sys->input_dts;\r
+ }\r
+ else p_ff_pic->pts = 0;\r
+ }\r
+ else p_ff_pic->pts = 0;\r
+\r
+ if( p_sys->i_pts ) /* make sure 1st frame has a pts > 0 */\r
+ {\r
+ p_sys->input_pts = p_sys->input_dts = 0;\r
+ }\r
+\r
+ p_ff_pic->opaque = 0;\r
+\r
+ /* Not much to do in indirect rendering mode */\r
+ if( !p_sys->b_direct_rendering || p_sys->b_pp )\r
+ {\r
+ return avcodec_default_get_buffer( p_context, p_ff_pic );\r
+ }\r
+\r
+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,\r
+ * so this check is necessary. */\r
+ if( !ffmpeg_PixFmtToChroma( p_context->pix_fmt ) ||\r
+ p_sys->p_context->width % 16 || p_sys->p_context->height % 16 )\r
+ {\r
+ msg_Dbg( p_dec, "disabling direct rendering" );\r
+ p_sys->b_direct_rendering = 0;\r
+ return avcodec_default_get_buffer( p_context, p_ff_pic );\r
+ }\r
+\r
+ /* Get a new picture */\r
+ //p_sys->p_vout->render.b_allow_modify_pics = 0;\r
+ p_pic = ffmpeg_NewPictBuf( p_dec, p_sys->p_context );\r
+ if( !p_pic )\r
+ {\r
+ p_sys->b_direct_rendering = 0;\r
+ return avcodec_default_get_buffer( p_context, p_ff_pic );\r
+ }\r
+ p_sys->p_context->draw_horiz_band = NULL;\r
+\r
+ p_ff_pic->opaque = (void*)p_pic;\r
+ p_ff_pic->type = FF_BUFFER_TYPE_USER;\r
+ p_ff_pic->data[0] = p_pic->p[0].p_pixels;\r
+ p_ff_pic->data[1] = p_pic->p[1].p_pixels;\r
+ p_ff_pic->data[2] = p_pic->p[2].p_pixels;\r
+ p_ff_pic->data[3] = NULL; /* alpha channel but I'm not sure */\r
+\r
+ p_ff_pic->linesize[0] = p_pic->p[0].i_pitch;\r
+ p_ff_pic->linesize[1] = p_pic->p[1].i_pitch;\r
+ p_ff_pic->linesize[2] = p_pic->p[2].i_pitch;\r
+ p_ff_pic->linesize[3] = 0;\r
+\r
+ if( p_ff_pic->reference != 0 )\r
+ {\r
+ p_dec->pf_picture_link( p_dec, p_pic );\r
+ }\r
+\r
+ /* FIXME what is that, should give good value */\r
+ p_ff_pic->age = 256*256*256*64; // FIXME FIXME from ffmpeg\r
+\r
+ return 0;\r
+}\r
+\r
+static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,\r
+ AVFrame *p_ff_pic )\r
+{\r
+ decoder_t *p_dec = (decoder_t *)p_context->opaque;\r
+ picture_t *p_pic;\r
+\r
+ if( !p_ff_pic->opaque )\r
+ {\r
+ avcodec_default_release_buffer( p_context, p_ff_pic );\r
+ return;\r
+ }\r
+\r
+ p_pic = (picture_t*)p_ff_pic->opaque;\r
+\r
+ p_ff_pic->data[0] = NULL;\r
+ p_ff_pic->data[1] = NULL;\r
+ p_ff_pic->data[2] = NULL;\r
+ p_ff_pic->data[3] = NULL;\r
+\r
+ if( p_ff_pic->reference != 0 )\r
+ {\r
+ p_dec->pf_picture_unlink( p_dec, p_pic );\r
+ }\r
+}\r
-/*****************************************************************************
- * es_out.c: Es Out handler for input.
- *****************************************************************************
- * Copyright (C) 2003-2004 VideoLAN
- * $Id$
- *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- *
- * 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.
- *
- * 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 <stdlib.h>
-
-#include <vlc/vlc.h>
-#include <vlc/input.h>
-#include <vlc/decoder.h>
-
-#include "input_internal.h"
-
-#include "vlc_playlist.h"
-#include "iso_lang.h"
-/* FIXME we should find a better way than including that */
-#include "../misc/iso-639_def.h"
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-typedef struct
-{
- /* Program ID */
- int i_id;
-
- /* Number of es for this pgrm */
- int i_es;
-
- vlc_bool_t b_selected;
-
- /* Clock for this program */
- input_clock_t clock;
-
-} es_out_pgrm_t;
-
-struct es_out_id_t
-{
- /* ES ID */
- int i_id;
- es_out_pgrm_t *p_pgrm;
-
- /* */
- int64_t i_preroll_end;
-
- /* Channel in the track type */
- int i_channel;
- es_format_t fmt;
- char *psz_language;
- char *psz_language_code;
- decoder_t *p_dec;
-};
-
-struct es_out_sys_t
-{
- input_thread_t *p_input;
-
- /* all programs */
- int i_pgrm;
- es_out_pgrm_t **pgrm;
- es_out_pgrm_t **pp_selected_pgrm; /* --programs */
- es_out_pgrm_t *p_pgrm; /* Master program */
-
- /* all es */
- int i_id;
- int i_es;
- es_out_id_t **es;
-
- /* mode gestion */
- vlc_bool_t b_active;
- int i_mode;
-
- /* es count */
- int i_audio;
- int i_video;
- int i_sub;
-
- /* es to select */
- int i_audio_last;
- int i_sub_last;
- char **ppsz_audio_language;
- char **ppsz_sub_language;
-
- /* current main es */
- es_out_id_t *p_es_audio;
- es_out_id_t *p_es_video;
- es_out_id_t *p_es_sub;
-
- /* delay */
- int64_t i_audio_delay;
- int64_t i_spu_delay;
-};
-
-static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * );
-static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );
-static void EsOutDel ( es_out_t *, es_out_id_t * );
-static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );
-static int EsOutControl( es_out_t *, int i_query, va_list );
-
-static void EsOutAddInfo( es_out_t *, es_out_id_t *es );
-
-static void EsSelect( es_out_t *out, es_out_id_t *es );
-static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );
-static char *LanguageGetName( const char *psz_code );
-static char *LanguageGetCode( const char *psz_lang );
-static char **LanguageSplit( const char *psz_langs );
-static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );
-
-/*****************************************************************************
- * input_EsOutNew:
- *****************************************************************************/
-es_out_t *input_EsOutNew( input_thread_t *p_input )
-{
- es_out_t *out = malloc( sizeof( es_out_t ) );
- es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );
- vlc_value_t val;
- int i;
-
- out->pf_add = EsOutAdd;
- out->pf_send = EsOutSend;
- out->pf_del = EsOutDel;
- out->pf_control = EsOutControl;
- out->p_sys = p_sys;
-
- p_sys->p_input = p_input;
-
- p_sys->b_active = VLC_FALSE;
- p_sys->i_mode = ES_OUT_MODE_AUTO;
-
-
- p_sys->i_pgrm = 0;
- p_sys->pgrm = NULL;
- p_sys->p_pgrm = NULL;
-
- p_sys->i_id = 0;
- p_sys->i_es = 0;
- p_sys->es = NULL;
-
- p_sys->i_audio = 0;
- p_sys->i_video = 0;
- p_sys->i_sub = 0;
-
- /* */
- var_Get( p_input, "audio-track", &val );
- p_sys->i_audio_last = val.i_int;
-
- var_Get( p_input, "sub-track", &val );
- p_sys->i_sub_last = val.i_int;
-
- var_Get( p_input, "audio-language", &val );
- p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);
- if( p_sys->ppsz_audio_language )
- {
- for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
- msg_Dbg( p_input, "Select audio in language[%d] %s",
- i, p_sys->ppsz_audio_language[i] );
- }
-
- var_Get( p_input, "sub-language", &val );
- p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);
- if( p_sys->ppsz_sub_language )
- {
- for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
- msg_Dbg( p_input, "Select subtitle in language[%d] %s",
- i, p_sys->ppsz_sub_language[i] );
- }
-
- /* */
- p_sys->p_es_audio = NULL;
- p_sys->p_es_video = NULL;
- p_sys->p_es_sub = NULL;
-
- p_sys->i_audio_delay= 0;
- p_sys->i_spu_delay = 0;
-
- return out;
-}
-
-/*****************************************************************************
- * input_EsOutDelete:
- *****************************************************************************/
-void input_EsOutDelete( es_out_t *out )
-{
- es_out_sys_t *p_sys = out->p_sys;
- int i;
-
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec )
- {
- input_DecoderDelete( p_sys->es[i]->p_dec );
- }
- if( p_sys->es[i]->psz_language )
- free( p_sys->es[i]->psz_language );
- if( p_sys->es[i]->psz_language_code )
- free( p_sys->es[i]->psz_language_code );
- es_format_Clean( &p_sys->es[i]->fmt );
-
- free( p_sys->es[i] );
- }
- if( p_sys->ppsz_audio_language )
- {
- for( i = 0; p_sys->ppsz_audio_language[i]; i++ )
- free( p_sys->ppsz_audio_language[i] );
- free( p_sys->ppsz_audio_language );
- }
- if( p_sys->ppsz_sub_language )
- {
- for( i = 0; p_sys->ppsz_sub_language[i]; i++ )
- free( p_sys->ppsz_sub_language[i] );
- free( p_sys->ppsz_sub_language );
- }
-
- if( p_sys->es )
- free( p_sys->es );
-
- for( i = 0; i < p_sys->i_pgrm; i++ )
- {
- free( p_sys->pgrm[i] );
- }
- if( p_sys->pgrm )
- free( p_sys->pgrm );
-
- free( p_sys );
- free( out );
-}
-
-es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )
-{
- int i;
- if( i_id < 0 )
- {
- /* Special HACK, -i_id is tha cat of the stream */
- return (es_out_id_t*)((uint8_t*)NULL-i_id);
- }
-
- for( i = 0; i < out->p_sys->i_es; i++ )
- {
- if( out->p_sys->es[i]->i_id == i_id )
- return out->p_sys->es[i];
- }
- return NULL;
-}
-
-void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )
-{
- es_out_sys_t *p_sys = out->p_sys;
- int i;
-
- for( i = 0; i < p_sys->i_es; i++ )
- {
- es_out_id_t *es = p_sys->es[i];
-
- /* Send a dummy block to let decoder know that
- * there is a discontinuity */
- if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )
- {
- input_DecoderDiscontinuity( es->p_dec );
- }
- }
-}
-
-void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )
-{
- es_out_sys_t *p_sys = out->p_sys;
-
- if( i_cat == AUDIO_ES )
- p_sys->i_audio_delay = i_delay;
- else if( i_cat == SPU_ES )
- p_sys->i_spu_delay = i_delay;
-}
-
-vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out )
-{
- es_out_sys_t *p_sys = out->p_sys;
- int i;
-
- for( i = 0; i < p_sys->i_es; i++ )
- {
- es_out_id_t *es = p_sys->es[i];
-
- if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )
- return VLC_FALSE;
- }
- return VLC_TRUE;
-}
-
-/*****************************************************************************
- *
- *****************************************************************************/
-static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,
- vlc_bool_t b_delete )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- vlc_value_t val, text;
-
- char *psz_var;
-
- if( es->fmt.i_cat == AUDIO_ES )
- psz_var = "audio-es";
- else if( es->fmt.i_cat == VIDEO_ES )
- psz_var = "video-es";
- else if( es->fmt.i_cat == SPU_ES )
- psz_var = "spu-es";
- else
- return;
-
- if( b_delete )
- {
- val.i_int = es->i_id;
- var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
- return;
- }
-
- /* Get the number of ES already added */
- var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
- if( val.i_int == 0 )
- {
- vlc_value_t val2;
-
- /* First one, we need to add the "Disable" choice */
- val2.i_int = -1; text.psz_string = _("Disable");
- var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );
- val.i_int++;
- }
-
- /* Take care of the ES description */
- if( es->fmt.psz_description && *es->fmt.psz_description )
- {
- if( es->psz_language && *es->psz_language )
- {
- text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 );
- sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language );
- }
- else text.psz_string = strdup( es->fmt.psz_description );
- }
- else
- {
- if( es->psz_language && *es->psz_language )
- {
- char *temp;
- text.psz_string = malloc( strlen( _("Track %i") )+ strlen( es->psz_language ) + 30 );
- asprintf( &temp, _("Track %i"), val.i_int );
- sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language );
- free( temp );
- }
- else
- {
- text.psz_string = malloc( strlen( _("Track %i") ) + 20 );
- sprintf( text.psz_string, _("Track %i"), val.i_int );
- }
- }
-
- val.i_int = es->i_id;
- var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );
-
- free( text.psz_string );
-
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
-}
-
-/* EsOutProgramSelect:
- * Select a program and update the object variable
- */
-static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- vlc_value_t val;
- int i;
-
- if( p_sys->p_pgrm == p_pgrm )
- return; /* Nothing to do */
-
- if( p_sys->p_pgrm )
- {
- es_out_pgrm_t *old = p_sys->p_pgrm;
- msg_Dbg( p_input, "Unselecting program id=%d", old->i_id );
-
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&
- p_sys->i_mode != ES_OUT_MODE_ALL )
- EsUnselect( out, p_sys->es[i], VLC_TRUE );
- }
-
- p_sys->p_es_audio = NULL;
- p_sys->p_es_sub = NULL;
- p_sys->p_es_video = NULL;
- }
-
- msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id );
-
- /* Mark it selected */
- p_pgrm->b_selected = VLC_TRUE;
-
- /* Switch master stream */
- if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )
- {
- p_sys->p_pgrm->clock.b_master = VLC_FALSE;
- }
- p_pgrm->clock.b_master = VLC_TRUE;
- p_sys->p_pgrm = p_pgrm;
-
- /* Update "program" */
- val.i_int = p_pgrm->i_id;
- var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );
-
- /* Update "es-*" */
- var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
- var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
- var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL );
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )
- EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE );
- EsOutSelect( out, p_sys->es[i], VLC_FALSE );
- }
-
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
-}
-
-/* EsOutAddProgram:
- * Add a program
- */
-static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- vlc_value_t val;
-
- es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );
-
- /* Init */
- p_pgrm->i_id = i_group;
- p_pgrm->i_es = 0;
- p_pgrm->b_selected = VLC_FALSE;
- input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );
-
- /* Append it */
- TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );
-
- /* Update "program" variable */
- val.i_int = i_group;
- var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );
-
- if( i_group == var_GetInteger( p_input, "program" ) )
- {
- EsOutProgramSelect( out, p_pgrm );
- }
- else
- {
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
- }
- return p_pgrm;
-}
-
-/* EsOutAdd:
- * Add an es_out
- */
-static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
-
- es_out_id_t *es = malloc( sizeof( es_out_id_t ) );
- es_out_pgrm_t *p_pgrm = NULL;
- int i;
-
- if( fmt->i_group < 0 )
- {
- msg_Err( p_input, "invalid group number" );
- return NULL;
- }
-
- /* Search the program */
- for( i = 0; i < p_sys->i_pgrm; i++ )
- {
- if( fmt->i_group == p_sys->pgrm[i]->i_id )
- {
- p_pgrm = p_sys->pgrm[i];
- break;
- }
- }
- if( p_pgrm == NULL )
- {
- /* Create a new one */
- p_pgrm = EsOutProgramAdd( out, fmt->i_group );
- }
-
- /* Increase ref count for program */
- p_pgrm->i_es++;
-
- /* Set up ES */
- if( fmt->i_id < 0 )
- fmt->i_id = out->p_sys->i_id;
- es->i_id = fmt->i_id;
- es->p_pgrm = p_pgrm;
- es_format_Copy( &es->fmt, fmt );
- es->i_preroll_end = -1;
-
- switch( fmt->i_cat )
- {
- case AUDIO_ES:
- es->i_channel = p_sys->i_audio;
- break;
-
- case VIDEO_ES:
- es->i_channel = p_sys->i_video;
- break;
-
- case SPU_ES:
- es->i_channel = p_sys->i_sub;
- break;
-
- default:
- es->i_channel = 0;
- break;
- }
- es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */
- es->psz_language_code = LanguageGetCode( fmt->psz_language );
- es->p_dec = NULL;
-
- if( es->p_pgrm == p_sys->p_pgrm )
- EsOutESVarUpdate( out, es, VLC_FALSE );
-
- /* Select it if needed */
- EsOutSelect( out, es, VLC_FALSE );
-
-
- TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );
- p_sys->i_id++; /* always incremented */
- switch( fmt->i_cat )
- {
- case AUDIO_ES:
- p_sys->i_audio++;
- break;
- case SPU_ES:
- p_sys->i_sub++;
- break;
- case VIDEO_ES:
- p_sys->i_video++;
- break;
- }
-
- EsOutAddInfo( out, es );
-
- return es;
-}
-
-static void EsSelect( es_out_t *out, es_out_id_t *es )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- vlc_value_t val;
- char *psz_var;
-
- if( es->p_dec )
- {
- msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );
- return;
- }
-
- if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )
- {
- if( !var_GetBool( p_input, "video" ) ||
- ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )
- {
- msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",
- es->i_id );
- return;
- }
- }
- else if( es->fmt.i_cat == AUDIO_ES )
- {
- var_Get( p_input, "audio", &val );
- if( !var_GetBool( p_input, "audio" ) ||
- ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )
- {
- msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",
- es->i_id );
- return;
- }
- }
-
- es->i_preroll_end = -1;
- es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );
- if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )
- return;
-
- if( es->fmt.i_cat == VIDEO_ES )
- psz_var = "video-es";
- else if( es->fmt.i_cat == AUDIO_ES )
- psz_var = "audio-es";
- else if( es->fmt.i_cat == SPU_ES )
- psz_var = "spu-es";
- else
- return;
-
- /* Mark it as selected */
- val.i_int = es->i_id;
- var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
-
-
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
-}
-
-static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- vlc_value_t val;
- char *psz_var;
-
- if( es->p_dec == NULL )
- {
- msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );
- return;
- }
-
- input_DecoderDelete( es->p_dec );
- es->p_dec = NULL;
-
- if( !b_update )
- return;
-
- /* Update var */
- if( es->p_dec == NULL )
- return;
- if( es->fmt.i_cat == VIDEO_ES )
- psz_var = "video-es";
- else if( es->fmt.i_cat == AUDIO_ES )
- psz_var = "audio-es";
- else if( es->fmt.i_cat == SPU_ES )
- psz_var = "spu-es";
- else
- return;
-
- /* Mark it as selected */
- val.i_int = -1;
- var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );
-
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
-}
-
-/**
- * Select an ES given the current mode
- * XXX: you need to take a the lock before (stream.stream_lock)
- *
- * \param out The es_out structure
- * \param es es_out_id structure
- * \param b_force ...
- * \return nothing
- */
-static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )
-{
- es_out_sys_t *p_sys = out->p_sys;
-
- int i_cat = es->fmt.i_cat;
-
- if( !p_sys->b_active ||
- ( !b_force && es->fmt.i_priority < 0 ) )
- {
- return;
- }
-
- if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )
- {
- if( !es->p_dec )
- EsSelect( out, es );
- }
- else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )
- {
- vlc_value_t val;
- int i;
- var_Get( p_sys->p_input, "programs", &val );
- for ( i = 0; i < val.p_list->i_count; i++ )
- {
- if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )
- {
- if( !es->p_dec )
- EsSelect( out, es );
- break;
- }
- }
- var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );
- }
- else if( p_sys->i_mode == ES_OUT_MODE_AUTO )
- {
- int i_wanted = -1;
-
- if( es->p_pgrm != p_sys->p_pgrm )
- return;
-
- if( i_cat == AUDIO_ES )
- {
- int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,
- es->psz_language_code );
-
- if( p_sys->p_es_audio &&
- p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )
- {
- int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,
- p_sys->p_es_audio->psz_language_code );
-
- if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
- return;
- i_wanted = es->i_channel;
- }
- else
- {
- /* Select audio if (no audio selected yet)
- * - no audio-language
- * - no audio code for the ES
- * - audio code in the requested list */
- if( idx1 >= 0 ||
- !strcmp( es->psz_language_code, "??" ) ||
- !p_sys->ppsz_audio_language )
- i_wanted = es->i_channel;
- }
-
- if( p_sys->i_audio_last >= 0 )
- i_wanted = p_sys->i_audio_last;
- }
- else if( i_cat == SPU_ES )
- {
- int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,
- es->psz_language_code );
-
- if( p_sys->p_es_sub &&
- p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )
- {
- int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,
- p_sys->p_es_sub->psz_language_code );
-
- msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",
- idx1, es->psz_language_code, idx2,
- p_sys->p_es_sub->psz_language_code );
-
- if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )
- return;
- /* We found a SPU that matches our language request */
- i_wanted = es->i_channel;
- }
- else if( idx1 >= 0 )
- {
- msg_Dbg( p_sys->p_input, "idx1=%d(%s)",
- idx1, es->psz_language_code );
-
- i_wanted = es->i_channel;
- }
- if( p_sys->i_sub_last >= 0 )
- i_wanted = p_sys->i_sub_last;
- }
- else if( i_cat == VIDEO_ES )
- {
- i_wanted = es->i_channel;
- }
-
- if( i_wanted == es->i_channel && es->p_dec == NULL )
- EsSelect( out, es );
- }
-
- /* FIXME TODO handle priority here */
- if( es->p_dec )
- {
- if( i_cat == AUDIO_ES )
- {
- if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
- p_sys->p_es_audio &&
- p_sys->p_es_audio != es &&
- p_sys->p_es_audio->p_dec )
- {
- EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );
- }
- p_sys->p_es_audio = es;
- }
- else if( i_cat == SPU_ES )
- {
- if( p_sys->i_mode == ES_OUT_MODE_AUTO &&
- p_sys->p_es_sub &&
- p_sys->p_es_sub != es &&
- p_sys->p_es_sub->p_dec )
- {
- EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );
- }
- p_sys->p_es_sub = es;
- }
- else if( i_cat == VIDEO_ES )
- {
- p_sys->p_es_video = es;
- }
- }
-}
-
-/**
- * Send a block for the given es_out
- *
- * \param out the es_out to send from
- * \param es the es_out_id
- * \param p_block the data block to send
- */
-static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- es_out_pgrm_t *p_pgrm = es->p_pgrm;
- int64_t i_delay;
-
- if( es->fmt.i_cat == AUDIO_ES )
- i_delay = p_sys->i_audio_delay;
- else if( es->fmt.i_cat == SPU_ES )
- i_delay = p_sys->i_spu_delay;
- else
- i_delay = 0;
-
- /* +11 -> avoid null value with non null dts/pts */
- if( p_block->i_dts > 0 )
- {
- p_block->i_dts =
- input_ClockGetTS( p_input, &p_pgrm->clock,
- ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay;
- }
- if( p_block->i_pts > 0 )
- {
- p_block->i_pts =
- input_ClockGetTS( p_input, &p_pgrm->clock,
- ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay;
- }
- if ( es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) )
- {
- mtime_t current_date = mdate();
- if( !p_block->i_pts
- || p_block->i_pts > current_date + 10000000
- || current_date > p_block->i_pts )
- {
- /* ETSI EN 300 472 Annex A : do not take into account the PTS
- * for teletext streams. */
- p_block->i_pts = current_date + 400000
- + p_input->i_pts_delay + i_delay;
- }
- }
-
- p_block->i_rate = p_input->i_rate;
- /* Mark preroll blocks */
- if( es->i_preroll_end >= 0 )
- {
- int64_t i_date = p_block->i_pts;
- if( i_date <= 0 )
- i_date = p_block->i_dts;
-
- if( i_date < es->i_preroll_end )
- p_block->i_flags |= BLOCK_FLAG_PREROLL;
- else
- es->i_preroll_end = -1;
- }
-
- /* TODO handle mute */
- if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES ||
- p_input->i_rate == INPUT_RATE_DEFAULT ) )
- {
- input_DecoderDecode( es->p_dec, p_block );
- }
- else
- {
- block_Release( p_block );
- }
-
- return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * EsOutDel:
- *****************************************************************************/
-static void EsOutDel( es_out_t *out, es_out_id_t *es )
-{
- es_out_sys_t *p_sys = out->p_sys;
-
- /* We don't try to reselect */
- if( es->p_dec )
- EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
-
- if( es->p_pgrm == p_sys->p_pgrm )
- EsOutESVarUpdate( out, es, VLC_TRUE );
-
- TAB_REMOVE( p_sys->i_es, p_sys->es, es );
-
- es->p_pgrm->i_es--;
- if( es->p_pgrm->i_es == 0 )
- {
- msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, "
- "TODO cleaning ?" );
- }
-
- if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;
- if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;
- if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL;
-
- switch( es->fmt.i_cat )
- {
- case AUDIO_ES:
- p_sys->i_audio--;
- break;
- case SPU_ES:
- p_sys->i_sub--;
- break;
- case VIDEO_ES:
- p_sys->i_video--;
- break;
- }
-
- if( es->psz_language )
- free( es->psz_language );
- if( es->psz_language_code )
- free( es->psz_language_code );
-
- es_format_Clean( &es->fmt );
-
- free( es );
-}
-
-/**
- * Control query handler
- *
- * \param out the es_out to control
- * \param i_query A es_out query as defined in include/ninput.h
- * \param args a variable list of arguments for the query
- * \return VLC_SUCCESS or an error code
- */
-static int EsOutControl( es_out_t *out, int i_query, va_list args )
-{
- es_out_sys_t *p_sys = out->p_sys;
- vlc_bool_t b, *pb;
- int i, *pi;
-
- es_out_id_t *es;
-
- switch( i_query )
- {
- case ES_OUT_SET_ES_STATE:
- es = (es_out_id_t*) va_arg( args, es_out_id_t * );
- b = (vlc_bool_t) va_arg( args, vlc_bool_t );
- if( b && es->p_dec == NULL )
- {
- EsSelect( out, es );
- return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;
- }
- else if( !b && es->p_dec )
- {
- EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );
- return VLC_SUCCESS;
- }
- return VLC_SUCCESS;
-
- case ES_OUT_GET_ES_STATE:
- es = (es_out_id_t*) va_arg( args, es_out_id_t * );
- pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
-
- *pb = es->p_dec ? VLC_TRUE : VLC_FALSE;
- return VLC_SUCCESS;
-
- case ES_OUT_SET_ACTIVE:
- {
- b = (vlc_bool_t) va_arg( args, vlc_bool_t );
- p_sys->b_active = b;
- /* Needed ? */
- if( b )
- var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );
- return VLC_SUCCESS;
- }
-
- case ES_OUT_GET_ACTIVE:
- pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );
- *pb = p_sys->b_active;
- return VLC_SUCCESS;
-
- case ES_OUT_SET_MODE:
- i = (int) va_arg( args, int );
- if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||
- i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )
- {
- p_sys->i_mode = i;
-
- /* Reapply policy mode */
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec )
- {
- EsUnselect( out, p_sys->es[i],
- p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
- }
- }
- for( i = 0; i < p_sys->i_es; i++ )
- {
- EsOutSelect( out, p_sys->es[i], VLC_FALSE );
- }
- return VLC_SUCCESS;
- }
- return VLC_EGENERIC;
-
- case ES_OUT_GET_MODE:
- pi = (int*) va_arg( args, int* );
- *pi = p_sys->i_mode;
- return VLC_SUCCESS;
-
- case ES_OUT_SET_ES:
- es = (es_out_id_t*) va_arg( args, es_out_id_t * );
- /* Special case NULL, NULL+i_cat */
- if( es == NULL )
- {
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec )
- EsUnselect( out, p_sys->es[i],
- p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
- }
- }
- else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )
- {
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec &&
- p_sys->es[i]->fmt.i_cat == AUDIO_ES )
- EsUnselect( out, p_sys->es[i],
- p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
- }
- }
- else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )
- {
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec &&
- p_sys->es[i]->fmt.i_cat == VIDEO_ES )
- EsUnselect( out, p_sys->es[i],
- p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
- }
- }
- else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )
- {
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( p_sys->es[i]->p_dec &&
- p_sys->es[i]->fmt.i_cat == SPU_ES )
- EsUnselect( out, p_sys->es[i],
- p_sys->es[i]->p_pgrm == p_sys->p_pgrm );
- }
- }
- else
- {
- for( i = 0; i < p_sys->i_es; i++ )
- {
- if( es == p_sys->es[i] )
- {
- EsOutSelect( out, es, VLC_TRUE );
- break;
- }
- }
- }
- return VLC_SUCCESS;
-
- case ES_OUT_SET_PCR:
- case ES_OUT_SET_GROUP_PCR:
- {
- es_out_pgrm_t *p_pgrm = NULL;
- int i_group = 0;
- int64_t i_pcr;
-
- if( i_query == ES_OUT_SET_PCR )
- {
- p_pgrm = p_sys->p_pgrm;
- }
- else
- {
- int i;
- i_group = (int)va_arg( args, int );
- for( i = 0; i < p_sys->i_pgrm; i++ )
- {
- if( p_sys->pgrm[i]->i_id == i_group )
- {
- p_pgrm = p_sys->pgrm[i];
- break;
- }
- }
- }
- if( p_pgrm == NULL )
- p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */
-
- i_pcr = (int64_t)va_arg( args, int64_t );
- /* search program */
- /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */
- input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock,
- (i_pcr + 11 ) * 9 / 100);
- return VLC_SUCCESS;
- }
-
- case ES_OUT_RESET_PCR:
- for( i = 0; i < p_sys->i_pgrm; i++ )
- {
- p_sys->pgrm[i]->clock.i_synchro_state = SYNCHRO_REINIT;
- p_sys->pgrm[i]->clock.last_pts = 0;
- }
- return VLC_SUCCESS;
-
- case ES_OUT_GET_TS:
- if( p_sys->p_pgrm )
- {
- int64_t i_ts = (int64_t)va_arg( args, int64_t );
- int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );
- *pi_ts = input_ClockGetTS( p_sys->p_input,
- &p_sys->p_pgrm->clock,
- ( i_ts + 11 ) * 9 / 100 );
- return VLC_SUCCESS;
- }
- return VLC_EGENERIC;
-
- case ES_OUT_GET_GROUP:
- pi = (int*) va_arg( args, int* );
- if( p_sys->p_pgrm )
- *pi = p_sys->p_pgrm->i_id;
- else
- *pi = -1; /* FIXME */
- return VLC_SUCCESS;
-
- case ES_OUT_SET_GROUP:
- {
- int j;
- i = (int) va_arg( args, int );
- for( j = 0; j < p_sys->i_pgrm; j++ )
- {
- es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];
- if( p_pgrm->i_id == i )
- {
- EsOutProgramSelect( out, p_pgrm );
- return VLC_SUCCESS;
- }
- }
- return VLC_EGENERIC;
- }
-
- case ES_OUT_SET_FMT:
- {
- /* This ain't pretty but is need by some demuxers (eg. Ogg )
- * to update the p_extra data */
- es_format_t *p_fmt;
- es = (es_out_id_t*) va_arg( args, es_out_id_t * );
- p_fmt = (es_format_t*) va_arg( args, es_format_t * );
- if( es == NULL ) return VLC_EGENERIC;
-
- if( p_fmt->i_extra )
- {
- es->fmt.i_extra = p_fmt->i_extra;
- es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );
- memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );
-
- if( !es->p_dec ) return VLC_SUCCESS;
-
- es->p_dec->fmt_in.i_extra = p_fmt->i_extra;
- es->p_dec->fmt_in.p_extra =
- realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );
- memcpy( es->p_dec->fmt_in.p_extra,
- p_fmt->p_extra, p_fmt->i_extra );
- }
-
- return VLC_SUCCESS;
- }
-
- case ES_OUT_SET_NEXT_DISPLAY_TIME:
- {
- int64_t i_date;
-
- es = (es_out_id_t*) va_arg( args, es_out_id_t * );
- i_date = (int64_t)va_arg( args, int64_t );
-
- if( !es || !es->p_dec )
- return VLC_EGENERIC;
-
- es->i_preroll_end = i_date;
- input_DecoderPreroll( es->p_dec, i_date );
-
- return VLC_SUCCESS;
- }
-
- default:
- msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
- return VLC_EGENERIC;
- }
-}
-
-/****************************************************************************
- * LanguageGetName: try to expend iso639 into plain name
- ****************************************************************************/
-static char *LanguageGetName( const char *psz_code )
-{
- const iso639_lang_t *pl;
-
- if( psz_code == NULL )
- {
- return strdup( "" );
- }
-
- if( strlen( psz_code ) == 2 )
- {
- pl = GetLang_1( psz_code );
- }
- else if( strlen( psz_code ) == 3 )
- {
- pl = GetLang_2B( psz_code );
- if( !strcmp( pl->psz_iso639_1, "??" ) )
- {
- pl = GetLang_2T( psz_code );
- }
- }
- else
- {
- return strdup( psz_code );
- }
-
- if( !strcmp( pl->psz_iso639_1, "??" ) )
- {
- return strdup( psz_code );
- }
- else
- {
- if( *pl->psz_native_name )
- {
- return strdup( pl->psz_native_name );
- }
- return strdup( pl->psz_eng_name );
- }
-}
-
-/* Get a 2 char code */
-static char *LanguageGetCode( const char *psz_lang )
-{
- const iso639_lang_t *pl;
-
- if( psz_lang == NULL || *psz_lang == '\0' )
- return strdup("??");
-
- for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )
- {
- if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||
- !strcasecmp( pl->psz_native_name, psz_lang ) ||
- !strcasecmp( pl->psz_iso639_1, psz_lang ) ||
- !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||
- !strcasecmp( pl->psz_iso639_2B, psz_lang ) )
- break;
- }
-
- if( pl->psz_iso639_1 != NULL )
- return strdup( pl->psz_iso639_1 );
-
- return strdup("??");
-}
-
-static char **LanguageSplit( const char *psz_langs )
-{
- char *psz_dup;
- char *psz_parser;
- char **ppsz = NULL;
- int i_psz = 0;
-
- if( psz_langs == NULL )
- return NULL;
-
- psz_parser = psz_dup = strdup(psz_langs);
-
- while( psz_parser && *psz_parser )
- {
- char *psz;
- char *psz_code;
-
- psz = strchr(psz_parser, ',' );
- if( psz )
- {
- *psz++ = '\0';
- }
-
- psz_code = LanguageGetCode( psz_parser );
- if( strcmp( psz_code, "??" ) )
- {
- TAB_APPEND( i_psz, ppsz, psz_code );
- }
-
- psz_parser = psz;
- }
-
- if( i_psz )
- {
- TAB_APPEND( i_psz, ppsz, NULL );
- }
-
- return ppsz;
-}
-
-static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )
-{
- int i;
-
- if( !ppsz_langs || !psz_lang )
- return -1;
-
- for( i = 0; ppsz_langs[i]; i++ )
- if( !strcasecmp( ppsz_langs[i], psz_lang ) )
- return i;
-
- return -1;
-}
-
-/****************************************************************************
- * EsOutAddInfo:
- * - add meta info to the playlist item
- ****************************************************************************/
-static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )
-{
- es_out_sys_t *p_sys = out->p_sys;
- input_thread_t *p_input = p_sys->p_input;
- es_format_t *fmt = &es->fmt;
- char *psz_cat;
-
- /* Add stream info */
- asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );
-
- input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),
- "%.4s", (char*)&fmt->i_codec );
-
- input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),
- "%s", es->psz_language );
-
- /* Add information */
- switch( fmt->i_cat )
- {
- case AUDIO_ES:
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Type"), _("Audio") );
-
- if( fmt->audio.i_channels > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),
- "%d", fmt->audio.i_channels );
-
- if( fmt->audio.i_rate > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),
- _("%d Hz"), fmt->audio.i_rate );
-
- if( fmt->audio.i_bitspersample > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Bits per sample"), "%d",
- fmt->audio.i_bitspersample );
-
- if( fmt->i_bitrate > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),
- _("%d kb/s"), fmt->i_bitrate / 1000 );
- break;
-
- case VIDEO_ES:
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Type"), _("Video") );
-
- if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Resolution"), "%dx%d",
- fmt->video.i_width, fmt->video.i_height );
-
- if( fmt->video.i_visible_width > 0 &&
- fmt->video.i_visible_height > 0 )
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Display resolution"), "%dx%d",
- fmt->video.i_visible_width,
- fmt->video.i_visible_height);
- break;
-
- case SPU_ES:
- input_Control( p_input, INPUT_ADD_INFO, psz_cat,
- _("Type"), _("Subtitle") );
- break;
-
- default:
- break;
- }
-
- free( psz_cat );
-}
+/*****************************************************************************\r
+ * es_out.c: Es Out handler for input.\r
+ *****************************************************************************\r
+ * Copyright (C) 2003-2004 VideoLAN\r
+ * $Id$\r
+ *\r
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <stdlib.h>\r
+\r
+#include <vlc/vlc.h>\r
+#include <vlc/input.h>\r
+#include <vlc/decoder.h>\r
+\r
+#include "input_internal.h"\r
+\r
+#include "vlc_playlist.h"\r
+#include "iso_lang.h"\r
+/* FIXME we should find a better way than including that */\r
+#include "../misc/iso-639_def.h"\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+typedef struct\r
+{\r
+ /* Program ID */\r
+ int i_id;\r
+\r
+ /* Number of es for this pgrm */\r
+ int i_es;\r
+\r
+ vlc_bool_t b_selected;\r
+\r
+ /* Clock for this program */\r
+ input_clock_t clock;\r
+\r
+} es_out_pgrm_t;\r
+\r
+struct es_out_id_t\r
+{\r
+ /* ES ID */\r
+ int i_id;\r
+ es_out_pgrm_t *p_pgrm;\r
+\r
+ /* */\r
+ int64_t i_preroll_end;\r
+\r
+ /* Channel in the track type */\r
+ int i_channel;\r
+ es_format_t fmt;\r
+ char *psz_language;\r
+ char *psz_language_code;\r
+ decoder_t *p_dec;\r
+};\r
+\r
+struct es_out_sys_t\r
+{\r
+ input_thread_t *p_input;\r
+\r
+ /* all programs */\r
+ int i_pgrm;\r
+ es_out_pgrm_t **pgrm;\r
+ es_out_pgrm_t **pp_selected_pgrm; /* --programs */\r
+ es_out_pgrm_t *p_pgrm; /* Master program */\r
+\r
+ /* all es */\r
+ int i_id;\r
+ int i_es;\r
+ es_out_id_t **es;\r
+\r
+ /* mode gestion */\r
+ vlc_bool_t b_active;\r
+ int i_mode;\r
+\r
+ /* es count */\r
+ int i_audio;\r
+ int i_video;\r
+ int i_sub;\r
+\r
+ /* es to select */\r
+ int i_audio_last;\r
+ int i_sub_last;\r
+ char **ppsz_audio_language;\r
+ char **ppsz_sub_language;\r
+\r
+ /* current main es */\r
+ es_out_id_t *p_es_audio;\r
+ es_out_id_t *p_es_video;\r
+ es_out_id_t *p_es_sub;\r
+\r
+ /* delay */\r
+ int64_t i_audio_delay;\r
+ int64_t i_spu_delay;\r
+};\r
+\r
+static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * );\r
+static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * );\r
+static void EsOutDel ( es_out_t *, es_out_id_t * );\r
+static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force );\r
+static int EsOutControl( es_out_t *, int i_query, va_list );\r
+\r
+static void EsOutAddInfo( es_out_t *, es_out_id_t *es );\r
+\r
+static void EsSelect( es_out_t *out, es_out_id_t *es );\r
+static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update );\r
+static char *LanguageGetName( const char *psz_code );\r
+static char *LanguageGetCode( const char *psz_lang );\r
+static char **LanguageSplit( const char *psz_langs );\r
+static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang );\r
+\r
+/*****************************************************************************\r
+ * input_EsOutNew:\r
+ *****************************************************************************/\r
+es_out_t *input_EsOutNew( input_thread_t *p_input )\r
+{\r
+ es_out_t *out = malloc( sizeof( es_out_t ) );\r
+ es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) );\r
+ vlc_value_t val;\r
+ int i;\r
+\r
+ out->pf_add = EsOutAdd;\r
+ out->pf_send = EsOutSend;\r
+ out->pf_del = EsOutDel;\r
+ out->pf_control = EsOutControl;\r
+ out->p_sys = p_sys;\r
+\r
+ p_sys->p_input = p_input;\r
+\r
+ p_sys->b_active = VLC_FALSE;\r
+ p_sys->i_mode = ES_OUT_MODE_AUTO;\r
+\r
+\r
+ p_sys->i_pgrm = 0;\r
+ p_sys->pgrm = NULL;\r
+ p_sys->p_pgrm = NULL;\r
+\r
+ p_sys->i_id = 0;\r
+ p_sys->i_es = 0;\r
+ p_sys->es = NULL;\r
+\r
+ p_sys->i_audio = 0;\r
+ p_sys->i_video = 0;\r
+ p_sys->i_sub = 0;\r
+\r
+ /* */\r
+ var_Get( p_input, "audio-track", &val );\r
+ p_sys->i_audio_last = val.i_int;\r
+\r
+ var_Get( p_input, "sub-track", &val );\r
+ p_sys->i_sub_last = val.i_int;\r
+\r
+ var_Get( p_input, "audio-language", &val );\r
+ p_sys->ppsz_audio_language = LanguageSplit(val.psz_string);\r
+ if( p_sys->ppsz_audio_language )\r
+ {\r
+ for( i = 0; p_sys->ppsz_audio_language[i]; i++ )\r
+ msg_Dbg( p_input, "Select audio in language[%d] %s",\r
+ i, p_sys->ppsz_audio_language[i] );\r
+ }\r
+\r
+ var_Get( p_input, "sub-language", &val );\r
+ p_sys->ppsz_sub_language = LanguageSplit(val.psz_string);\r
+ if( p_sys->ppsz_sub_language )\r
+ {\r
+ for( i = 0; p_sys->ppsz_sub_language[i]; i++ )\r
+ msg_Dbg( p_input, "Select subtitle in language[%d] %s",\r
+ i, p_sys->ppsz_sub_language[i] );\r
+ }\r
+\r
+ /* */\r
+ p_sys->p_es_audio = NULL;\r
+ p_sys->p_es_video = NULL;\r
+ p_sys->p_es_sub = NULL;\r
+\r
+ p_sys->i_audio_delay= 0;\r
+ p_sys->i_spu_delay = 0;\r
+\r
+ return out;\r
+}\r
+\r
+/*****************************************************************************\r
+ * input_EsOutDelete:\r
+ *****************************************************************************/\r
+void input_EsOutDelete( es_out_t *out )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ int i;\r
+\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec )\r
+ {\r
+ input_DecoderDelete( p_sys->es[i]->p_dec );\r
+ }\r
+ if( p_sys->es[i]->psz_language )\r
+ free( p_sys->es[i]->psz_language );\r
+ if( p_sys->es[i]->psz_language_code )\r
+ free( p_sys->es[i]->psz_language_code );\r
+ es_format_Clean( &p_sys->es[i]->fmt );\r
+\r
+ free( p_sys->es[i] );\r
+ }\r
+ if( p_sys->ppsz_audio_language )\r
+ {\r
+ for( i = 0; p_sys->ppsz_audio_language[i]; i++ )\r
+ free( p_sys->ppsz_audio_language[i] );\r
+ free( p_sys->ppsz_audio_language );\r
+ }\r
+ if( p_sys->ppsz_sub_language )\r
+ {\r
+ for( i = 0; p_sys->ppsz_sub_language[i]; i++ )\r
+ free( p_sys->ppsz_sub_language[i] );\r
+ free( p_sys->ppsz_sub_language );\r
+ }\r
+\r
+ if( p_sys->es )\r
+ free( p_sys->es );\r
+\r
+ for( i = 0; i < p_sys->i_pgrm; i++ )\r
+ {\r
+ free( p_sys->pgrm[i] );\r
+ }\r
+ if( p_sys->pgrm )\r
+ free( p_sys->pgrm );\r
+\r
+ free( p_sys );\r
+ free( out );\r
+}\r
+\r
+es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id )\r
+{\r
+ int i;\r
+ if( i_id < 0 )\r
+ {\r
+ /* Special HACK, -i_id is tha cat of the stream */\r
+ return (es_out_id_t*)((uint8_t*)NULL-i_id);\r
+ }\r
+\r
+ for( i = 0; i < out->p_sys->i_es; i++ )\r
+ {\r
+ if( out->p_sys->es[i]->i_id == i_id )\r
+ return out->p_sys->es[i];\r
+ }\r
+ return NULL;\r
+}\r
+\r
+void input_EsOutDiscontinuity( es_out_t *out, vlc_bool_t b_audio )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ int i;\r
+\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ es_out_id_t *es = p_sys->es[i];\r
+\r
+ /* Send a dummy block to let decoder know that\r
+ * there is a discontinuity */\r
+ if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) )\r
+ {\r
+ input_DecoderDiscontinuity( es->p_dec );\r
+ }\r
+ }\r
+}\r
+\r
+void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+\r
+ if( i_cat == AUDIO_ES )\r
+ p_sys->i_audio_delay = i_delay;\r
+ else if( i_cat == SPU_ES )\r
+ p_sys->i_spu_delay = i_delay;\r
+}\r
+\r
+vlc_bool_t input_EsOutDecodersEmpty( es_out_t *out )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ int i;\r
+\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ es_out_id_t *es = p_sys->es[i];\r
+\r
+ if( es->p_dec && !input_DecoderEmpty( es->p_dec ) )\r
+ return VLC_FALSE;\r
+ }\r
+ return VLC_TRUE;\r
+}\r
+\r
+/*****************************************************************************\r
+ *\r
+ *****************************************************************************/\r
+static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es,\r
+ vlc_bool_t b_delete )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ vlc_value_t val, text;\r
+\r
+ char *psz_var;\r
+\r
+ if( es->fmt.i_cat == AUDIO_ES )\r
+ psz_var = "audio-es";\r
+ else if( es->fmt.i_cat == VIDEO_ES )\r
+ psz_var = "video-es";\r
+ else if( es->fmt.i_cat == SPU_ES )\r
+ psz_var = "spu-es";\r
+ else\r
+ return;\r
+\r
+ if( b_delete )\r
+ {\r
+ val.i_int = es->i_id;\r
+ var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL );\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+ return;\r
+ }\r
+\r
+ /* Get the number of ES already added */\r
+ var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );\r
+ if( val.i_int == 0 )\r
+ {\r
+ vlc_value_t val2;\r
+\r
+ /* First one, we need to add the "Disable" choice */\r
+ val2.i_int = -1; text.psz_string = _("Disable");\r
+ var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text );\r
+ val.i_int++;\r
+ }\r
+\r
+ /* Take care of the ES description */\r
+ if( es->fmt.psz_description && *es->fmt.psz_description )\r
+ {\r
+ if( es->psz_language && *es->psz_language )\r
+ {\r
+ text.psz_string = malloc( strlen( es->fmt.psz_description) + strlen( es->psz_language ) + 10 );\r
+ sprintf( text.psz_string, "%s - [%s]", es->fmt.psz_description, es->psz_language );\r
+ }\r
+ else text.psz_string = strdup( es->fmt.psz_description );\r
+ }\r
+ else\r
+ {\r
+ if( es->psz_language && *es->psz_language )\r
+ {\r
+ char *temp;\r
+ text.psz_string = malloc( strlen( _("Track %i") )+ strlen( es->psz_language ) + 30 );\r
+ asprintf( &temp, _("Track %i"), val.i_int );\r
+ sprintf( text.psz_string, "%s - [%s]", temp, es->psz_language );\r
+ free( temp );\r
+ }\r
+ else\r
+ {\r
+ text.psz_string = malloc( strlen( _("Track %i") ) + 20 );\r
+ sprintf( text.psz_string, _("Track %i"), val.i_int );\r
+ }\r
+ }\r
+\r
+ val.i_int = es->i_id;\r
+ var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text );\r
+\r
+ free( text.psz_string );\r
+\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+}\r
+\r
+/* EsOutProgramSelect:\r
+ * Select a program and update the object variable\r
+ */\r
+static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ vlc_value_t val;\r
+ int i;\r
+\r
+ if( p_sys->p_pgrm == p_pgrm )\r
+ return; /* Nothing to do */\r
+\r
+ if( p_sys->p_pgrm )\r
+ {\r
+ es_out_pgrm_t *old = p_sys->p_pgrm;\r
+ msg_Dbg( p_input, "Unselecting program id=%d", old->i_id );\r
+\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_pgrm == old && p_sys->es[i]->p_dec &&\r
+ p_sys->i_mode != ES_OUT_MODE_ALL )\r
+ EsUnselect( out, p_sys->es[i], VLC_TRUE );\r
+ }\r
+\r
+ p_sys->p_es_audio = NULL;\r
+ p_sys->p_es_sub = NULL;\r
+ p_sys->p_es_video = NULL;\r
+ }\r
+\r
+ msg_Dbg( p_input, "Selecting program id=%d", p_pgrm->i_id );\r
+\r
+ /* Mark it selected */\r
+ p_pgrm->b_selected = VLC_TRUE;\r
+\r
+ /* Switch master stream */\r
+ if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master )\r
+ {\r
+ p_sys->p_pgrm->clock.b_master = VLC_FALSE;\r
+ }\r
+ p_pgrm->clock.b_master = VLC_TRUE;\r
+ p_sys->p_pgrm = p_pgrm;\r
+\r
+ /* Update "program" */\r
+ val.i_int = p_pgrm->i_id;\r
+ var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL );\r
+\r
+ /* Update "es-*" */\r
+ var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL );\r
+ var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL );\r
+ var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL );\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm )\r
+ EsOutESVarUpdate( out, p_sys->es[i], VLC_FALSE );\r
+ EsOutSelect( out, p_sys->es[i], VLC_FALSE );\r
+ }\r
+\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+}\r
+\r
+/* EsOutAddProgram:\r
+ * Add a program\r
+ */\r
+static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ vlc_value_t val;\r
+\r
+ es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) );\r
+\r
+ /* Init */\r
+ p_pgrm->i_id = i_group;\r
+ p_pgrm->i_es = 0;\r
+ p_pgrm->b_selected = VLC_FALSE;\r
+ input_ClockInit( &p_pgrm->clock, VLC_FALSE, p_input->input.i_cr_average );\r
+\r
+ /* Append it */\r
+ TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm );\r
+\r
+ /* Update "program" variable */\r
+ val.i_int = i_group;\r
+ var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL );\r
+\r
+ if( i_group == var_GetInteger( p_input, "program" ) )\r
+ {\r
+ EsOutProgramSelect( out, p_pgrm );\r
+ }\r
+ else\r
+ {\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+ }\r
+ return p_pgrm;\r
+}\r
+\r
+/* EsOutAdd:\r
+ * Add an es_out\r
+ */\r
+static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+\r
+ es_out_id_t *es = malloc( sizeof( es_out_id_t ) );\r
+ es_out_pgrm_t *p_pgrm = NULL;\r
+ int i;\r
+\r
+ if( fmt->i_group < 0 )\r
+ {\r
+ msg_Err( p_input, "invalid group number" );\r
+ return NULL;\r
+ }\r
+\r
+ /* Search the program */\r
+ for( i = 0; i < p_sys->i_pgrm; i++ )\r
+ {\r
+ if( fmt->i_group == p_sys->pgrm[i]->i_id )\r
+ {\r
+ p_pgrm = p_sys->pgrm[i];\r
+ break;\r
+ }\r
+ }\r
+ if( p_pgrm == NULL )\r
+ {\r
+ /* Create a new one */\r
+ p_pgrm = EsOutProgramAdd( out, fmt->i_group );\r
+ }\r
+\r
+ /* Increase ref count for program */\r
+ p_pgrm->i_es++;\r
+\r
+ /* Set up ES */\r
+ if( fmt->i_id < 0 )\r
+ fmt->i_id = out->p_sys->i_id;\r
+ es->i_id = fmt->i_id;\r
+ es->p_pgrm = p_pgrm;\r
+ es_format_Copy( &es->fmt, fmt );\r
+ es->i_preroll_end = -1;\r
+\r
+ switch( fmt->i_cat )\r
+ {\r
+ case AUDIO_ES:\r
+ es->i_channel = p_sys->i_audio;\r
+ break;\r
+\r
+ case VIDEO_ES:\r
+ es->i_channel = p_sys->i_video;\r
+ break;\r
+\r
+ case SPU_ES:\r
+ es->i_channel = p_sys->i_sub;\r
+ break;\r
+\r
+ default:\r
+ es->i_channel = 0;\r
+ break;\r
+ }\r
+ es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */\r
+ es->psz_language_code = LanguageGetCode( fmt->psz_language );\r
+ es->p_dec = NULL;\r
+\r
+ if( es->p_pgrm == p_sys->p_pgrm )\r
+ EsOutESVarUpdate( out, es, VLC_FALSE );\r
+\r
+ /* Select it if needed */\r
+ EsOutSelect( out, es, VLC_FALSE );\r
+\r
+\r
+ TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es );\r
+ p_sys->i_id++; /* always incremented */\r
+ switch( fmt->i_cat )\r
+ {\r
+ case AUDIO_ES:\r
+ p_sys->i_audio++;\r
+ break;\r
+ case SPU_ES:\r
+ p_sys->i_sub++;\r
+ break;\r
+ case VIDEO_ES:\r
+ p_sys->i_video++;\r
+ break;\r
+ }\r
+\r
+ EsOutAddInfo( out, es );\r
+\r
+ return es;\r
+}\r
+\r
+static void EsSelect( es_out_t *out, es_out_id_t *es )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ vlc_value_t val;\r
+ char *psz_var;\r
+\r
+ if( es->p_dec )\r
+ {\r
+ msg_Warn( p_input, "ES 0x%x is already selected", es->i_id );\r
+ return;\r
+ }\r
+\r
+ if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES )\r
+ {\r
+ if( !var_GetBool( p_input, "video" ) ||\r
+ ( p_input->p_sout && !var_GetBool( p_input, "sout-video" ) ) )\r
+ {\r
+ msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x",\r
+ es->i_id );\r
+ return;\r
+ }\r
+ }\r
+ else if( es->fmt.i_cat == AUDIO_ES )\r
+ {\r
+ var_Get( p_input, "audio", &val );\r
+ if( !var_GetBool( p_input, "audio" ) ||\r
+ ( p_input->p_sout && !var_GetBool( p_input, "sout-audio" ) ) )\r
+ {\r
+ msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x",\r
+ es->i_id );\r
+ return;\r
+ }\r
+ }\r
+\r
+ es->i_preroll_end = -1;\r
+ es->p_dec = input_DecoderNew( p_input, &es->fmt, VLC_FALSE );\r
+ if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm )\r
+ return;\r
+\r
+ if( es->fmt.i_cat == VIDEO_ES )\r
+ psz_var = "video-es";\r
+ else if( es->fmt.i_cat == AUDIO_ES )\r
+ psz_var = "audio-es";\r
+ else if( es->fmt.i_cat == SPU_ES )\r
+ psz_var = "spu-es";\r
+ else\r
+ return;\r
+\r
+ /* Mark it as selected */\r
+ val.i_int = es->i_id;\r
+ var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );\r
+\r
+\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+}\r
+\r
+static void EsUnselect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_update )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ vlc_value_t val;\r
+ char *psz_var;\r
+\r
+ if( es->p_dec == NULL )\r
+ {\r
+ msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id );\r
+ return;\r
+ }\r
+\r
+ input_DecoderDelete( es->p_dec );\r
+ es->p_dec = NULL;\r
+\r
+ if( !b_update )\r
+ return;\r
+\r
+ /* Update var */\r
+ if( es->p_dec == NULL )\r
+ return;\r
+ if( es->fmt.i_cat == VIDEO_ES )\r
+ psz_var = "video-es";\r
+ else if( es->fmt.i_cat == AUDIO_ES )\r
+ psz_var = "audio-es";\r
+ else if( es->fmt.i_cat == SPU_ES )\r
+ psz_var = "spu-es";\r
+ else\r
+ return;\r
+\r
+ /* Mark it as selected */\r
+ val.i_int = -1;\r
+ var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL );\r
+\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+}\r
+\r
+/**\r
+ * Select an ES given the current mode\r
+ * XXX: you need to take a the lock before (stream.stream_lock)\r
+ *\r
+ * \param out The es_out structure\r
+ * \param es es_out_id structure\r
+ * \param b_force ...\r
+ * \return nothing\r
+ */\r
+static void EsOutSelect( es_out_t *out, es_out_id_t *es, vlc_bool_t b_force )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+\r
+ int i_cat = es->fmt.i_cat;\r
+\r
+ if( !p_sys->b_active ||\r
+ ( !b_force && es->fmt.i_priority < 0 ) )\r
+ {\r
+ return;\r
+ }\r
+\r
+ if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force )\r
+ {\r
+ if( !es->p_dec )\r
+ EsSelect( out, es );\r
+ }\r
+ else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL )\r
+ {\r
+ vlc_value_t val;\r
+ int i;\r
+ var_Get( p_sys->p_input, "programs", &val );\r
+ for ( i = 0; i < val.p_list->i_count; i++ )\r
+ {\r
+ if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force )\r
+ {\r
+ if( !es->p_dec )\r
+ EsSelect( out, es );\r
+ break;\r
+ }\r
+ }\r
+ var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL );\r
+ }\r
+ else if( p_sys->i_mode == ES_OUT_MODE_AUTO )\r
+ {\r
+ int i_wanted = -1;\r
+\r
+ if( es->p_pgrm != p_sys->p_pgrm )\r
+ return;\r
+\r
+ if( i_cat == AUDIO_ES )\r
+ {\r
+ int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language,\r
+ es->psz_language_code );\r
+\r
+ if( p_sys->p_es_audio &&\r
+ p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority )\r
+ {\r
+ int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language,\r
+ p_sys->p_es_audio->psz_language_code );\r
+\r
+ if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )\r
+ return;\r
+ i_wanted = es->i_channel;\r
+ }\r
+ else\r
+ {\r
+ /* Select audio if (no audio selected yet)\r
+ * - no audio-language\r
+ * - no audio code for the ES\r
+ * - audio code in the requested list */\r
+ if( idx1 >= 0 ||\r
+ !strcmp( es->psz_language_code, "??" ) ||\r
+ !p_sys->ppsz_audio_language )\r
+ i_wanted = es->i_channel;\r
+ }\r
+\r
+ if( p_sys->i_audio_last >= 0 )\r
+ i_wanted = p_sys->i_audio_last;\r
+ }\r
+ else if( i_cat == SPU_ES )\r
+ {\r
+ int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language,\r
+ es->psz_language_code );\r
+\r
+ if( p_sys->p_es_sub &&\r
+ p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority )\r
+ {\r
+ int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language,\r
+ p_sys->p_es_sub->psz_language_code );\r
+\r
+ msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)",\r
+ idx1, es->psz_language_code, idx2,\r
+ p_sys->p_es_sub->psz_language_code );\r
+\r
+ if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) )\r
+ return;\r
+ /* We found a SPU that matches our language request */\r
+ i_wanted = es->i_channel;\r
+ }\r
+ else if( idx1 >= 0 )\r
+ {\r
+ msg_Dbg( p_sys->p_input, "idx1=%d(%s)",\r
+ idx1, es->psz_language_code );\r
+\r
+ i_wanted = es->i_channel;\r
+ }\r
+ if( p_sys->i_sub_last >= 0 )\r
+ i_wanted = p_sys->i_sub_last;\r
+ }\r
+ else if( i_cat == VIDEO_ES )\r
+ {\r
+ i_wanted = es->i_channel;\r
+ }\r
+\r
+ if( i_wanted == es->i_channel && es->p_dec == NULL )\r
+ EsSelect( out, es );\r
+ }\r
+\r
+ /* FIXME TODO handle priority here */\r
+ if( es->p_dec )\r
+ {\r
+ if( i_cat == AUDIO_ES )\r
+ {\r
+ if( p_sys->i_mode == ES_OUT_MODE_AUTO &&\r
+ p_sys->p_es_audio &&\r
+ p_sys->p_es_audio != es &&\r
+ p_sys->p_es_audio->p_dec )\r
+ {\r
+ EsUnselect( out, p_sys->p_es_audio, VLC_FALSE );\r
+ }\r
+ p_sys->p_es_audio = es;\r
+ }\r
+ else if( i_cat == SPU_ES )\r
+ {\r
+ if( p_sys->i_mode == ES_OUT_MODE_AUTO &&\r
+ p_sys->p_es_sub &&\r
+ p_sys->p_es_sub != es &&\r
+ p_sys->p_es_sub->p_dec )\r
+ {\r
+ EsUnselect( out, p_sys->p_es_sub, VLC_FALSE );\r
+ }\r
+ p_sys->p_es_sub = es;\r
+ }\r
+ else if( i_cat == VIDEO_ES )\r
+ {\r
+ p_sys->p_es_video = es;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * Send a block for the given es_out\r
+ *\r
+ * \param out the es_out to send from\r
+ * \param es the es_out_id\r
+ * \param p_block the data block to send\r
+ */\r
+static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ es_out_pgrm_t *p_pgrm = es->p_pgrm;\r
+ int64_t i_delay;\r
+\r
+ if( es->fmt.i_cat == AUDIO_ES )\r
+ i_delay = p_sys->i_audio_delay;\r
+ else if( es->fmt.i_cat == SPU_ES )\r
+ i_delay = p_sys->i_spu_delay;\r
+ else\r
+ i_delay = 0;\r
+\r
+ /* Mark preroll blocks */\r
+ if( es->i_preroll_end >= 0 )\r
+ {\r
+ int64_t i_date = p_block->i_pts;\r
+ if( i_date <= 0 )\r
+ i_date = p_block->i_dts;\r
+\r
+ if( i_date < es->i_preroll_end )\r
+ p_block->i_flags |= BLOCK_FLAG_PREROLL;\r
+ else\r
+ es->i_preroll_end = -1;\r
+ }\r
+\r
+ /* +11 -> avoid null value with non null dts/pts */\r
+ if( p_block->i_dts > 0 )\r
+ {\r
+ p_block->i_dts =\r
+ input_ClockGetTS( p_input, &p_pgrm->clock,\r
+ ( p_block->i_dts + 11 ) * 9 / 100 ) + i_delay;\r
+ }\r
+ if( p_block->i_pts > 0 )\r
+ {\r
+ p_block->i_pts =\r
+ input_ClockGetTS( p_input, &p_pgrm->clock,\r
+ ( p_block->i_pts + 11 ) * 9 / 100 ) + i_delay;\r
+ }\r
+ if ( es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) )\r
+ {\r
+ mtime_t current_date = mdate();\r
+ if( !p_block->i_pts\r
+ || p_block->i_pts > current_date + 10000000\r
+ || current_date > p_block->i_pts )\r
+ {\r
+ /* ETSI EN 300 472 Annex A : do not take into account the PTS\r
+ * for teletext streams. */\r
+ p_block->i_pts = current_date + 400000\r
+ + p_input->i_pts_delay + i_delay;\r
+ }\r
+ }\r
+\r
+ p_block->i_rate = p_input->i_rate;\r
+\r
+ /* TODO handle mute */\r
+ if( es->p_dec && ( es->fmt.i_cat != AUDIO_ES ||\r
+ p_input->i_rate == INPUT_RATE_DEFAULT ) )\r
+ {\r
+ input_DecoderDecode( es->p_dec, p_block );\r
+ }\r
+ else\r
+ {\r
+ block_Release( p_block );\r
+ }\r
+\r
+ return VLC_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************\r
+ * EsOutDel:\r
+ *****************************************************************************/\r
+static void EsOutDel( es_out_t *out, es_out_id_t *es )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+\r
+ /* We don't try to reselect */\r
+ if( es->p_dec )\r
+ EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );\r
+\r
+ if( es->p_pgrm == p_sys->p_pgrm )\r
+ EsOutESVarUpdate( out, es, VLC_TRUE );\r
+\r
+ TAB_REMOVE( p_sys->i_es, p_sys->es, es );\r
+\r
+ es->p_pgrm->i_es--;\r
+ if( es->p_pgrm->i_es == 0 )\r
+ {\r
+ msg_Warn( p_sys->p_input, "Program doesn't contain anymore ES, "\r
+ "TODO cleaning ?" );\r
+ }\r
+\r
+ if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL;\r
+ if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL;\r
+ if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL;\r
+\r
+ switch( es->fmt.i_cat )\r
+ {\r
+ case AUDIO_ES:\r
+ p_sys->i_audio--;\r
+ break;\r
+ case SPU_ES:\r
+ p_sys->i_sub--;\r
+ break;\r
+ case VIDEO_ES:\r
+ p_sys->i_video--;\r
+ break;\r
+ }\r
+\r
+ if( es->psz_language )\r
+ free( es->psz_language );\r
+ if( es->psz_language_code )\r
+ free( es->psz_language_code );\r
+\r
+ es_format_Clean( &es->fmt );\r
+\r
+ free( es );\r
+}\r
+\r
+/**\r
+ * Control query handler\r
+ *\r
+ * \param out the es_out to control\r
+ * \param i_query A es_out query as defined in include/ninput.h\r
+ * \param args a variable list of arguments for the query\r
+ * \return VLC_SUCCESS or an error code\r
+ */\r
+static int EsOutControl( es_out_t *out, int i_query, va_list args )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ vlc_bool_t b, *pb;\r
+ int i, *pi;\r
+\r
+ es_out_id_t *es;\r
+\r
+ switch( i_query )\r
+ {\r
+ case ES_OUT_SET_ES_STATE:\r
+ es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
+ b = (vlc_bool_t) va_arg( args, vlc_bool_t );\r
+ if( b && es->p_dec == NULL )\r
+ {\r
+ EsSelect( out, es );\r
+ return es->p_dec ? VLC_SUCCESS : VLC_EGENERIC;\r
+ }\r
+ else if( !b && es->p_dec )\r
+ {\r
+ EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm );\r
+ return VLC_SUCCESS;\r
+ }\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_GET_ES_STATE:\r
+ es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
+ pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );\r
+\r
+ *pb = es->p_dec ? VLC_TRUE : VLC_FALSE;\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_SET_ACTIVE:\r
+ {\r
+ b = (vlc_bool_t) va_arg( args, vlc_bool_t );\r
+ p_sys->b_active = b;\r
+ /* Needed ? */\r
+ if( b )\r
+ var_SetBool( p_sys->p_input, "intf-change", VLC_TRUE );\r
+ return VLC_SUCCESS;\r
+ }\r
+\r
+ case ES_OUT_GET_ACTIVE:\r
+ pb = (vlc_bool_t*) va_arg( args, vlc_bool_t * );\r
+ *pb = p_sys->b_active;\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_SET_MODE:\r
+ i = (int) va_arg( args, int );\r
+ if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL ||\r
+ i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL )\r
+ {\r
+ p_sys->i_mode = i;\r
+\r
+ /* Reapply policy mode */\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec )\r
+ {\r
+ EsUnselect( out, p_sys->es[i],\r
+ p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
+ }\r
+ }\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ EsOutSelect( out, p_sys->es[i], VLC_FALSE );\r
+ }\r
+ return VLC_SUCCESS;\r
+ }\r
+ return VLC_EGENERIC;\r
+\r
+ case ES_OUT_GET_MODE:\r
+ pi = (int*) va_arg( args, int* );\r
+ *pi = p_sys->i_mode;\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_SET_ES:\r
+ es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
+ /* Special case NULL, NULL+i_cat */\r
+ if( es == NULL )\r
+ {\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec )\r
+ EsUnselect( out, p_sys->es[i],\r
+ p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
+ }\r
+ }\r
+ else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) )\r
+ {\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec &&\r
+ p_sys->es[i]->fmt.i_cat == AUDIO_ES )\r
+ EsUnselect( out, p_sys->es[i],\r
+ p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
+ }\r
+ }\r
+ else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) )\r
+ {\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec &&\r
+ p_sys->es[i]->fmt.i_cat == VIDEO_ES )\r
+ EsUnselect( out, p_sys->es[i],\r
+ p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
+ }\r
+ }\r
+ else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) )\r
+ {\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( p_sys->es[i]->p_dec &&\r
+ p_sys->es[i]->fmt.i_cat == SPU_ES )\r
+ EsUnselect( out, p_sys->es[i],\r
+ p_sys->es[i]->p_pgrm == p_sys->p_pgrm );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for( i = 0; i < p_sys->i_es; i++ )\r
+ {\r
+ if( es == p_sys->es[i] )\r
+ {\r
+ EsOutSelect( out, es, VLC_TRUE );\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_SET_PCR:\r
+ case ES_OUT_SET_GROUP_PCR:\r
+ {\r
+ es_out_pgrm_t *p_pgrm = NULL;\r
+ int i_group = 0;\r
+ int64_t i_pcr;\r
+\r
+ if( i_query == ES_OUT_SET_PCR )\r
+ {\r
+ p_pgrm = p_sys->p_pgrm;\r
+ }\r
+ else\r
+ {\r
+ int i;\r
+ i_group = (int)va_arg( args, int );\r
+ for( i = 0; i < p_sys->i_pgrm; i++ )\r
+ {\r
+ if( p_sys->pgrm[i]->i_id == i_group )\r
+ {\r
+ p_pgrm = p_sys->pgrm[i];\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if( p_pgrm == NULL )\r
+ p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */\r
+\r
+ i_pcr = (int64_t)va_arg( args, int64_t );\r
+ /* search program */\r
+ /* 11 is a vodoo trick to avoid non_pcr*9/100 to be null */\r
+ input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock,\r
+ (i_pcr + 11 ) * 9 / 100);\r
+ return VLC_SUCCESS;\r
+ }\r
+\r
+ case ES_OUT_RESET_PCR:\r
+ for( i = 0; i < p_sys->i_pgrm; i++ )\r
+ {\r
+ p_sys->pgrm[i]->clock.i_synchro_state = SYNCHRO_REINIT;\r
+ p_sys->pgrm[i]->clock.last_pts = 0;\r
+ }\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_GET_TS:\r
+ if( p_sys->p_pgrm )\r
+ {\r
+ int64_t i_ts = (int64_t)va_arg( args, int64_t );\r
+ int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * );\r
+ *pi_ts = input_ClockGetTS( p_sys->p_input,\r
+ &p_sys->p_pgrm->clock,\r
+ ( i_ts + 11 ) * 9 / 100 );\r
+ return VLC_SUCCESS;\r
+ }\r
+ return VLC_EGENERIC;\r
+\r
+ case ES_OUT_GET_GROUP:\r
+ pi = (int*) va_arg( args, int* );\r
+ if( p_sys->p_pgrm )\r
+ *pi = p_sys->p_pgrm->i_id;\r
+ else\r
+ *pi = -1; /* FIXME */\r
+ return VLC_SUCCESS;\r
+\r
+ case ES_OUT_SET_GROUP:\r
+ {\r
+ int j;\r
+ i = (int) va_arg( args, int );\r
+ for( j = 0; j < p_sys->i_pgrm; j++ )\r
+ {\r
+ es_out_pgrm_t *p_pgrm = p_sys->pgrm[j];\r
+ if( p_pgrm->i_id == i )\r
+ {\r
+ EsOutProgramSelect( out, p_pgrm );\r
+ return VLC_SUCCESS;\r
+ }\r
+ }\r
+ return VLC_EGENERIC;\r
+ }\r
+\r
+ case ES_OUT_SET_FMT:\r
+ {\r
+ /* This ain't pretty but is need by some demuxers (eg. Ogg )\r
+ * to update the p_extra data */\r
+ es_format_t *p_fmt;\r
+ es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
+ p_fmt = (es_format_t*) va_arg( args, es_format_t * );\r
+ if( es == NULL ) return VLC_EGENERIC;\r
+\r
+ if( p_fmt->i_extra )\r
+ {\r
+ es->fmt.i_extra = p_fmt->i_extra;\r
+ es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra );\r
+ memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra );\r
+\r
+ if( !es->p_dec ) return VLC_SUCCESS;\r
+\r
+ es->p_dec->fmt_in.i_extra = p_fmt->i_extra;\r
+ es->p_dec->fmt_in.p_extra =\r
+ realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra );\r
+ memcpy( es->p_dec->fmt_in.p_extra,\r
+ p_fmt->p_extra, p_fmt->i_extra );\r
+ }\r
+\r
+ return VLC_SUCCESS;\r
+ }\r
+\r
+ case ES_OUT_SET_NEXT_DISPLAY_TIME:\r
+ {\r
+ int64_t i_date;\r
+\r
+ es = (es_out_id_t*) va_arg( args, es_out_id_t * );\r
+ i_date = (int64_t)va_arg( args, int64_t );\r
+\r
+ if( !es || !es->p_dec )\r
+ return VLC_EGENERIC;\r
+\r
+ es->i_preroll_end = i_date;\r
+ input_DecoderPreroll( es->p_dec, i_date );\r
+\r
+ return VLC_SUCCESS;\r
+ }\r
+\r
+ default:\r
+ msg_Err( p_sys->p_input, "unknown query in es_out_Control" );\r
+ return VLC_EGENERIC;\r
+ }\r
+}\r
+\r
+/****************************************************************************\r
+ * LanguageGetName: try to expend iso639 into plain name\r
+ ****************************************************************************/\r
+static char *LanguageGetName( const char *psz_code )\r
+{\r
+ const iso639_lang_t *pl;\r
+\r
+ if( psz_code == NULL )\r
+ {\r
+ return strdup( "" );\r
+ }\r
+\r
+ if( strlen( psz_code ) == 2 )\r
+ {\r
+ pl = GetLang_1( psz_code );\r
+ }\r
+ else if( strlen( psz_code ) == 3 )\r
+ {\r
+ pl = GetLang_2B( psz_code );\r
+ if( !strcmp( pl->psz_iso639_1, "??" ) )\r
+ {\r
+ pl = GetLang_2T( psz_code );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ return strdup( psz_code );\r
+ }\r
+\r
+ if( !strcmp( pl->psz_iso639_1, "??" ) )\r
+ {\r
+ return strdup( psz_code );\r
+ }\r
+ else\r
+ {\r
+ if( *pl->psz_native_name )\r
+ {\r
+ return strdup( pl->psz_native_name );\r
+ }\r
+ return strdup( pl->psz_eng_name );\r
+ }\r
+}\r
+\r
+/* Get a 2 char code */\r
+static char *LanguageGetCode( const char *psz_lang )\r
+{\r
+ const iso639_lang_t *pl;\r
+\r
+ if( psz_lang == NULL || *psz_lang == '\0' )\r
+ return strdup("??");\r
+\r
+ for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ )\r
+ {\r
+ if( !strcasecmp( pl->psz_eng_name, psz_lang ) ||\r
+ !strcasecmp( pl->psz_native_name, psz_lang ) ||\r
+ !strcasecmp( pl->psz_iso639_1, psz_lang ) ||\r
+ !strcasecmp( pl->psz_iso639_2T, psz_lang ) ||\r
+ !strcasecmp( pl->psz_iso639_2B, psz_lang ) )\r
+ break;\r
+ }\r
+\r
+ if( pl->psz_iso639_1 != NULL )\r
+ return strdup( pl->psz_iso639_1 );\r
+\r
+ return strdup("??");\r
+}\r
+\r
+static char **LanguageSplit( const char *psz_langs )\r
+{\r
+ char *psz_dup;\r
+ char *psz_parser;\r
+ char **ppsz = NULL;\r
+ int i_psz = 0;\r
+\r
+ if( psz_langs == NULL )\r
+ return NULL;\r
+\r
+ psz_parser = psz_dup = strdup(psz_langs);\r
+\r
+ while( psz_parser && *psz_parser )\r
+ {\r
+ char *psz;\r
+ char *psz_code;\r
+\r
+ psz = strchr(psz_parser, ',' );\r
+ if( psz )\r
+ {\r
+ *psz++ = '\0';\r
+ }\r
+\r
+ psz_code = LanguageGetCode( psz_parser );\r
+ if( strcmp( psz_code, "??" ) )\r
+ {\r
+ TAB_APPEND( i_psz, ppsz, psz_code );\r
+ }\r
+\r
+ psz_parser = psz;\r
+ }\r
+\r
+ if( i_psz )\r
+ {\r
+ TAB_APPEND( i_psz, ppsz, NULL );\r
+ }\r
+\r
+ return ppsz;\r
+}\r
+\r
+static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang )\r
+{\r
+ int i;\r
+\r
+ if( !ppsz_langs || !psz_lang )\r
+ return -1;\r
+\r
+ for( i = 0; ppsz_langs[i]; i++ )\r
+ if( !strcasecmp( ppsz_langs[i], psz_lang ) )\r
+ return i;\r
+\r
+ return -1;\r
+}\r
+\r
+/****************************************************************************\r
+ * EsOutAddInfo:\r
+ * - add meta info to the playlist item\r
+ ****************************************************************************/\r
+static void EsOutAddInfo( es_out_t *out, es_out_id_t *es )\r
+{\r
+ es_out_sys_t *p_sys = out->p_sys;\r
+ input_thread_t *p_input = p_sys->p_input;\r
+ es_format_t *fmt = &es->fmt;\r
+ char *psz_cat;\r
+\r
+ /* Add stream info */\r
+ asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 );\r
+\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"),\r
+ "%.4s", (char*)&fmt->i_codec );\r
+\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"),\r
+ "%s", es->psz_language );\r
+\r
+ /* Add information */\r
+ switch( fmt->i_cat )\r
+ {\r
+ case AUDIO_ES:\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Type"), _("Audio") );\r
+\r
+ if( fmt->audio.i_channels > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"),\r
+ "%d", fmt->audio.i_channels );\r
+\r
+ if( fmt->audio.i_rate > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"),\r
+ _("%d Hz"), fmt->audio.i_rate );\r
+\r
+ if( fmt->audio.i_bitspersample > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Bits per sample"), "%d",\r
+ fmt->audio.i_bitspersample );\r
+\r
+ if( fmt->i_bitrate > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"),\r
+ _("%d kb/s"), fmt->i_bitrate / 1000 );\r
+ break;\r
+\r
+ case VIDEO_ES:\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Type"), _("Video") );\r
+\r
+ if( fmt->video.i_width > 0 && fmt->video.i_height > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Resolution"), "%dx%d",\r
+ fmt->video.i_width, fmt->video.i_height );\r
+\r
+ if( fmt->video.i_visible_width > 0 &&\r
+ fmt->video.i_visible_height > 0 )\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Display resolution"), "%dx%d",\r
+ fmt->video.i_visible_width,\r
+ fmt->video.i_visible_height);\r
+ break;\r
+\r
+ case SPU_ES:\r
+ input_Control( p_input, INPUT_ADD_INFO, psz_cat,\r
+ _("Type"), _("Subtitle") );\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ free( psz_cat );\r
+}\r