Commit 9f69c8d1 authored by Steve Lhomme's avatar Steve Lhomme

video.c: don't display pre-rolled pictures

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