Merge remote-tracking branch 'cigaes/master'

* cigaes/master:
  lavu: add myself as dynarray.h maintainer.
  ffmpeg: sub2video: send a last blank frame before closing.
  tools: add dvd2concat.
  lavf/concatdec: allow to match streams by id.

Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
Michael Niedermayer 2014-03-24 00:04:56 +01:00
commit 3788b8dbe6
5 changed files with 237 additions and 17 deletions

View File

@ -73,6 +73,7 @@ Other:
bprint Nicolas George
bswap.h
des Reimar Doeffinger
dynarray.h Nicolas George
eval.c, eval.h Michael Niedermayer
float_dsp Loren Merritt
hash Reimar Doeffinger

View File

@ -74,7 +74,7 @@ following directive is recognized:
Path to a file to read; special characters and spaces must be escaped with
backslash or single quotes.
All subsequent directives apply to that file.
All subsequent file-related directives apply to that file.
@item @code{ffconcat version 1.0}
Identify the script type and version. It also sets the @option{safe} option
@ -92,6 +92,22 @@ file is not available or accurate.
If the duration is set for all files, then it is possible to seek in the
whole concatenated video.
@item @code{stream}
Introduce a stream in the virtual file.
All subsequent stream-related directives apply to the last introduced
stream.
Some streams properties must be set in order to allow identifying the
matching streams in the subfiles.
If no streams are defined in the script, the streams from the first file are
copied.
@item @code{exact_stream_id @var{id}}
Set the id of the stream.
If this directive is given, the string with the corresponding id in the
subfiles will be used.
This is especially useful for MPEG-PS (VOB) files, where the order of the
streams is not reliable.
@end table
@subsection Options

View File

@ -297,6 +297,8 @@ static void sub2video_flush(InputStream *ist)
{
int i;
if (ist->sub2video.end_pts < INT64_MAX)
sub2video_update(ist, NULL);
for (i = 0; i < ist->nb_filters; i++)
av_buffersrc_add_ref(ist->filters[i]->filter, NULL, 0);
}

View File

@ -29,6 +29,8 @@ typedef struct {
char *url;
int64_t start_time;
int64_t duration;
int *stream_map;
int stream_map_size;
} ConcatFile;
typedef struct {
@ -39,6 +41,7 @@ typedef struct {
AVFormatContext *avf;
int safe;
int seekable;
int match_streams;
} ConcatContext;
static int concat_probe(AVProbeData *probe)
@ -134,6 +137,54 @@ fail:
return ret;
}
static int copy_stream_props(AVStream *st, AVStream *source_st)
{
int ret;
if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
return ret;
st->r_frame_rate = source_st->r_frame_rate;
st->avg_frame_rate = source_st->avg_frame_rate;
st->time_base = source_st->time_base;
st->sample_aspect_ratio = source_st->sample_aspect_ratio;
return 0;
}
static int match_streams(AVFormatContext *avf)
{
ConcatContext *cat = avf->priv_data;
AVStream *st;
int *map, i, j, ret;
if (!cat->match_streams ||
cat->cur_file->stream_map_size >= cat->avf->nb_streams)
return 0;
map = av_realloc(cat->cur_file->stream_map,
cat->avf->nb_streams * sizeof(*map));
if (!map)
return AVERROR(ENOMEM);
cat->cur_file->stream_map = map;
for (i = cat->cur_file->stream_map_size; i < cat->avf->nb_streams; i++) {
st = cat->avf->streams[i];
map[i] = -1;
for (j = 0; j < avf->nb_streams; j++) {
if (avf->streams[j]->id == st->id) {
av_log(avf, AV_LOG_VERBOSE,
"Match slave stream #%d with stream #%d id 0x%x\n",
i, j, st->id);
map[i] = j;
if (!avf->streams[j]->codec->codec_id && st->codec->codec_id)
if ((ret = copy_stream_props(avf->streams[j], st)) < 0)
return ret;
}
}
}
cat->cur_file->stream_map_size = cat->avf->nb_streams;
return 0;
}
static int open_file(AVFormatContext *avf, unsigned fileno)
{
ConcatContext *cat = avf->priv_data;
@ -159,6 +210,8 @@ static int open_file(AVFormatContext *avf, unsigned fileno)
file->start_time = !fileno ? 0 :
cat->files[fileno - 1].start_time +
cat->files[fileno - 1].duration;
if ((ret = match_streams(avf)) < 0)
return ret;
return 0;
}
@ -183,7 +236,7 @@ static int concat_read_header(AVFormatContext *avf)
int ret, line = 0, i;
unsigned nb_files_alloc = 0;
ConcatFile *file = NULL;
AVStream *st, *source_st;
AVStream *st;
int64_t time = 0;
while (1) {
@ -217,6 +270,17 @@ static int concat_read_header(AVFormatContext *avf)
FAIL(ret);
}
file->duration = dur;
} else if (!strcmp(keyword, "stream")) {
if (!avformat_new_stream(avf, NULL))
FAIL(AVERROR(ENOMEM));
} else if (!strcmp(keyword, "exact_stream_id")) {
if (!avf->nb_streams) {
av_log(avf, AV_LOG_ERROR, "Line %d: exact_stream_id without stream\n",
line);
FAIL(AVERROR_INVALIDDATA);
}
avf->streams[avf->nb_streams - 1]->id =
strtol(get_keyword(&cursor), NULL, 0);
} else if (!strcmp(keyword, "ffconcat")) {
char *ver_kw = get_keyword(&cursor);
char *ver_val = get_keyword(&cursor);
@ -251,18 +315,16 @@ static int concat_read_header(AVFormatContext *avf)
cat->seekable = 1;
}
cat->match_streams = !!avf->nb_streams;
if ((ret = open_file(avf, 0)) < 0)
FAIL(ret);
for (i = 0; i < cat->avf->nb_streams; i++) {
if (!(st = avformat_new_stream(avf, NULL)))
FAIL(AVERROR(ENOMEM));
source_st = cat->avf->streams[i];
if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
FAIL(ret);
st->r_frame_rate = source_st->r_frame_rate;
st->avg_frame_rate = source_st->avg_frame_rate;
st->time_base = source_st->time_base;
st->sample_aspect_ratio = source_st->sample_aspect_ratio;
if (!cat->match_streams) {
for (i = 0; i < cat->avf->nb_streams; i++) {
if (!(st = avformat_new_stream(avf, NULL)))
FAIL(AVERROR(ENOMEM));
if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)
FAIL(ret);
}
}
return 0;
@ -292,12 +354,24 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
int64_t delta;
while (1) {
if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
(ret = open_next_file(avf)) < 0)
break;
ret = av_read_frame(cat->avf, pkt);
if (ret == AVERROR_EOF) {
if ((ret = open_next_file(avf)) < 0)
return ret;
continue;
}
if (ret < 0)
return ret;
if (cat->match_streams) {
match_streams(avf);
pkt->stream_index = cat->cur_file->stream_map[pkt->stream_index];
if (pkt->stream_index < 0) {
av_packet_unref(pkt);
continue;
}
}
break;
}
if (ret < 0)
return ret;
delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
AV_TIME_BASE_Q,

127
tools/dvd2concat Executable file
View File

@ -0,0 +1,127 @@
#!/usr/bin/env perl
# Copyright (c) 2014 Nicolas George
#
# This file is part of FFmpeg.
#
# FFmpeg 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.
#
# FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
=head1 NAME
dvd2concat - create a concat script for a DVD title
=head1 SYNOPSIS
tools/dvd2concat I<path/to/dvd/structure> > I<file.concat>
=head1 DESCRIPTION
This script uses B<lsdvd> to produce concat script for a DVD title.
The resulting script can be used to play the DVD using B<ffplay>, to
transcode it using B<ffmpeg> or any other similar use.
I<path/to/dvd/structure> is the path to the DVD structure hierarchy; it
normally contains a directory named B<VIDEO_TS>. It must not be encrypted
with CSS.
I<file.concat> is the output file. It can be used a input to ffmpeg.
It will require the B<-safe 0> option.
=cut
use strict;
use warnings;
use Getopt::Long ":config" => "require_order";
use Pod::Usage;
my $title;
GetOptions (
"help|usage|?|h" => sub { pod2usage({ -verbose => 1, -exitval => 0 }) },
"manpage|m" => sub { pod2usage({ -verbose => 2, -exitval => 0 }) },
"title|t=i" => \$title,
) and @ARGV == 1 or pod2usage({ -verbose => 1, -exitval => 1 });
my ($path) = @ARGV;
my $lsdvd_message =
"Make sure your lsdvd version has the two following patches applied:\n" .
"http://sourceforge.net/p/lsdvd/feature-requests/1/\n" .
"https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=603826\n";
my $lsdvd = do {
open my $l, "-|", "lsdvd", "-Op", "-x", $path
or die "You need to install lsdvd for this script to work.\n$lsdvd_message";
local $/;
<$l>;
};
my %lsdvd = eval $lsdvd;
die $@ if $@;
if (!defined $title) {
$title = $lsdvd{longest_track};
warn "Using longest title $title\n";
}
my $track = $lsdvd{track}[$title - 1]
or die "Title $title does not exist (1-", scalar(@{$lsdvd{track}}), ")\n";
my $vts_base = sprintf "%s/VIDEO_TS/VTS_%02d_", $path, $track->{vts};
my @frag;
for my $i (1 .. 9) {
my $file = sprintf "%s%d.VOB", $vts_base, $i;
my $size = -s $file or last;
push @frag, { file => $file, size => $size >> 11 };
}
my $concat = "ffconcat version 1.0\n";
$concat .= "\nstream\nexact_stream_id 0x1E0\n";
for my $audio (@{$track->{audio}}) {
$concat .= "\nstream\nexact_stream_id " . $audio->{streamid} . "\n";
}
for my $subp (@{$track->{subp}}) {
$concat .= "\nstream\nexact_stream_id " . $subp->{streamid} . "\n";
}
for my $cell (@{$track->{cell}}) {
my $off = $cell->{first_sector};
die "Your lsdvd version does not print cell sectors.\n$lsdvd_message"
unless defined $off;
my $size = $cell->{last_sector} + 1 - $cell->{first_sector};
my $frag = 0;
while ($frag < @frag) {
last if $off < $frag[$frag]->{size};
$off -= $frag[$frag++]->{size};
}
die "Cell beyond VOB data\n" unless $frag < @frag;
my $cur_off = $off;
my $cur_size = $size;
my @files;
while ($cur_size > $frag[$frag]->{size} - $cur_off) {
push @files, $frag[$frag]->{file};
$cur_size -= $frag[$frag]->{size} - $cur_off;
$cur_off = 0;
die "Cell end beyond VOB data\n" unless ++$frag < @frag;
}
push @files, $frag[$frag]->{file};
my $file = @files == 1 ? $files[0] : "concat:" . join("|", @files);
my $start = $off << 11;
my $end = ($off + $size) << 11;
$file = "subfile,,start,${start},end,${end},,:$file";
my $dur = int(1000 * $cell->{length});
$concat .= sprintf "\nfile '%s'\nduration %02d:%02d:%02d.%03d\n", $file,
int($dur / 3600000), int($dur / 60000) % 60, int($dur / 1000) % 60,
$dur % 1000;
}
print $concat;