Version in base suite: 1.0.4-3 Base version: libmedia-convert-perl_1.0.4-3 Target version: libmedia-convert-perl_1.4.0-1~bpo12+2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libm/libmedia-convert-perl/libmedia-convert-perl_1.0.4-3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libm/libmedia-convert-perl/libmedia-convert-perl_1.4.0-1~bpo12+2.dsc MANIFEST | 2 META.json | 10 - META.yml | 6 MYMETA.json | 10 - MYMETA.yml | 6 Makefile.PL | 2 debian/changelog | 69 +++++++ debian/control | 4 debian/patches/debian-changes | 50 ++++- lib/Media/Convert.pm | 2 lib/Media/Convert/Asset.pm | 259 +++++++++++++++++++++++++++--- lib/Media/Convert/Asset/PNGGen.pm | 23 ++ lib/Media/Convert/Asset/Profile/av1.pm | 31 ++- lib/Media/Convert/Asset/ProfileFactory.pm | 12 - lib/Media/Convert/AvSync.pm | 2 lib/Media/Convert/FfmpegInfo.pm | 23 ++ lib/Media/Convert/KeyframeFinder.pm | 37 ++++ lib/Media/Convert/Map.pm | 15 - lib/Media/Convert/Normalizer/Ffmpeg.pm | 10 - lib/Media/Convert/Pipe.pm | 6 scripts/mc-encode | 9 - t/av1.t | 6 t/avsync.t | 3 t/keyframes.t | 18 ++ t/normalize.t | 4 t/probe.t | 2 26 files changed, 540 insertions(+), 81 deletions(-) diff -Nru libmedia-convert-perl-1.0.4/MANIFEST libmedia-convert-perl-1.4.0/MANIFEST --- libmedia-convert-perl-1.0.4/MANIFEST 2023-02-15 08:39:08.000000000 +0000 +++ libmedia-convert-perl-1.4.0/MANIFEST 2024-12-12 18:17:14.000000000 +0000 @@ -10,6 +10,7 @@ lib/Media/Convert/AvSync.pm lib/Media/Convert/CodecMap.pm lib/Media/Convert/FfmpegInfo.pm +lib/Media/Convert/KeyframeFinder.pm lib/Media/Convert/Map.pm lib/Media/Convert/Normalizer.pm lib/Media/Convert/Normalizer/Bs1770gain.pm @@ -28,6 +29,7 @@ t/concat.t t/convert.t t/copy.t +t/keyframes.t t/normalize.t t/pnggen.t t/probe.t diff -Nru libmedia-convert-perl-1.0.4/META.json libmedia-convert-perl-1.4.0/META.json --- libmedia-convert-perl-1.0.4/META.json 2023-02-15 08:39:08.000000000 +0000 +++ libmedia-convert-perl-1.4.0/META.json 2024-12-12 18:17:14.000000000 +0000 @@ -4,7 +4,7 @@ "Wouter Verhelst " ], "dynamic_config" : 1, - "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", + "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], @@ -33,9 +33,11 @@ "runtime" : { "requires" : { "Alien::ffmpeg" : "0", + "IPC::System::Simple" : "0", "JSON::MaybeXS" : "0", "Moose" : "0", - "MooseX::Singleton" : "0" + "MooseX::Singleton" : "0", + "SemVer" : "0" } }, "test" : { @@ -52,6 +54,6 @@ "web" : "https://salsa.debian.org/wouter/media-convert" } }, - "version" : "v1.0.4", - "x_serialization_backend" : "JSON::PP version 4.07" + "version" : "v1.4.0", + "x_serialization_backend" : "JSON::PP version 4.16" } diff -Nru libmedia-convert-perl-1.0.4/META.yml libmedia-convert-perl-1.4.0/META.yml --- libmedia-convert-perl-1.0.4/META.yml 2023-02-15 08:39:08.000000000 +0000 +++ libmedia-convert-perl-1.4.0/META.yml 2024-12-12 18:17:14.000000000 +0000 @@ -8,7 +8,7 @@ configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 -generated_by: 'ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010' +generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -20,10 +20,12 @@ - inc requires: Alien::ffmpeg: '0' + IPC::System::Simple: '0' JSON::MaybeXS: '0' Moose: '0' MooseX::Singleton: '0' + SemVer: '0' resources: repository: https://salsa.debian.org/wouter/media-convert.git -version: v1.0.4 +version: v1.4.0 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -Nru libmedia-convert-perl-1.0.4/MYMETA.json libmedia-convert-perl-1.4.0/MYMETA.json --- libmedia-convert-perl-1.0.4/MYMETA.json 2023-02-15 08:33:58.000000000 +0000 +++ libmedia-convert-perl-1.4.0/MYMETA.json 2024-12-12 18:16:00.000000000 +0000 @@ -4,7 +4,7 @@ "Wouter Verhelst " ], "dynamic_config" : 0, - "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", + "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "unknown" ], @@ -33,9 +33,11 @@ "runtime" : { "requires" : { "Alien::ffmpeg" : "0", + "IPC::System::Simple" : "0", "JSON::MaybeXS" : "0", "Moose" : "0", - "MooseX::Singleton" : "0" + "MooseX::Singleton" : "0", + "SemVer" : "0" } }, "test" : { @@ -52,6 +54,6 @@ "web" : "https://salsa.debian.org/wouter/media-convert" } }, - "version" : "v1.0.4", - "x_serialization_backend" : "JSON::PP version 4.07" + "version" : "v1.4.0", + "x_serialization_backend" : "JSON::PP version 4.16" } diff -Nru libmedia-convert-perl-1.0.4/MYMETA.yml libmedia-convert-perl-1.4.0/MYMETA.yml --- libmedia-convert-perl-1.0.4/MYMETA.yml 2023-02-15 08:33:55.000000000 +0000 +++ libmedia-convert-perl-1.4.0/MYMETA.yml 2024-12-12 18:15:57.000000000 +0000 @@ -8,7 +8,7 @@ configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 -generated_by: 'ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010' +generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -20,10 +20,12 @@ - inc requires: Alien::ffmpeg: '0' + IPC::System::Simple: '0' JSON::MaybeXS: '0' Moose: '0' MooseX::Singleton: '0' + SemVer: '0' resources: repository: https://salsa.debian.org/wouter/media-convert.git -version: v1.0.4 +version: v1.4.0 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -Nru libmedia-convert-perl-1.0.4/Makefile.PL libmedia-convert-perl-1.4.0/Makefile.PL --- libmedia-convert-perl-1.0.4/Makefile.PL 2022-09-06 15:10:11.000000000 +0000 +++ libmedia-convert-perl-1.4.0/Makefile.PL 2024-04-14 12:47:47.000000000 +0000 @@ -17,6 +17,8 @@ 'JSON::MaybeXS' => 0, 'Moose' => 0, 'MooseX::Singleton' => 0, + 'SemVer' => 0, + 'IPC::System::Simple' => 0, }, EXE_FILES => [ 'scripts/mc-encode', diff -Nru libmedia-convert-perl-1.0.4/debian/changelog libmedia-convert-perl-1.4.0/debian/changelog --- libmedia-convert-perl-1.0.4/debian/changelog 2023-03-07 17:54:35.000000000 +0000 +++ libmedia-convert-perl-1.4.0/debian/changelog 2024-12-31 07:32:08.000000000 +0000 @@ -1,3 +1,72 @@ +libmedia-convert-perl (1.4.0-1~bpo12+2) bookworm-backports; urgency=medium + + * Rebuild in a stable chroot + + -- Wouter Verhelst Tue, 31 Dec 2024 09:32:08 +0200 + +libmedia-convert-perl (1.4.0-1~bpo12+1) bookworm-backports; urgency=medium + + * Rebuild for bookworm-backports. + + -- Wouter Verhelst Sun, 22 Dec 2024 15:21:54 +0200 + +libmedia-convert-perl (1.4.0-1) unstable; urgency=medium + + * New upstream version, yet again. + + -- Wouter Verhelst Thu, 12 Dec 2024 18:41:26 +0200 + +libmedia-convert-perl (1.3.0-1) unstable; urgency=medium + + * New upstream release with more ffmpeg 7 fixes + + -- Wouter Verhelst Mon, 21 Oct 2024 09:04:45 +0200 + +libmedia-convert-perl (1.2.0-1) unstable; urgency=medium + + * New upstream release. + - Fixes behavior with ffmpeg 7; Closes: #1072339. + + -- Wouter Verhelst Mon, 19 Aug 2024 10:21:48 +0200 + +libmedia-convert-perl (1.1.0-2) unstable; urgency=medium + + * Add support for input_parameters + + -- Wouter Verhelst Fri, 15 Sep 2023 14:51:49 +0530 + +libmedia-convert-perl (1.1.0-1) unstable; urgency=medium + + * New upstream release + - With support for converting synfig animations to video + + -- Wouter Verhelst Sat, 02 Sep 2023 10:17:29 +0200 + +libmedia-convert-perl (1.0.5-3) unstable; urgency=medium + + * Fix up test suite for ffmpeg 6.0. Closes: #1042431, for reals this + time. + + -- Wouter Verhelst Sat, 29 Jul 2023 19:08:21 +0200 + +libmedia-convert-perl (1.0.5-2) unstable; urgency=medium + + * debian/control: add missing libsemver-perl dependency. + Closes: #1042431. + * Media::Convert::Asset: revert accidental removal of + audio_channel_count + * Media::Convert::Asset::av1: sync docs with reality + + -- Wouter Verhelst Thu, 27 Jul 2023 09:29:39 +0200 + +libmedia-convert-perl (1.0.5-1) unstable; urgency=medium + + * Media::Convert::Asset::Profile::av1: default to using preset 4 + rather than 2, disable video bit rates, and deafult to using vorbis + rather than opus for audio (because the former is more flexible). + + -- Wouter Verhelst Wed, 26 Jul 2023 11:27:41 +0200 + libmedia-convert-perl (1.0.4-3) unstable; urgency=medium * Revert the extra -map parameter. It does not have the effect we diff -Nru libmedia-convert-perl-1.0.4/debian/control libmedia-convert-perl-1.4.0/debian/control --- libmedia-convert-perl-1.0.4/debian/control 2022-10-26 10:57:04.000000000 +0000 +++ libmedia-convert-perl-1.4.0/debian/control 2024-08-15 11:08:59.000000000 +0000 @@ -10,17 +10,21 @@ debhelper-compat (=13), ffmpeg, libextutils-depends-perl, + libipc-system-simple-perl, libjson-maybexs-perl, libmoose-perl, libmoosex-singleton-perl, + libsemver-perl, Package: libmedia-convert-perl Architecture: all Depends: ffmpeg, + libipc-system-simple-perl, libjson-maybexs-perl, libmoose-perl, libmoosex-singleton-perl, + libsemver-perl, ${misc:Depends}, ${perl:Depends}, Recommends: diff -Nru libmedia-convert-perl-1.0.4/debian/patches/debian-changes libmedia-convert-perl-1.4.0/debian/patches/debian-changes --- libmedia-convert-perl-1.0.4/debian/patches/debian-changes 2023-03-07 17:54:35.000000000 +0000 +++ libmedia-convert-perl-1.4.0/debian/patches/debian-changes 2024-12-31 07:32:08.000000000 +0000 @@ -1,12 +1,17 @@ -This is an autogenerated patch header for a single-debian-patch file. The -delta against upstream is either kept as a single patch, or maintained -in some VCS, and exported as a single patch instead of more manageable -atomic patches. +Description: Autogenerated patch header for a single-debian-patch file. + The delta against upstream is either kept as a single patch, or maintained + in some VCS, and exported as a single patch instead of more manageable + atomic patches. +Forwarded: not-needed +--- --- /dev/null -+++ libmedia-convert-perl-1.0.4/.gitlab-ci.yml -@@ -0,0 +1,55 @@ ++++ libmedia-convert-perl-1.4.0/.gitlab-ci.yml +@@ -0,0 +1,77 @@ +--- ++variables: ++ SALSA_CI_DISABLE_BUILD_PACKAGE_ANY: 1 ++ SALSA_CI_DISABLE_BUILD_PACKAGE_I386: 1 +include: +- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + @@ -22,7 +27,8 @@ + image: $CI_JOB_NAME + coverage: '/^Total.* (\d+.\d+)$/' + before_script: -+ - cpanm ExtUtils::Depends Devel::Cover TAP::Harness::JUnit ++ - if [ ! -z "$NEEDS_ARCHIVE" ]; then sed -i -e 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; fi ++ - cpanm --notest ExtUtils::Depends Devel::Cover TAP::Harness::JUnit Devel::Cover::Report::Cobertura + - apt-get update && apt-get -y --no-install-recommends install ffmpeg + - cpanm --notest --installdeps . + - perl Makefile.PL @@ -30,16 +36,21 @@ + - cover -delete + - HARNESS_PERL_SWITCHES='-MDevel::Cover' prove -v -l -s --harness TAP::Harness::JUnit + - cover ++ - cover -report cobertura + artifacts: + paths: + - cover_db + reports: + junit: junit_output.xml ++ coverage_report: ++ path: cover_db/cobertura.xml ++ coverage_format: cobertura + +perl:latest: + <<: *test + -+perl:5.24: ++perl:5.28: ++ variables: + <<: *test + +test:committed: @@ -47,7 +58,7 @@ + image: perl:latest + before_script: + - apt-get update && apt-get -y --no-install-recommends install git ffmpeg -+ - cpanm ExtUtils::Depends ++ - cpanm --notest ExtUtils::Depends + - cpanm --notest --installdeps . + - perl Makefile.PL + script: @@ -58,12 +69,25 @@ + stage: upstream + image: perl:latest + before_script: -+ - cpanm Perl::Critic ++ - cpanm --notest Perl::Critic + script: + - perlcritic . ---- libmedia-convert-perl-1.0.4.orig/MANIFEST -+++ libmedia-convert-perl-1.0.4/MANIFEST -@@ -37,5 +37,3 @@ t/script.t ++ ++dput: ++ stage: publish ++ image: debian:stable ++ dependencies: ++ - build ++ allow_failure: true ++ before_script: ++ - apt-get update ++ - apt-get -y install dput-ng curl ++ - echo -e "[gitlab]\nmethod=https\nfqdn=https://gitlab-runner:$CI_JOB_TOKEN@$CI_SERVER_HOST\nincoming=/api/v4/projects/$CI_PROJECT_ID/packages/debian\n" > dput.cf ++ script: ++ - dput --config=dput.cf --unchecked --no-upload-log gitlab debian/output/*.changes +--- libmedia-convert-perl-1.4.0.orig/MANIFEST ++++ libmedia-convert-perl-1.4.0/MANIFEST +@@ -39,5 +39,3 @@ t/script.t t/testvids/bbb.mp4 t/testvids/m-c.png t/testvids/m-c.svg diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/PNGGen.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/PNGGen.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/PNGGen.pm 2022-08-22 14:19:56.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/PNGGen.pm 2023-08-21 13:48:35.000000000 +0000 @@ -45,6 +45,22 @@ default => "mono", ); +=head2 loop + +If truthy, loops over the PNG files given. Useful if there is only one +PNG file, but then a video length for the output file should be given. +If falsy, stops after all the input files are processed. + +Defaults to true. + +=cut + +has "loop" => ( + is => "rw", + isa => "Bool", + default => 1, +); + =head1 BUGS This module does not work correctly with L. If you @@ -61,7 +77,12 @@ carp "Video resolution does not match image resolution. Will scale, but the result may be suboptimal..."; } - return ('-loop', '1', '-framerate', $output->video_framerate, '-i', $self->url, '-f', 'lavfi', '-i', 'anullsrc=channel_layout=' . $self->audio_channels); + my @rv; + if($self->loop) { + push @rv, ("-loop", '1'); + } + push @rv, ('-framerate', $output->video_framerate, '-i', $self->url, '-f', 'lavfi', '-i', 'anullsrc=channel_layout=' . $self->audio_channels); + return @rv; } no Moose; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/Profile/av1.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/Profile/av1.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/Profile/av1.pm 2022-11-02 07:10:00.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/Profile/av1.pm 2023-07-29 17:07:25.000000000 +0000 @@ -9,7 +9,7 @@ =head1 NAME -Media::Convert::Asset::Profile::av1 - Create an H.264/AAC video +Media::Convert::Asset::Profile::av1 - Create an av1/vorbis video =head1 SYNOPSIS @@ -24,7 +24,7 @@ =head1 DESCRIPTION -The C profile re-encodes the input video into AV1 with Opus audio. +The C profile re-encodes the input video into AV1 with Vorbis audio. =cut @@ -36,6 +36,8 @@ my $codecs = Media::Convert::FfmpegInfo->instance->codecs; die "av1 not supported by ffmpeg" unless exists($codecs->{av1}); if($codecs->{av1}{description} =~ /libsvtav1/) { + my $version = Media::Convert::FfmpegInfo->instance->version; + die "svtav1 support immature by supplied version of ffmpeg" unless $version >= "5.1.0"; return "libsvtav1"; } elsif ($codecs->{av1}{description} =~ /libaom/) { print STDERR "Warning: libsvtav1 not supported by ffmpeg. Trying libaom-av1 instead, which will probably be too slow...\n"; @@ -46,6 +48,10 @@ } } +sub _probe_pix_fmt { + return "yuv420p10le"; +} + sub _probe_audiocodec { return "vorbis"; } @@ -55,10 +61,23 @@ } sub _probe_video_preset { - if(shift->video_height > 1080) { - return 5; - } - return 1; + return 4; +} + +sub _probe_videobitrate { + return; +} + +sub _probe_videominrate { + return; +} + +sub _probe_videomaxrate { + return; +} + +sub _probe_extra_params { + return { "svtav1-params" => "tune=0:film-grain=8" }; } 1; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/ProfileFactory.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/ProfileFactory.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset/ProfileFactory.pm 2023-02-14 07:59:06.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset/ProfileFactory.pm 2024-02-15 07:48:46.000000000 +0000 @@ -335,6 +335,8 @@ package Media::Convert::Asset::ProfileFactory; +use Module::Runtime qw/require_module/; + sub create { my $class = shift; my $profile = shift; @@ -346,16 +348,12 @@ } if(!exists($profiles->{$profile})) { - eval "require Media::Convert::Asset::Profile::$profile;"; ## no critic(StringyEval) - + require_module("Media::Convert::Asset::Profile::$profile"); return "Media::Convert::Asset::Profile::$profile"->new(url => '', reference => $ref); } else { my $parent = $profiles->{$profile}{parent}; - eval "require Media::Convert::Asset::Profile::$parent;"; ## no critic(StringyEval) - my $rv = "Media::Convert::Asset::Profile::$parent"->new(url => '', reference => $ref); - foreach my $param(keys %{$profiles->{$profile}{settings}}) { - $rv->meta->find_attribute_by_name($param)->set_value($rv, $profiles->{$profile}{settings}{$param}); - } + require_module("Media::Convert::Asset::Profile::$parent"); + my $rv = "Media::Convert::Asset::Profile::$parent"->new(url => '', reference => $ref, %{$profiles->{$profile}{settings}}); return $rv; } die "Unknown profile $profile requested!"; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Asset.pm 2023-02-14 07:51:31.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Asset.pm 2024-12-01 21:00:57.000000000 +0000 @@ -7,6 +7,9 @@ our $VERSION; use Media::Convert; +use Carp; + +use autodie qw/:all/; =head1 NAME @@ -128,9 +131,52 @@ return $statdata[9]; } +=head2 canonical_duration + +Which value to take as the canonical duration while probing the duration +of a media file. + +Unfortunately, some versions of ffprobe have bugs in that they either +read or write corrupt values for some parts of a media file. Sometimes +the video stream length is correct but the container length is not, +sometimes it is the other way around. + +Since, given that, it is not possible to choose a source that will +always work as the probed value for the L attribute, this +needs to be configurable in some way. + +This option can take the following values: + +=over 4 + +=item container + +Uses the duration value that is parsed from the container file, rather +than any particular stream. If the parsing of that value is not buggy, +this is the right thing to do, and therefore this is also the default. + +=item video + +Uses the duration value that is parsed from the first video stream. + +=item audio + +Uses the duration value that is parsed from the first audio stream. + +=back + +=cut + +has 'canonical_duration' => ( + is => 'ro', + isa => 'Str', + default => 'container', +); + =head2 duration -The duration of this asset. +The duration of this asset. See C for details on how +it is probed. =cut @@ -145,7 +191,36 @@ if($self->has_reference) { return $self->reference->duration; } - return $self->_get_probedata->{format}{duration}; + if($self->duration_style eq "seconds") { + if($self->canonical_duration eq 'container') { + return $self->_get_probedata->{format}{duration}; + } elsif($self->canonical_duration eq 'video') { + return $self->_get_videodata->{duration}; + } elsif($self->canonical_duration eq 'audio') { + return $self->_get_audiodata->{duration}; + } else { + # don't support this + ... + } + } else { + return $self->duration_frames; + } +} + +=head2 duration_frames + +The number of frames in this asset. + +=cut + +has 'duration_frames' => ( + is => 'rw', + builder => '_probe_duration_frames', + lazy => 1, +); + +sub _probe_duration_frames { + return shift->_get_videodata->{duration_ts}; } =head2 duration_style @@ -153,13 +228,44 @@ The time unit is used for the C attribute. One of 'seconds' (default) or 'frames'. Will not be probed. +Deprecated; will be removed in a future release. Use duration_frames +instead of setting this to a non-default value. + =cut +sub _warn_duration { + carp "setting duration_style is deprecated. Rather use duration_frames." +} + has 'duration_style' => ( is => 'rw', default => 'seconds', + trigger => \&_warn_duration, ); +=head2 force_key_frames + +Can be set to an expression that can be passed to ffmpeg's C<-force_key_frames> +parameter. Will not be probed. + +=cut + +has 'force_key_frames' => ( + is => 'rw', + isa => 'Maybe[Str]', + builder => '_probe_force_key_frames', + lazy => 1, + predicate => 'has_force_key_frames', +); + +sub _probe_force_key_frames { + my $self = shift; + if($self->has_reference) { + return $self->reference->force_key_frames; + } + return; +} + =head2 video_codec The codec in use for the video stream. Note that C will @@ -622,24 +728,50 @@ sub _probe_blackspots { my $self = shift; my $blacks = []; - pipe R, W; + pipe my $R, my $W; if(fork == 0) { - open STDERR, ">", "&W"; - open STDOUT, ">", "&W"; + open STDERR, ">", "$W"; + open STDOUT, ">", "$W"; my @cmd = ("ffmpeg", "-threads", "1", "-nostats", "-i", $self->url, "-vf", "blackdetect=d=0:pix_th=.01", "-f", "null", "/dev/null"); exec @cmd; die "exec failed"; } - close W; - while() { + close $W; + while(<$R>) { if(/blackdetect.*black_start:(?[\d\.]+)\sblack_end:(?[\d\.]+)\sblack_duration:(?[\d\.]+)/) { push @$blacks, { %+ }; } } - close(R); + close($R); return $blacks; } + +=head2 channel_layouts + +Returns an array of audio channel layouts, as detected by ffprobe. + +=cut + +has 'channel_layouts' => ( + is => 'rw', + traits => ['Array'], + isa => 'ArrayRef[Str]', + builder => '_probe_channel_layouts', + lazy => 1, +); + +sub _probe_channel_layouts { + my $self = shift; + my $rv = []; + foreach my $stream(@{$self->_get_probedata->{streams}}) { + if($stream->{codec_type} eq "audio") { + push @$rv, $stream->{channel_layout}; + } + } + return $rv; +} + =head2 astream_ids Returns an array with the IDs for the audio streams in this file. @@ -663,13 +795,34 @@ sub _probe_astream_ids { my $self = shift; - my @rv; + my $rv = []; foreach my $stream(@{$self->_get_probedata->{streams}}) { if($stream->{codec_type} eq "audio") { - push @rv, $stream->{index}; + push @$rv, $stream->{index}; } } - return \@rv; + return $rv; +} + +=head2 audio_channel_count + +Returns the number of channels in the first audio stream + +=cut + +has 'audio_channel_count' => ( + is => 'rw', + isa => 'Int', + lazy => 1, + builder => '_probe_audio_channel_count', +); + +sub _probe_audio_channel_count { + my $self = shift; + if($self->has_reference) { + return $self->reference->audio_channel_count; + } + return $self->_get_audiodata->{channels}; } =head2 vstream_id @@ -692,9 +845,23 @@ =head2 extra_params -Add extra parameters. This should be used sparingly, rather add some +Add extra (output) parameters. This should be used sparingly, rather add some abstraction. +=head3 handles + +=over + +=item * + +add_param (adds an extra parameter) + +=item * + +drop_param (delete a parameter) + +=back + =cut has 'extra_params' => ( @@ -717,6 +884,47 @@ return {}; } +=head2 input_params + +Add extra input parameters. This should be used sparingly, rather add +some abstraction. + +=head3 handles + +=over + +=item * + +add_input_param (adds an extra parameter) + +=item * + +drop_input_param (delete a parameter) + +=back + +=cut + +has 'input_params' => ( + traits => ['Hash'], + isa => 'HashRef[Str]', + is => 'ro', + handles => { + add_input_param => 'set', + drop_input_param => 'delete', + }, + builder => "_probe_input_params", + lazy => 1, +); + +sub _probe_input_params { + my $self = shift; + if($self->has_reference) { + return $self->reference->input_params; + } + return {}; +} + =head2 time_offset Apply an input time offset to this video (only valid when used as an @@ -766,6 +974,13 @@ if($self->has_time_offset) { push @opts, ("-itsoffset", $self->time_offset); } + + if(scalar(keys(%{$self->input_params})) > 0) { + foreach my $param(keys %{$self->input_params}) { + push @opts, ("-$param", $self->input_params->{$param}); + } + } + push @opts, ("-i", $self->url); return @opts; } @@ -795,6 +1010,9 @@ if(defined($self->video_preset)) { push @opts, ('-preset', $self->video_preset); } + if(defined($self->force_key_frames)) { + push @opts, ('-force_key_frames', $self->force_key_frames); + } if($self->has_pass) { push @opts, ('-pass', $self->pass, '-passlogfile', $self->url . '-multipass'); } @@ -819,6 +1037,8 @@ } else { push @opts, ('-frames:v', $self->duration); } + } elsif(defined($self->duration_frames)) { + push @opts, ('-frames:v', $self->duration_frames); } if(defined($self->pix_fmt)) { push @opts, ('-pix_fmt', $self->pix_fmt); @@ -854,13 +1074,16 @@ if($self->has_reference) { return $self->reference->_get_probedata; } - open my $jsonpipe, "-|:encoding(UTF-8)", "ffprobe", "-loglevel", "quiet", "-print_format", "json", "-show_format", "-show_streams", $self->url; - my $json = ""; - while(<$jsonpipe>) { - $json .= $_; + if(-e $self->url) { + open my $jsonpipe, "-|:encoding(UTF-8)", "ffprobe", "-loglevel", "quiet", "-print_format", "json", "-show_format", "-show_streams", $self->url; + my $json = ""; + while(<$jsonpipe>) { + $json .= $_; + } + close $jsonpipe; + return decode_json($json); } - close $jsonpipe; - return decode_json($json); + return {}; } sub _probe_audiodata { diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/AvSync.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/AvSync.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/AvSync.pm 2022-08-22 14:19:56.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/AvSync.pm 2023-09-16 05:29:55.000000000 +0000 @@ -79,7 +79,7 @@ sub run() { my $self = shift; - my $tempdir = tempdir("avsXXXXXX", CLEANUP => 1); + my $tempdir = tempdir("avsXXXXXX", CLEANUP => 1, TMPDIR => 1); if($self->audio_delay == 0) { # Why are we here?? Media::Convert::Pipe->new(inputs => [$self->input], output => $self->output)->run(); diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/FfmpegInfo.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/FfmpegInfo.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/FfmpegInfo.pm 2023-02-14 07:53:55.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/FfmpegInfo.pm 2024-01-03 15:41:10.000000000 +0000 @@ -1,6 +1,8 @@ package Media::Convert::FfmpegInfo; use MooseX::Singleton; +use SemVer; +use JSON::MaybeXS; has 'codecs' => ( is => 'ro', @@ -43,4 +45,25 @@ return $rv; } +has version => ( + is => 'ro', + isa => 'SemVer', + lazy => 1, + builder => '_build_version', +); + +sub _build_version { + open my $ffprobe, "-|", "ffprobe -loglevel quiet -show_program_version -print_format json"; + local $/ = undef; + my $json = decode_json(<$ffprobe>); + my $version = $json->{program_version}{version}; + if($version =~ /([0-9.]+)/) { + my $ver = $1; + while(scalar(split /\./, $ver) < 3) { + $ver .= ".0"; + } + return SemVer->new($ver); + } +} + 1; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/KeyframeFinder.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/KeyframeFinder.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/KeyframeFinder.pm 1970-01-01 00:00:00.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/KeyframeFinder.pm 2024-04-14 09:01:39.000000000 +0000 @@ -0,0 +1,37 @@ +package Media::Convert::KeyframeFinder; + +use Moose; + +use JSON::MaybeXS qw/decode_json/; +use autodie qw/:all/; + +has 'asset' => ( + is => 'ro', + isa => 'Media::Convert::Asset', + required => 1, +); + +has 'keyframes' => ( + is => 'ro', + isa => 'ArrayRef[Num]', + lazy => 1, + builder => '_build_keyframes', +); + +sub _build_keyframes { + my $self = shift; + + local $/ = ""; + open my $jsonpipe, "-|:encoding(UTF-8)", "ffprobe", "-loglevel", "quiet", "-print_format", "json", "-select_streams", "v", "-skip_frame", "nokey", "-show_frames", "-show_entries", "frame=pts_time,pict_type,best_effort_timestamp_time", $self->asset->url; + my $data = decode_json(<$jsonpipe>); + my $rv = []; + foreach my $frame(@{$data->{frames}}) { + next unless $frame->{pict_type} eq "I"; + push @$rv, ($frame->{pts_time} // $frame->{best_effort_timestamp_time}) + 0; + } + return $rv; +} + +no Moose; + +1; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Map.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Map.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Map.pm 2023-02-14 07:53:25.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Map.pm 2024-12-01 21:00:57.000000000 +0000 @@ -185,15 +185,14 @@ if($self->choice eq "both") { return ('-ac', '1'); } + my $channel_layout = $self->input->channel_layouts->[0]; + if($self->choice ne "left" && $self->choice ne "right") { + # not supported + croak("Invalid audio channel choice"); + } $stream_id = $self->input->astream_id; - if($self->choice eq "left") { - return ('-map_channel', "$index.$stream_id.0"); - } elsif($self->choice eq "right") { - return ('-map_channel', "$index.$stream_id.1"); - } else { - # other choices exist?!? - ... - } + my $choice = $self->choice eq "left" ? 1 : 2; + return ("-filter_complex", "[$index:$stream_id]channelsplit=channel_layout=" . $channel_layout . ":channels=${choice}" . "[out]", "-map", "[out]"); } elsif($self->type eq "stream") { if($self->choice eq 'audio') { return ('-map', "$index:a"); diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Normalizer/Ffmpeg.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Normalizer/Ffmpeg.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Normalizer/Ffmpeg.pm 2022-08-22 14:19:56.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Normalizer/Ffmpeg.pm 2024-08-15 07:57:19.000000000 +0000 @@ -47,13 +47,15 @@ my $json = ""; my $reading_json = 0; while(<$ffmpeg>) { + if(/{/) { + $reading_json++; + } if($reading_json) { $json .= $_; - next; - } - if(/Parsed_loudnorm/) { - $reading_json = 1; } + if(/}/) { + $reading_json--; + } } $json = decode_json($json); diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert/Pipe.pm libmedia-convert-perl-1.4.0/lib/Media/Convert/Pipe.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert/Pipe.pm 2023-02-14 08:25:53.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert/Pipe.pm 2024-08-19 08:02:09.000000000 +0000 @@ -4,6 +4,8 @@ use Moose; use Carp; +use autodie qw/:all/; + =head1 NAME Media::Convert::Pipe - class to generate ffmpeg command lines with Media::Convert::Asset @@ -173,7 +175,7 @@ Boolean. If true, the C method performs a two-pass encode, rather than a single-pass encode. -Two-pass encodes will generate a better end result, but require more +Two-pass encodes may generate a better end result, but require more time to perform. =cut @@ -248,6 +250,8 @@ /^(\w+)=(.*)$/; $vals{$1} = $2; if($1 eq 'progress') { + next if $vals{out_time_ms} eq "N/A"; + next if $length eq "N/A"; my $perc = int($vals{out_time_ms} / $length * 100); if($vals{progress} eq 'end') { $perc = 100; diff -Nru libmedia-convert-perl-1.0.4/lib/Media/Convert.pm libmedia-convert-perl-1.4.0/lib/Media/Convert.pm --- libmedia-convert-perl-1.0.4/lib/Media/Convert.pm 2023-02-15 08:28:31.000000000 +0000 +++ libmedia-convert-perl-1.4.0/lib/Media/Convert.pm 2024-12-12 18:10:34.000000000 +0000 @@ -4,7 +4,7 @@ use warnings; our $VERSION; -$VERSION = "1.0.4"; +$VERSION = "1.4.0"; =head1 NAME diff -Nru libmedia-convert-perl-1.0.4/scripts/mc-encode libmedia-convert-perl-1.4.0/scripts/mc-encode --- libmedia-convert-perl-1.0.4/scripts/mc-encode 2022-11-02 06:37:16.000000000 +0000 +++ libmedia-convert-perl-1.4.0/scripts/mc-encode 2024-02-15 07:48:46.000000000 +0000 @@ -19,6 +19,7 @@ use File::Basename qw/dirname basename/; use Getopt::Long; use Data::Dumper; +use JSON::MaybeXS; my $inputname; my $outputname; @@ -79,8 +80,14 @@ print "$outputname: $perc\%\r"; } +my $profiles = {}; + +if(exists($ENV{MC_EXTRA_PROFILES})) { + $profiles = decode_json($ENV{MC_EXTRA_PROFILES}); +} + my $input = Media::Convert::Asset->new(url => $inputname); -my $profile = Media::Convert::Asset::ProfileFactory->create($profilename, $input); +my $profile = Media::Convert::Asset::ProfileFactory->create($profilename, $input, $profiles); if(!defined($outputname)) { $outputname = $inputname; $outputname =~ s/\.[^\.]*$//g; diff -Nru libmedia-convert-perl-1.0.4/t/av1.t libmedia-convert-perl-1.4.0/t/av1.t --- libmedia-convert-perl-1.0.4/t/av1.t 2022-10-17 19:10:38.000000000 +0000 +++ libmedia-convert-perl-1.4.0/t/av1.t 2023-07-21 13:56:44.000000000 +0000 @@ -16,18 +16,18 @@ my $input = Media::Convert::Asset->new(url => 't/testvids/bbb.mp4'); my $profile = Media::Convert::Asset::ProfileFactory->create("av1", $input); - my $output = Media::Convert::Asset->new(url => './1sec.webm', duration => 0.1, reference => $profile); + my $output = Media::Convert::Asset->new(url => './1sec.webm', duration => 1, reference => $profile); ok(defined($input), "Creating an input asset is possible"); ok(defined($output), "Creating an output asset is possible"); Media::Convert::Pipe->new(inputs => [$input], output => $output)->run(); - ok(-f $output->url, "Creating a 0.1 second AV1 file is possible"); + ok(-f $output->url, "Creating a 1 second AV1 file is possible"); my $check = Media::Convert::Asset->new(url => $output->url); ok($check->video_codec eq "av1", "The output video has the correct codec"); - ok($check->duration < 0.15, "The output video has approximately the correct length"); + ok($check->duration < 1.1 && $check->duration > 0.9, "The output video has approximately the correct length"); unlink($check->url); } diff -Nru libmedia-convert-perl-1.0.4/t/avsync.t libmedia-convert-perl-1.4.0/t/avsync.t --- libmedia-convert-perl-1.0.4/t/avsync.t 2022-09-06 13:32:21.000000000 +0000 +++ libmedia-convert-perl-1.4.0/t/avsync.t 2023-07-29 17:07:25.000000000 +0000 @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 15; +use Test::More tests => 14; use_ok("Media::Convert::Asset"); use_ok("Media::Convert::AvSync"); @@ -27,5 +27,4 @@ ok($check->video_size eq $input->video_size, "The video was generated with the correct output size"); ok($check->video_codec eq $input->video_codec, "The video was copied correctly"); ok($check->audio_codec eq $input->audio_codec, "The audio was copied correctly"); -ok($check->duration < $input->duration, "The output duration is shorter than the input duration"); unlink($check->url); diff -Nru libmedia-convert-perl-1.0.4/t/keyframes.t libmedia-convert-perl-1.4.0/t/keyframes.t --- libmedia-convert-perl-1.0.4/t/keyframes.t 1970-01-01 00:00:00.000000000 +0000 +++ libmedia-convert-perl-1.4.0/t/keyframes.t 2024-04-15 07:39:11.000000000 +0000 @@ -0,0 +1,18 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use Test::More; + +use_ok('Media::Convert::Asset'); +use_ok('Media::Convert::KeyframeFinder'); + +my $asset = Media::Convert::Asset->new(url => 't/testvids/bbb.mp4'); + +my $finder = Media::Convert::KeyframeFinder->new(asset => $asset); +my $keyframes = $finder->keyframes; +my $expected_keyframes = [0.000000, 3.166667, 4.708333, 15.125000, 17.875000]; +is_deeply($keyframes, $expected_keyframes, "key frames are what we expect"); + +done_testing diff -Nru libmedia-convert-perl-1.0.4/t/normalize.t libmedia-convert-perl-1.4.0/t/normalize.t --- libmedia-convert-perl-1.0.4/t/normalize.t 2022-08-22 14:19:56.000000000 +0000 +++ libmedia-convert-perl-1.4.0/t/normalize.t 2023-09-01 18:36:02.000000000 +0000 @@ -7,14 +7,14 @@ use_ok("Media::Convert::Asset"); use_ok("Media::Convert::Normalizer"); -my $input = Media::Convert::Asset->new(url => "t/testvids/bbb.mp4", duration_style => "frames"); +my $input = Media::Convert::Asset->new(url => "t/testvids/bbb.mp4"); isa_ok($input, "Media::Convert::Asset"); my $output = Media::Convert::Asset->new(url => "./test.mkv"); isa_ok($output, "Media::Convert::Asset"); my $norm = Media::Convert::Normalizer->new(input => $input, output => $output); isa_ok($norm, 'Media::Convert::Normalizer'); $norm->run(); -my $check = Media::Convert::Asset->new(url => $output->url, duration_style => "frames"); +my $check = Media::Convert::Asset->new(url => $output->url); ok($check->video_size eq $input->video_size, "The video was generated with the correct output size"); ok($check->video_codec eq $input->video_codec, "The video was copied correctly"); ok($check->audio_codec eq $input->audio_codec, "The audio was encoded correctly"); diff -Nru libmedia-convert-perl-1.0.4/t/probe.t libmedia-convert-perl-1.4.0/t/probe.t --- libmedia-convert-perl-1.0.4/t/probe.t 2022-09-06 13:33:03.000000000 +0000 +++ libmedia-convert-perl-1.4.0/t/probe.t 2023-07-29 17:07:25.000000000 +0000 @@ -8,7 +8,7 @@ my $vid = Media::Convert::Asset->new(url => 't/testvids/bbb.mp4'); isa_ok($vid, 'Media::Convert::Asset'); -ok($vid->duration == 20.024000, 'video duration probed correctly'); +ok(($vid->duration <= 20.024000) && ($vid->duration >= 20), 'video duration probed correctly'); ok($vid->video_codec eq 'h264', 'video codec probed correctly'); ok($vid->audio_codec eq 'aac', 'audio codec probed correctly'); ok($vid->video_size eq '854x480', 'video resolution probed correctly');