Skip to content

Instantly share code, notes, and snippets.

@ken39arg
Created June 3, 2014 06:44
Show Gist options
  • Select an option

  • Save ken39arg/6dd03d41916797780c8b to your computer and use it in GitHub Desktop.

Select an option

Save ken39arg/6dd03d41916797780c8b to your computer and use it in GitHub Desktop.
m3u8の持つ要素を色々調べたいときの殴り書き
use common::sense;
use Furl;
use JSON::XS;
my $FFPROBE = `which ffprobe`;
chomp $FFPROBE;
say encode_json( parse_all( shift ) );
sub parse_all {
my $file = shift;
my $data;
if ( $file =~ /\.m3u8$/ ) {
$data = parse_m3u8( read_m3u8( $file ) );
if ( $data->{ stream_inf } ) {
# master m3u8
for my $stream ( @{ $data->{ stream_inf } } ) {
$stream->{ media } = parse_all( mkurl( $stream->{ url }, $file ) );
}
} else {
# media m3u8
for my $ts ( @{ $data->{ inf } } ) {
$ts->{ info } = parse_all( mkurl( $ts->{ url }, $file ) );
}
}
} else {
$data = parse_video( $file );
}
return $data;
}
sub read_m3u8 {
my $m3u8_file = shift;
my $m3u8_data;
if ( $m3u8_file =~ /^http/ ) {
my $res = Furl->new->get( $m3u8_file );
$m3u8_data = $res->content;
} else {
open my $fh, '<', $m3u8_file;
$m3u8_data = do { local $/; <$fh> };
close $fh;
}
return $m3u8_data;
}
sub parse_m3u8 {
my $m3u8_data = shift;
my $ret = {};
while ( $m3u8_data =~ m/\#(EXT[A-Z0-9\-]+)[:\n]?([^\#]*)/gmsx ) {
my $tag = $1;
my ( $attrs, $value ) = split /\n/, $2;
if ( $tag eq 'EXT-X-STREAM-INF' ) {
my $v = {};
while ($attrs =~ m/([A-Z\-]+)=(".*?"|\w+),?/gms ) {
$v->{ lc $1 } = jformat($2);
}
$ret->{ stream_inf } ||= [];
push @{ $ret->{ stream_inf } }, {
%$v,
url => jformat($value),
};
}
elsif ( $tag eq 'EXTINF' ) {
my ( $duration, $title ) = split /,/, $attrs;
$ret->{ inf } ||= [];
push @{ $ret->{ inf } }, {
duration => jformat($duration),
url => jformat($value),
( $title ? ( title => $title ) : () ),
};
}
elsif ( $tag =~ /^EXT-X-(.*)$/ ) {
$ret->{ lc $1 } = jformat($attrs);
}
}
return $ret;
}
sub jformat($) {
my $v = shift;
$v =~ s/^["\s]+//;
$v =~ s/["\s]+$//;
if ( $v =~ /^\d+(\.\d+)?$/ ) {
return $v + 0;
}
return $v;
}
sub mkurl {
my ( $url, $base_file ) = @_;
unless ( $url =~ /^(\/|http)/ ) {
my @u = split "/", $base_file;
pop @u;
$url = join "/", @u, $url;
}
return $url;
}
sub parse_video {
my $file = shift;
my ( $format, $video, $audio ) = probe( $file );
return {
f => {
bit_rate => jformat $format->{ bit_rate },
size => jformat $format->{ size },
duration => jformat $format->{ duration },
start_time => jformat $format->{ start_time },
format_name => jformat $format->{ format_name },
},
v => {
codec_name => jformat $video->{ codec_name },
codec_tag => jformat $video->{ codec_tag },
level => jformat $video->{ level },
width => jformat $video->{ width },
height => jformat $video->{ height },
DAR => jformat $video->{ display_aspect_ratio },
SAR => jformat $video->{ sample_aspect_ratio },
duration => jformat $video->{ duration },
duration_ts => jformat $video->{ duration_ts },
},
a => {
codec_name => jformat $audio->{ codec_name },
codec_tag => jformat $audio->{ codec_tag },
duration => jformat $audio->{ duration },
duration_ts => jformat $audio->{ duration_ts },
},
};
}
sub probe {
my ( $input ) = @_;
my @ffprove_options = qw{-v quiet
-print_format json
-show_format
-show_streams};
my $command = join " ", $FFPROBE, @ffprove_options, $input;
my $probe_result = decode_json `$command`;
my ( $format, $video_stream, $audio_stream );
$format = $probe_result->{ format };
for my $stream ( @{ $probe_result->{ streams } } ) {
if ( $stream->{ codec_type } eq 'video' ) {
$video_stream = $stream;
}
elsif ( $stream->{ codec_type } eq 'audio' ) {
$audio_stream = $stream;
}
}
return ( $format, $video_stream, $audio_stream );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment