package FLV::Splice; use warnings; use strict; use 5.008; use FLV::File; use FLV::Util; use List::MoreUtils qw(any); use English qw(-no_match_vars); use Carp; use Readonly; our $VERSION = '0.24'; =for stopwords FLVs codec AVC framerates =head1 NAME FLV::Splice - Concatenate FLV files into new files =head1 ACKNOWLEDGMENTS This feature was created with financial support from John Drago (CPAN:JOHND). Thanks! =head1 LICENSE See L =head1 SYNOPSIS use FLV::Splic; my $converter = FLV::Splice->new(); $converter->add_input('first.flv'); $converter->add_input('second.flv'); $converter->save('output.flv'); =head1 DESCRIPTION Concatenates compatible FLV movies into a single file. In this context, 'compatible' means that they have the same video and audio codec. It is possible that this tool will produce unplayable movies, for example concatenating AVC content will likely fail because each segment has its own binary configuration block. This tool may also produce unplayable content if the segments have different framerates. That depends on the player implementation. =head1 METHODS =over =item $pkg->new() Instantiate a converter. =cut sub new { my $pkg = shift; my $start = { time => 0 }; my $self = bless { start => 0, nvideo => 0, naudio => 0, last_vid_time => 0, last_aud_time => 0, }, $pkg; return $self; } =item $self->add_input($flv_filename) =item $self->add_input($flv_instance) Open and append the specified FLV file. Alternatively, you may pass an instantiated and parsed L instance. =cut sub add_input ## no critic (Complexity) { my $self = shift; my $infile = shift; my $flv; if (ref $infile && $infile->isa('FLV::File')) { $flv = $infile->clone; } else { $flv = FLV::File->new; $flv->parse($infile); } if ($self->{flv}) { # add 2nd, 3rd, etc if (($self->{flv}->get_header->has_video || 0) != ($flv->get_header->has_video || 0)) { die 'One FLV has video and the other does not'; } if (($self->{flv}->get_header->has_audio || 0) != ($flv->get_header->has_audio || 0)) { die 'One FLV has audio and the other does not'; } for my $tag ($flv->get_body->get_tags) { $tag->{start} += $self->{start}; push @{$self->{flv}->get_body->{tags}}, $tag; } } else { # add 1st $self->{flv} = $flv; } # validate and count for my $tag ($flv->get_body->get_tags) { if ($tag->isa('FLV::VideoTag')) { if (!$self->{nvideo}++) { $self->{video_codec} = $tag->{codec}; } elsif ($tag->{codec} != $self->{video_codec}) { die 'FLV has inconsistent video codecs'; } $self->{last_vid_time} = $tag->{start}; } elsif ($tag->isa('FLV::AudioTag')) { if (!$self->{naudio}++) { $self->{audio_codec} = $tag->{format}; } elsif ($tag->{format} != $self->{audio_codec}) { die 'FLV has inconsistent audio codecs'; } $self->{last_aud_time} = $tag->{start}; } } $self->{one_frame} ||= 1 < $self->{nvideo} ? $self->{last_vid_time} / ($self->{nvideo} - 1) : 1 < $self->{naudio} ? $self->{last_aud_time} / ($self->{naudio} - 1) : die 'FLV has no media'; $self->{start} = ($self->{nvideo} ? $self->{last_vid_time} : $self->{last_aud_time}) + $self->{one_frame}; return; } =item $self->save($outfile) Serialize the combined FLV to file. =cut sub save { my $self = shift; my $outfile = shift; my $outfh = FLV::Util->get_write_filehandle($outfile); if (!$outfh) { die 'Failed to write FLV file: ' . $OS_ERROR; } $self->{flv}->populate_meta(); $self->{flv}->serialize($outfh); return; } 1; __END__ =back =head1 AUTHOR See L =cut