vlc/modules/demux/avformat/demux.c

1345 lines
44 KiB
C

/*****************************************************************************
* demux.c: demuxer using libavformat
*****************************************************************************
* Copyright (C) 2004-2009 VLC authors and VideoLAN
*
* 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 Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_demux.h>
#include <vlc_stream.h>
#include <vlc_meta.h>
#include <vlc_input.h>
#include <vlc_charset.h>
#include <vlc_avcodec.h>
#include "../../codec/avcodec/avcodec.h"
#include "../../codec/avcodec/chroma.h"
#include "../../codec/avcodec/avcommon_compat.h"
#include "avformat.h"
#include "../xiph.h"
#include "../vobsub.h"
#include <libavformat/avformat.h>
#include <libavutil/display.h>
#if LIBAVUTIL_VERSION_CHECK( 57, 16, 100 )
# include <libavutil/dovi_meta.h>
#endif
//#define AVFORMAT_DEBUG 1
#if LIBAVFORMAT_VERSION_CHECK(59, 0, 100)
# define AVF_MAYBE_CONST const
#else
# define AVF_MAYBE_CONST
#endif
#if (LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 80, 100))
static void avio_context_free(AVIOContext **io)
{
av_freep(io);
}
#endif
struct avformat_track_s
{
es_out_id_t *p_es;
vlc_tick_t i_pcr;
es_format_t es_format;
};
/*****************************************************************************
* demux_sys_t: demux descriptor
*****************************************************************************/
typedef struct
{
AVF_MAYBE_CONST AVInputFormat *fmt;
AVFormatContext *ic;
struct avformat_track_s *tracks;
unsigned i_tracks;
vlc_tick_t i_pcr;
unsigned i_ssa_order;
int i_attachments;
input_attachment_t **attachments;
/* Only one title with seekpoints possible atm. */
input_title_t *p_title;
int i_seekpoint;
unsigned i_update;
} demux_sys_t;
#define AVFORMAT_IOBUFFER_SIZE 32768 /* FIXME */
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux ( demux_t *p_demux );
static int Control( demux_t *p_demux, int i_query, va_list args );
static int IORead( void *opaque, uint8_t *buf, int buf_size );
static int64_t IOSeek( void *opaque, int64_t offset, int whence );
static block_t *BuildSsaFrame( const AVPacket *p_pkt, unsigned i_order );
static void UpdateSeekPoint( demux_t *p_demux, vlc_tick_t i_time );
static void ResetTime( demux_t *p_demux, int64_t i_time );
static vlc_fourcc_t CodecTagToFourcc( uint32_t codec_tag )
{
// convert from little-endian avcodec codec_tag to VLC native-endian fourcc
#ifdef WORDS_BIGENDIAN
return vlc_bswap32(codec_tag);
#else
return codec_tag;
#endif
}
/*****************************************************************************
* Open
*****************************************************************************/
static void get_rotation(es_format_t *fmt, AVStream *s)
{
char const *kRotateKey = "rotate";
AVDictionaryEntry *rotation = av_dict_get(s->metadata, kRotateKey, NULL, 0);
long angle = 0;
int32_t *matrix = (int32_t *)av_stream_get_side_data(s, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if( matrix ) {
int64_t det = (int64_t)matrix[0] * matrix[4] - (int64_t)matrix[1] * matrix[3];
if (det < 0) {
/* Flip the matrix to decouple flip and rotation operations.
* Always assume an horizontal flip for simplicity,
* it can be changed later if rotation is 180º. */
av_display_matrix_flip(matrix, 1, 0);
}
angle = lround(av_display_rotation_get(matrix));
if (angle > 45 && angle < 135)
fmt->video.orientation = ORIENT_ROTATED_270;
else if (angle > 135 || angle < -135) {
if (det < 0)
fmt->video.orientation = ORIENT_VFLIPPED;
else
fmt->video.orientation = ORIENT_ROTATED_180;
}
else if (angle < -45 && angle > -135)
fmt->video.orientation = ORIENT_ROTATED_90;
else
fmt->video.orientation = ORIENT_NORMAL;
/* Flip is already applied to the 180º case. */
if (det < 0 && !(angle > 135 || angle < -135)) {
video_transform_t transform = (video_transform_t)fmt->video.orientation;
/* Flip first then rotate */
fmt->video.orientation = ORIENT_HFLIPPED;
video_format_TransformBy(&fmt->video, transform);
}
} else if( rotation ) {
angle = strtol(rotation->value, NULL, 10);
if (angle > 45 && angle < 135)
fmt->video.orientation = ORIENT_ROTATED_90;
else if (angle > 135 && angle < 225)
fmt->video.orientation = ORIENT_ROTATED_180;
else if (angle > 225 && angle < 315)
fmt->video.orientation = ORIENT_ROTATED_270;
else
fmt->video.orientation = ORIENT_NORMAL;
}
}
static void get_dovi_config(es_format_t *fmt, AVStream *s)
{
#if LIBAVUTIL_VERSION_CHECK( 57, 16, 100 )
AVDOVIDecoderConfigurationRecord *cfg =
(AVDOVIDecoderConfigurationRecord *)
av_stream_get_side_data(s, AV_PKT_DATA_DOVI_CONF, NULL);
if (!cfg)
return;
fmt->video.dovi.version_major = cfg->dv_version_major;
fmt->video.dovi.version_minor = cfg->dv_version_minor;
fmt->video.dovi.profile = cfg->dv_profile;
fmt->video.dovi.level = cfg->dv_level;
fmt->video.dovi.rpu_present = cfg->rpu_present_flag;
fmt->video.dovi.el_present = cfg->el_present_flag;
fmt->video.dovi.bl_present = cfg->bl_present_flag;
#endif
}
static AVDictionary * BuildAVOptions( demux_t *p_demux )
{
char *psz_opts = var_InheritString( p_demux, "avformat-options" );
AVDictionary *options = NULL;
if( psz_opts )
{
vlc_av_get_options( psz_opts, &options );
free( psz_opts );
}
return options;
}
static void FreeUnclaimedOptions( demux_t *p_demux, AVDictionary **pp_dict )
{
AVDictionaryEntry *t = NULL;
while ((t = av_dict_get(*pp_dict, "", t, AV_DICT_IGNORE_SUFFIX))) {
msg_Err( p_demux, "Unknown option \"%s\"", t->key );
}
av_dict_free(pp_dict);
}
static void FindStreamInfo( demux_t *p_demux, AVDictionary *options )
{
demux_sys_t *p_sys = p_demux->p_sys;
unsigned nb_streams = p_sys->ic->nb_streams;
AVDictionary *streamsoptions[nb_streams ? nb_streams : 1];
streamsoptions[0] = options;
for ( unsigned i = 1; i < nb_streams; i++ )
{
streamsoptions[i] = NULL;
if( streamsoptions[0] )
av_dict_copy( &streamsoptions[i], streamsoptions[0], 0 );
}
vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */
int error = avformat_find_stream_info( p_sys->ic, streamsoptions );
vlc_avcodec_unlock();
FreeUnclaimedOptions( p_demux, &streamsoptions[0] );
for ( unsigned i = 1; i < nb_streams; i++ ) {
av_dict_free( &streamsoptions[i] );
}
if( error < 0 )
{
msg_Warn( p_demux, "Could not find stream info: %s",
vlc_strerror_c(AVUNERROR(error)) );
}
}
static int avformat_ProbeDemux( vlc_object_t *p_this,
AVF_MAYBE_CONST AVInputFormat **pp_fmt,
const char *psz_url )
{
demux_t *p_demux = (demux_t*)p_this;
AVProbeData pd = { 0 };
const uint8_t *peek;
/* Init Probe data */
pd.buf_size = vlc_stream_Peek( p_demux->s, &peek, 2048 + 213 );
if( pd.buf_size <= 0 )
return VLC_EGENERIC;
pd.buf = malloc( pd.buf_size + AVPROBE_PADDING_SIZE );
if( unlikely(pd.buf == NULL) )
return VLC_ENOMEM;
memcpy( pd.buf, peek, pd.buf_size );
memset( pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE );
if( psz_url != NULL )
msg_Dbg( p_demux, "trying url: %s", psz_url );
pd.filename = psz_url;
vlc_init_avformat(p_this);
/* Guess format */
char *psz_format = var_InheritString( p_this, "avformat-format" );
if( psz_format )
{
if( (*pp_fmt = av_find_input_format(psz_format)) )
msg_Dbg( p_demux, "forcing format: %s", (*pp_fmt)->name );
free( psz_format );
}
if( *pp_fmt == NULL )
*pp_fmt = av_probe_input_format( &pd, 1 );
free( pd.buf );
if( *pp_fmt == NULL )
{
msg_Dbg( p_demux, "couldn't guess format" );
return VLC_EGENERIC;
}
if( !p_demux->obj.force )
{
static const char ppsz_blacklist[][16] = {
/* Don't handle MPEG unless forced */
"mpeg", "vcd", "vob", "mpegts",
/* libavformat's redirector won't work */
"redir", "sdp",
/* Don't handle subtitles format */
"ass", "srt", "microdvd",
/* No timestamps at all */
"hevc", "h264",
""
};
for( int i = 0; *ppsz_blacklist[i]; i++ )
{
if( !strcmp( (*pp_fmt)->name, ppsz_blacklist[i] ) )
return VLC_EGENERIC;
}
}
/* Don't trigger false alarms on bin files */
if( !p_demux->obj.force && !strcmp( (*pp_fmt)->name, "psxstr" ) )
{
int i_len;
if( !p_demux->psz_filepath )
return VLC_EGENERIC;
i_len = strlen( p_demux->psz_filepath );
if( i_len < 4 )
return VLC_EGENERIC;
if( strcasecmp( &p_demux->psz_filepath[i_len - 4], ".str" ) &&
strcasecmp( &p_demux->psz_filepath[i_len - 4], ".xai" ) &&
strcasecmp( &p_demux->psz_filepath[i_len - 3], ".xa" ) )
{
return VLC_EGENERIC;
}
}
msg_Dbg( p_demux, "detected format: %s", (*pp_fmt)->name );
return VLC_SUCCESS;
}
int avformat_OpenDemux( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
AVF_MAYBE_CONST AVInputFormat *fmt = NULL;
vlc_tick_t i_start_time = VLC_TICK_INVALID;
bool b_can_seek;
const char *psz_url;
int error;
if( p_demux->psz_filepath )
psz_url = p_demux->psz_filepath;
else
psz_url = p_demux->psz_url;
if( avformat_ProbeDemux( p_this, &fmt, psz_url ) != VLC_SUCCESS )
return VLC_EGENERIC;
vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek );
/* Fill p_demux fields */
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
if( !p_sys )
return VLC_ENOMEM;
p_sys->ic = 0;
p_sys->fmt = fmt;
p_sys->tracks = NULL;
p_sys->i_tracks = 0;
p_sys->i_ssa_order = 0;
TAB_INIT( p_sys->i_attachments, p_sys->attachments);
p_sys->p_title = NULL;
p_sys->i_seekpoint = 0;
p_sys->i_update = 0;
/* Create I/O wrapper */
unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE );
if( !p_io_buffer )
{
avformat_CloseDemux( p_this );
return VLC_ENOMEM;
}
p_sys->ic = avformat_alloc_context();
if( !p_sys->ic )
{
av_free( p_io_buffer );
avformat_CloseDemux( p_this );
return VLC_ENOMEM;
}
AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer,
AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek );
if( !pb )
{
av_free( p_io_buffer );
avformat_CloseDemux( p_this );
return VLC_ENOMEM;
}
/* get all options, open_input will consume its own options from the dict */
AVDictionary *options = BuildAVOptions( p_demux );
p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0;
error = avformat_open_input( &p_sys->ic, psz_url, p_sys->fmt, &options );
if( error < 0 )
{
msg_Err( p_demux, "Could not open %s: %s", psz_url,
vlc_strerror_c(AVUNERROR(error)) );
av_dict_free( &options );
av_free( pb->buffer );
avio_context_free( &pb );
p_sys->ic = NULL;
avformat_CloseDemux( p_this );
return VLC_EGENERIC;
}
/* pass remaining options for as streams options */
FindStreamInfo( p_demux, options );
unsigned nb_streams = p_sys->ic->nb_streams; /* it may have changed */
if( !nb_streams )
{
msg_Err( p_demux, "No streams found");
avformat_CloseDemux( p_this );
return VLC_EGENERIC;
}
p_sys->tracks = calloc( nb_streams, sizeof(*p_sys->tracks) );
if( !p_sys->tracks )
{
avformat_CloseDemux( p_this );
return VLC_ENOMEM;
}
p_sys->i_tracks = nb_streams;
if( error < 0 )
{
msg_Warn( p_demux, "Could not find stream info: %s",
vlc_strerror_c(AVUNERROR(error)) );
}
for( unsigned i = 0; i < nb_streams; i++ )
{
struct avformat_track_s *p_track = &p_sys->tracks[i];
AVStream *s = p_sys->ic->streams[i];
const AVCodecParameters *cp = s->codecpar;
es_format_t es_fmt;
const char *psz_type = "unknown";
/* Do not use the cover art as a stream */
if( s->disposition == AV_DISPOSITION_ATTACHED_PIC )
continue;
vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id );
switch( cp->codec_type )
{
case AVMEDIA_TYPE_AUDIO:
es_format_Init( &es_fmt, AUDIO_ES, fcc );
es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
es_fmt.i_bitrate = cp->bit_rate;
#if LIBAVUTIL_VERSION_CHECK(56, 31, 100)
es_fmt.audio.i_channels = cp->ch_layout.nb_channels;
#else
es_fmt.audio.i_channels = cp->channels;
#endif
es_fmt.audio.i_rate = cp->sample_rate;
es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample;
es_fmt.audio.i_blockalign = cp->block_align;
psz_type = "audio";
if(cp->codec_id == AV_CODEC_ID_AAC_LATM)
{
es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M');
es_fmt.b_packetized = false;
}
else if(cp->codec_id == AV_CODEC_ID_AAC && p_sys->fmt->long_name &&
strstr(p_sys->fmt->long_name, "raw ADTS AAC"))
{
es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S');
es_fmt.b_packetized = false;
}
break;
case AVMEDIA_TYPE_VIDEO:
es_format_Init( &es_fmt, VIDEO_ES, fcc );
es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
// HACK: keep the bits per sample, will be lost if i_level is used.
es_fmt.i_level = cp->bits_per_coded_sample;
/* Special case for raw video data */
if( cp->codec_id == AV_CODEC_ID_RAWVIDEO )
{
msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format );
if( GetVlcChroma( &es_fmt.video, (enum AVPixelFormat)cp->format ) != VLC_SUCCESS)
{
msg_Err( p_demux, "was unable to find a FourCC match for raw video" );
}
else
es_fmt.i_codec = es_fmt.video.i_chroma;
}
/* We need this for the h264 packetizer */
else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") ||
p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) )
es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' );
es_fmt.video.i_width = cp->width;
es_fmt.video.i_height = cp->height;
es_fmt.video.i_visible_width = es_fmt.video.i_width;
es_fmt.video.i_visible_height = es_fmt.video.i_height;
get_rotation(&es_fmt, s);
get_dovi_config(&es_fmt, s);
# warning FIXME: implement palette transmission
psz_type = "video";
AVRational rate;
rate = av_guess_frame_rate( p_sys->ic, s, NULL );
if( rate.den && rate.num )
{
es_fmt.video.i_frame_rate = rate.num;
es_fmt.video.i_frame_rate_base = rate.den;
}
AVRational ar;
ar = av_guess_sample_aspect_ratio( p_sys->ic, s, NULL );
if( ar.num && ar.den )
{
es_fmt.video.i_sar_den = ar.den;
es_fmt.video.i_sar_num = ar.num;
}
break;
case AVMEDIA_TYPE_SUBTITLE:
es_format_Init( &es_fmt, SPU_ES, fcc );
es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 &&
cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE &&
cp->extradata != NULL &&
cp->extradata_size > 0 )
{
vobsub_extra_parse(p_this, &es_fmt.subs, cp->extradata, cp->extradata_size);
}
else if( cp->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
cp->extradata_size > 3 )
{
es_fmt.subs.dvb.i_id = GetWBE( cp->extradata ) |
(GetWBE( cp->extradata + 2 ) << 16);
}
else if( cp->codec_id == AV_CODEC_ID_MOV_TEXT )
{
if( cp->extradata_size && (es_fmt.p_extra = malloc(cp->extradata_size)) )
{
memcpy( es_fmt.p_extra, cp->extradata, cp->extradata_size );
es_fmt.i_extra = cp->extradata_size;
}
}
psz_type = "subtitle";
break;
default:
es_format_Init( &es_fmt, UNKNOWN_ES, 0 );
es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag );
if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT )
{
input_attachment_t *p_attachment;
psz_type = "attachment";
if( cp->codec_id == AV_CODEC_ID_TTF )
{
AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 );
if( filename && filename->value )
{
p_attachment = vlc_input_attachment_New(
filename->value, "application/x-truetype-font",
NULL, cp->extradata, (int)cp->extradata_size );
if( p_attachment )
TAB_APPEND( p_sys->i_attachments, p_sys->attachments,
p_attachment );
}
}
else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id );
}
else
{
if( cp->codec_type == AVMEDIA_TYPE_DATA )
psz_type = "data";
msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id );
}
break;
}
AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 );
if ( language && language->value )
es_fmt.psz_language = strdup( language->value );
if( s->disposition & AV_DISPOSITION_DEFAULT )
es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000;
if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
cp->codec_type != AVMEDIA_TYPE_DATA )
{
const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" );
const uint8_t *p_extra = cp->extradata;
unsigned i_extra = cp->extradata_size;
if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg )
{
unsigned pi_size[3];
const void *pp_data[3];
unsigned i_count;
for( i_count = 0; i_count < 3; i_count++ )
{
if( i_extra < 2 )
break;
pi_size[i_count] = GetWBE( p_extra );
pp_data[i_count] = &p_extra[2];
if( i_extra < pi_size[i_count] + 2 )
break;
p_extra += 2 + pi_size[i_count];
i_extra -= 2 + pi_size[i_count];
}
if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
pi_size, pp_data, i_count ) )
{
es_fmt.i_extra = 0;
es_fmt.p_extra = NULL;
}
}
else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg )
{
const uint8_t p_dummy_comment[] = {
0, 0, 0, 0,
0, 0, 0, 0,
};
unsigned pi_size[2];
const void *pp_data[2];
pi_size[0] = i_extra;
pp_data[0] = p_extra;
pi_size[1] = sizeof(p_dummy_comment);
pp_data[1] = p_dummy_comment;
if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
pi_size, pp_data, 2 ) )
{
es_fmt.i_extra = 0;
es_fmt.p_extra = NULL;
}
}
else if( cp->codec_id == AV_CODEC_ID_OPUS )
{
const uint8_t p_dummy_comment[] = {
'O', 'p', 'u', 's',
'T', 'a', 'g', 's',
0, 0, 0, 0, /* Vendor String length */
/* Vendor String */
0, 0, 0, 0, /* User Comment List Length */
};
unsigned pi_size[2];
const void *pp_data[2];
pi_size[0] = i_extra;
pp_data[0] = p_extra;
pi_size[1] = sizeof(p_dummy_comment);
pp_data[1] = p_dummy_comment;
if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra,
pi_size, pp_data, 2 ) )
{
es_fmt.i_extra = 0;
es_fmt.p_extra = NULL;
}
}
else if( cp->extradata_size > 0 && !es_fmt.i_extra )
{
es_fmt.p_extra = malloc( i_extra );
if( es_fmt.p_extra )
{
es_fmt.i_extra = i_extra;
memcpy( es_fmt.p_extra, p_extra, i_extra );
}
}
else if ( cp->codec_id == AV_CODEC_ID_AV1 )
{
// raw AV1, we need a packetizer to detect configuration changes
es_fmt.b_packetized = false;
}
es_fmt.i_id = i;
p_track->p_es = es_out_Add( p_demux->out, &es_fmt );
if( p_track->p_es != NULL )
es_format_Copy( &p_track->es_format, &es_fmt );
if( p_track->p_es && (s->disposition & AV_DISPOSITION_DEFAULT) )
es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_track->p_es );
msg_Dbg( p_demux, "adding es: %s codec = %4.4s (0x%x)",
psz_type, (char*)&fcc, cp->codec_id );
}
es_format_Clean( &es_fmt );
}
if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE )
i_start_time = FROM_AV_TS_NZ(p_sys->ic->start_time);
msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT );
msg_Dbg( p_demux, " - format = %s (%s)",
p_sys->fmt->name, p_sys->fmt->long_name );
msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time );
msg_Dbg( p_demux, " - duration = %"PRId64,
( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ?
FROM_AV_TS_NZ(p_sys->ic->duration) : -1 );
if( p_sys->ic->nb_chapters > 0 )
{
p_sys->p_title = vlc_input_title_New();
p_sys->p_title->i_length = FROM_AV_TS_NZ(p_sys->ic->duration);
}
for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ )
{
seekpoint_t *s = vlc_seekpoint_New();
AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0);
if( title && title->value )
{
s->psz_name = strdup( title->value );
EnsureUTF8( s->psz_name );
msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name );
}
s->i_time_offset = FROM_AVSCALE( p_sys->ic->chapters[i]->start,
p_sys->ic->chapters[i]->time_base )
- (i_start_time != VLC_TICK_INVALID ? i_start_time : 0 ) + VLC_TICK_0;
TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s );
}
ResetTime( p_demux, 0 );
return VLC_SUCCESS;
}
/*****************************************************************************
* Close
*****************************************************************************/
void avformat_CloseDemux( vlc_object_t *p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
es_format_Clean( &p_sys->tracks[i].es_format );
free( p_sys->tracks );
if( p_sys->ic )
{
if( p_sys->ic->pb )
{
av_free( p_sys->ic->pb->buffer );
avio_context_free( &p_sys->ic->pb );
}
avformat_close_input( &p_sys->ic );
}
for( int i = 0; i < p_sys->i_attachments; i++ )
vlc_input_attachment_Release( p_sys->attachments[i] );
TAB_CLEAN( p_sys->i_attachments, p_sys->attachments);
if( p_sys->p_title )
vlc_input_title_Delete( p_sys->p_title );
free( p_sys );
}
/*****************************************************************************
* Demux:
*****************************************************************************/
static int Demux( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
AVPacket pkt;
block_t *p_frame;
vlc_tick_t i_start_time;
/* Read a frame */
int i_av_ret = av_read_frame( p_sys->ic, &pkt );
if( i_av_ret )
{
/* Avoid EOF if av_read_frame returns AVERROR(EAGAIN) */
if( i_av_ret == AVERROR(EAGAIN) )
return VLC_DEMUXER_SUCCESS;
return VLC_DEMUXER_EOF;
}
if( pkt.stream_index < 0 || (unsigned) pkt.stream_index >= p_sys->i_tracks )
{
av_packet_unref( &pkt );
return VLC_DEMUXER_SUCCESS;
}
struct avformat_track_s *p_track = &p_sys->tracks[pkt.stream_index];
const AVStream *p_stream = p_sys->ic->streams[pkt.stream_index];
if( p_stream->time_base.den <= 0 )
{
msg_Warn( p_demux, "Invalid time base for the stream %d", pkt.stream_index );
av_packet_unref( &pkt );
return VLC_DEMUXER_SUCCESS;
}
// handle extra data change, this can happen for FLV
#if LIBAVUTIL_VERSION_MAJOR < 57
int side_data_size;
#else
size_t side_data_size;
#endif
uint8_t *side_data = av_packet_get_side_data( &pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size );
if( side_data_size > 0 ) {
// ignore new extradata which is the same as previous version
if( side_data_size != p_track->es_format.i_extra ||
memcmp( side_data, p_track->es_format.p_extra, side_data_size ) != 0 )
{
msg_Warn( p_demux, "New extra data found, seek may not work as expected" );
void *p_extra = realloc( p_track->es_format.p_extra, side_data_size );
bool success = p_extra != NULL;
if( likely(success) )
{
p_track->es_format.p_extra = p_extra;
memcpy( p_track->es_format.p_extra, side_data, side_data_size );
p_track->es_format.i_extra = side_data_size;
success = es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT,
p_track->p_es, &p_track->es_format ) == VLC_SUCCESS;
}
if( !success )
{
FREENULL( p_track->es_format.p_extra );
p_track->es_format.i_extra = 0;
av_packet_unref( &pkt );
return VLC_DEMUXER_EOF;
}
}
}
if( p_stream->codecpar->codec_id == AV_CODEC_ID_SSA )
{
p_frame = BuildSsaFrame( &pkt, p_sys->i_ssa_order++ );
if( !p_frame )
{
av_packet_unref( &pkt );
return VLC_DEMUXER_SUCCESS;
}
}
else if( p_stream->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE )
{
if( ( p_frame = block_Alloc( pkt.size + 3 ) ) == NULL )
{
av_packet_unref( &pkt );
return VLC_DEMUXER_EOF;
}
p_frame->p_buffer[0] = 0x20;
p_frame->p_buffer[1] = 0x00;
memcpy( &p_frame->p_buffer[2], pkt.data, pkt.size );
p_frame->p_buffer[p_frame->i_buffer - 1] = 0x3f;
}
else
{
if( ( p_frame = block_Alloc( pkt.size ) ) == NULL )
{
av_packet_unref( &pkt );
return VLC_DEMUXER_EOF;
}
memcpy( p_frame->p_buffer, pkt.data, pkt.size );
}
if( pkt.flags & AV_PKT_FLAG_KEY )
p_frame->i_flags |= BLOCK_FLAG_TYPE_I;
/* Used to avoid timestamps overflow */
if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE )
{
i_start_time = FROM_AV_TS_NZ(p_sys->ic->start_time);
}
else
i_start_time = 0;
if( pkt.dts == (int64_t)AV_NOPTS_VALUE )
p_frame->i_dts = VLC_TICK_INVALID;
else
{
p_frame->i_dts = FROM_AVSCALE( pkt.dts, p_stream->time_base )
- i_start_time + VLC_TICK_0;
}
if( pkt.pts == (int64_t)AV_NOPTS_VALUE )
p_frame->i_pts = VLC_TICK_INVALID;
else
{
p_frame->i_pts = FROM_AVSCALE( pkt.pts, p_stream->time_base )
- i_start_time + VLC_TICK_0;
}
if( pkt.duration > 0 && p_frame->i_length <= 0 )
p_frame->i_length = FROM_AVSCALE( pkt.duration, p_stream->time_base );
/* Add here notoriously bugged file formats/samples */
if( !strcmp( p_sys->fmt->name, "flv" ) )
{
/* FLV and video PTS */
if( p_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
pkt.dts != (int64_t)AV_NOPTS_VALUE && pkt.dts == pkt.pts )
p_frame->i_pts = VLC_TICK_INVALID;
/* Handle broken dts/pts increase with AAC. Duration is correct.
* sky_the80s_aacplus.flv #8195 */
if( p_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
p_stream->codecpar->codec_id == AV_CODEC_ID_AAC )
{
if( p_track->i_pcr != VLC_TICK_INVALID &&
p_track->i_pcr + p_frame->i_length > p_frame->i_dts )
{
p_frame->i_dts = p_frame->i_pts = p_track->i_pcr + p_frame->i_length;
}
}
}
#ifdef AVFORMAT_DEBUG
msg_Dbg( p_demux, "tk[%d] dts=%"PRId64" pts=%"PRId64,
pkt.stream_index, p_frame->i_dts, p_frame->i_pts );
#endif
if( p_frame->i_dts != VLC_TICK_INVALID && p_track->p_es != NULL )
p_track->i_pcr = p_frame->i_dts;
vlc_tick_t i_ts_max = VLC_TICK_MIN;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
if( p_sys->tracks[i].p_es != NULL )
i_ts_max = __MAX( i_ts_max, p_sys->tracks[i].i_pcr );
}
vlc_tick_t i_ts_min = VLC_TICK_MAX;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
{
if( p_sys->tracks[i].p_es != NULL &&
p_sys->tracks[i].i_pcr != VLC_TICK_INVALID &&
p_sys->tracks[i].i_pcr + VLC_TICK_FROM_SEC(10)>= i_ts_max )
i_ts_min = __MIN( i_ts_min, p_sys->tracks[i].i_pcr );
}
if( i_ts_min >= p_sys->i_pcr && likely(i_ts_min != VLC_TICK_MAX) )
{
p_sys->i_pcr = i_ts_min;
es_out_SetPCR( p_demux->out, p_sys->i_pcr );
UpdateSeekPoint( p_demux, p_sys->i_pcr );
}
if( p_track->p_es != NULL )
es_out_Send( p_demux->out, p_track->p_es, p_frame );
else
block_Release( p_frame );
av_packet_unref( &pkt );
return VLC_DEMUXER_SUCCESS;
}
static void UpdateSeekPoint( demux_t *p_demux, vlc_tick_t i_time )
{
demux_sys_t *p_sys = p_demux->p_sys;
int i;
if( !p_sys->p_title )
return;
for( i = 0; i < p_sys->p_title->i_seekpoint; i++ )
{
if( i_time < p_sys->p_title->seekpoint[i]->i_time_offset )
break;
}
i--;
if( i != p_sys->i_seekpoint && i >= 0 )
{
p_sys->i_seekpoint = i;
p_sys->i_update |= INPUT_UPDATE_SEEKPOINT;
}
}
static void ResetTime( demux_t *p_demux, int64_t i_time )
{
demux_sys_t *p_sys = p_demux->p_sys;
vlc_tick_t t;
if( p_sys->ic->start_time == (int64_t)AV_NOPTS_VALUE || i_time < 0 )
t = VLC_TICK_INVALID;
else
t = FROM_AV_TS( i_time );
p_sys->i_pcr = t;
for( unsigned i = 0; i < p_sys->i_tracks; i++ )
p_sys->tracks[i].i_pcr = VLC_TICK_INVALID;
if( t != VLC_TICK_INVALID )
{
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, t );
UpdateSeekPoint( p_demux, t );
}
}
static block_t *BuildSsaFrame( const AVPacket *p_pkt, unsigned i_order )
{
if( p_pkt->size <= 0 )
return NULL;
char buffer[256];
const size_t i_buffer_size = __MIN( (int)sizeof(buffer) - 1, p_pkt->size );
memcpy( buffer, p_pkt->data, i_buffer_size );
buffer[i_buffer_size] = '\0';
/* */
int i_layer;
int h0, m0, s0, c0;
int h1, m1, s1, c1;
int i_position = 0;
if( sscanf( buffer, "Dialogue: %d,%d:%d:%d.%d,%d:%d:%d.%d,%n", &i_layer,
&h0, &m0, &s0, &c0, &h1, &m1, &s1, &c1, &i_position ) < 9 )
return NULL;
if( i_position <= 0 || (unsigned)i_position >= i_buffer_size )
return NULL;
char *p;
if( asprintf( &p, "%u,%d,%.*s", i_order, i_layer, p_pkt->size - i_position, p_pkt->data + i_position ) < 0 )
return NULL;
block_t *p_frame = block_heap_Alloc( p, strlen(p) + 1 );
if( p_frame )
p_frame->i_length = vlc_tick_from_sec((h1-h0) * 3600 +
(m1-m0) * 60 +
(s1-s0) * 1) +
VLC_TICK_FROM_MS (c1-c0) / 100;
return p_frame;
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
demux_sys_t *p_sys = p_demux->p_sys;
const int64_t i_start_time = p_sys->ic->start_time != AV_NOPTS_VALUE ? p_sys->ic->start_time : 0;
double f, *pf;
int64_t i64;
switch( i_query )
{
case DEMUX_CAN_SEEK:
*va_arg( args, bool * ) = true;
return VLC_SUCCESS;
case DEMUX_GET_POSITION:
pf = va_arg( args, double * ); *pf = 0.0;
i64 = stream_Size( p_demux->s );
if( i64 > 0 )
{
double current = vlc_stream_Tell( p_demux->s );
*pf = current / (double)i64;
}
if( (p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE) && (p_sys->i_pcr > 0) )
{
*pf = (double)p_sys->i_pcr / (double)p_sys->ic->duration;
}
return VLC_SUCCESS;
case DEMUX_SET_POSITION:
{
f = va_arg( args, double );
bool precise = va_arg( args, int );
i64 = p_sys->ic->duration * f + i_start_time;
msg_Warn( p_demux, "DEMUX_SET_POSITION: %"PRId64, i64 );
/* If we have a duration, we prefer to seek by time
but if we don't, or if the seek fails, try BYTE seeking */
if( p_sys->ic->duration == (int64_t)AV_NOPTS_VALUE ||
(av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0) )
{
int64_t i_size = stream_Size( p_demux->s );
i64 = (i_size * f);
msg_Warn( p_demux, "DEMUX_SET_BYTE_POSITION: %"PRId64, i64 );
if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BYTE ) < 0 )
return VLC_EGENERIC;
ResetTime( p_demux, -1 );
}
else
{
if( precise )
ResetTime( p_demux, i64 - i_start_time );
else
ResetTime( p_demux, -1 );
}
return VLC_SUCCESS;
}
case DEMUX_GET_LENGTH:
if( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE )
*va_arg( args, vlc_tick_t * ) = FROM_AV_TS_NZ(p_sys->ic->duration);
else
*va_arg( args, vlc_tick_t * ) = 0;
return VLC_SUCCESS;
case DEMUX_GET_TIME:
*va_arg( args, vlc_tick_t * ) = p_sys->i_pcr;
return VLC_SUCCESS;
case DEMUX_SET_TIME:
{
i64 = TO_AV_TS(va_arg( args, vlc_tick_t ));
bool precise = va_arg( args, int );
msg_Warn( p_demux, "DEMUX_SET_TIME: %"PRId64, i64 );
if( av_seek_frame( p_sys->ic, -1, i64 + i_start_time, AVSEEK_FLAG_BACKWARD ) < 0 )
{
return VLC_EGENERIC;
}
if( precise )
ResetTime( p_demux, i64 - i_start_time );
else
ResetTime( p_demux, -1 );
return VLC_SUCCESS;
}
case DEMUX_HAS_UNSUPPORTED_META:
{
bool *pb_bool = va_arg( args, bool* );
*pb_bool = true;
return VLC_SUCCESS;
}
case DEMUX_GET_META:
{
static const char names[][10] = {
[vlc_meta_Title] = "title",
[vlc_meta_Artist] = "artist",
[vlc_meta_Genre] = "genre",
[vlc_meta_Copyright] = "copyright",
[vlc_meta_Album] = "album",
//[vlc_meta_TrackNumber] -- TODO: parse number/total value
[vlc_meta_Description] = "comment",
//[vlc_meta_Rating]
[vlc_meta_Date] = "date",
[vlc_meta_Setting] = "encoder",
//[vlc_meta_URL]
[vlc_meta_Language] = "language",
//[vlc_meta_NowPlaying]
[vlc_meta_Publisher] = "publisher",
[vlc_meta_EncodedBy] = "encoded_by",
//[vlc_meta_ArtworkURL]
//[vlc_meta_TrackID]
//[vlc_meta_TrackTotal]
};
vlc_meta_t *p_meta = va_arg( args, vlc_meta_t * );
AVDictionary *dict = p_sys->ic->metadata;
for( unsigned i = 0; i < sizeof(names) / sizeof(*names); i++)
{
if( !names[i][0] )
continue;
AVDictionaryEntry *e = av_dict_get( dict, names[i], NULL, 0 );
if( e != NULL && e->value != NULL && IsUTF8(e->value) )
vlc_meta_Set( p_meta, i, e->value );
}
return VLC_SUCCESS;
}
case DEMUX_GET_ATTACHMENTS:
{
input_attachment_t ***ppp_attach =
va_arg( args, input_attachment_t*** );
int *pi_int = va_arg( args, int * );
int i;
if( p_sys->i_attachments <= 0 )
return VLC_EGENERIC;
*ppp_attach = vlc_alloc( p_sys->i_attachments, sizeof(input_attachment_t*) );
if( *ppp_attach == NULL )
return VLC_EGENERIC;
for( i = 0; i < p_sys->i_attachments; i++ )
{
(*ppp_attach)[i] = vlc_input_attachment_Hold( p_sys->attachments[i] );
}
*pi_int = i;
return VLC_SUCCESS;
}
case DEMUX_GET_TITLE_INFO:
{
input_title_t ***ppp_title = va_arg( args, input_title_t *** );
int *pi_int = va_arg( args, int * );
int *pi_title_offset = va_arg( args, int * );
int *pi_seekpoint_offset = va_arg( args, int * );
if( !p_sys->p_title )
return VLC_EGENERIC;
*ppp_title = malloc( sizeof( input_title_t*) );
if( *ppp_title == NULL )
return VLC_EGENERIC;
(*ppp_title)[0] = vlc_input_title_Duplicate( p_sys->p_title );
*pi_int = (*ppp_title)[0] ? 1 : 0;
*pi_title_offset = 0;
*pi_seekpoint_offset = 0;
return VLC_SUCCESS;
}
case DEMUX_SET_TITLE:
{
const int i_title = va_arg( args, int );
if( !p_sys->p_title || i_title != 0 )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
case DEMUX_SET_SEEKPOINT:
{
const int i_seekpoint = va_arg( args, int );
if( !p_sys->p_title )
return VLC_EGENERIC;
i64 = TO_AV_TS(p_sys->p_title->seekpoint[i_seekpoint]->i_time_offset) +
i_start_time;
msg_Warn( p_demux, "DEMUX_SET_SEEKPOINT: %"PRId64, i64 );
if( av_seek_frame( p_sys->ic, -1, i64, AVSEEK_FLAG_BACKWARD ) < 0 )
{
return VLC_EGENERIC;
}
ResetTime( p_demux, i64 - i_start_time );
return VLC_SUCCESS;
}
case DEMUX_TEST_AND_CLEAR_FLAGS:
{
unsigned *restrict flags = va_arg(args, unsigned *);
*flags &= p_sys->i_update;
p_sys->i_update &= ~*flags;
return VLC_SUCCESS;
}
case DEMUX_GET_TITLE:
if( p_sys->p_title == NULL )
return VLC_EGENERIC;
*va_arg( args, int * ) = 0;
return VLC_SUCCESS;
case DEMUX_GET_SEEKPOINT:
if( p_sys->p_title == NULL )
return VLC_EGENERIC;
*va_arg( args, int * ) = p_sys->i_seekpoint;
return VLC_SUCCESS;
case DEMUX_CAN_PAUSE:
case DEMUX_SET_PAUSE_STATE:
case DEMUX_CAN_CONTROL_PACE:
case DEMUX_GET_PTS_DELAY:
return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args );
default:
return VLC_EGENERIC;
}
}
/*****************************************************************************
* I/O wrappers for libavformat
*****************************************************************************/
static int IORead( void *opaque, uint8_t *buf, int buf_size )
{
demux_t *p_demux = opaque;
if( buf_size < 0 ) return AVERROR_EOF;
int i_ret = vlc_stream_Read( p_demux->s, buf, buf_size );
return i_ret > 0 ? i_ret : AVERROR_EOF;
}
static int64_t IOSeek( void *opaque, int64_t offset, int whence )
{
demux_t *p_demux = opaque;
int64_t i_absolute;
int64_t i_size = stream_Size( p_demux->s );
#ifdef AVFORMAT_DEBUG
msg_Warn( p_demux, "IOSeek offset: %"PRId64", whence: %i", offset, whence );
#endif
switch( whence )
{
#ifdef AVSEEK_SIZE
case AVSEEK_SIZE:
return i_size;
#endif
case SEEK_SET:
i_absolute = (int64_t)offset;
break;
case SEEK_CUR:
i_absolute = vlc_stream_Tell( p_demux->s ) + (int64_t)offset;
break;
case SEEK_END:
i_absolute = i_size + (int64_t)offset;
break;
default:
return -1;
}
if( i_absolute < 0 )
{
msg_Dbg( p_demux, "Trying to seek before the beginning" );
return -1;
}
if( i_size > 0 && i_absolute >= i_size )
{
msg_Dbg( p_demux, "Trying to seek too far : EOF?" );
return -1;
}
if( vlc_stream_Seek( p_demux->s, i_absolute ) )
{
msg_Warn( p_demux, "we were not allowed to seek, or EOF " );
return -1;
}
return vlc_stream_Tell( p_demux->s );
}