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