1# $Id: Probe.pm 2195 2006-08-18 21:43:37Z joern $
2
3#-----------------------------------------------------------------------
4# Copyright (C) 2001-2006 J�rn Reder <joern AT zyn.de>.
5# All Rights Reserved. See file COPYRIGHT for details.
6#
7# This module is part of Video::DVDRip, which is free software; you can
8# redistribute it and/or modify it under the same terms as Perl itself.
9#-----------------------------------------------------------------------
10
11package Video::DVDRip::Probe;
12use Locale::TextDomain qw (video.dvdrip);
13
14use base Video::DVDRip::Base;
15
16use Carp;
17use strict;
18
19sub width			{ shift->{width}	    		}
20sub height			{ shift->{height}	    		}
21sub aspect_ratio		{ shift->{aspect_ratio}	    		}
22sub video_mode			{ shift->{video_mode}	    		}
23sub letterboxed			{ shift->{letterboxed}	    		}
24sub frames			{ shift->{frames}			}
25sub runtime			{ shift->{runtime}			}
26sub frame_rate			{ shift->{frame_rate}			}
27sub bitrates			{ shift->{bitrates}			}	# href
28sub audio_tracks		{ shift->{audio_tracks}			}	# lref
29sub probe_output		{ shift->{probe_output}	    		}
30sub audio_probe_output		{ shift->{audio_probe_output}  		}
31sub chapters			{ shift->{chapters}	    		}
32sub viewing_angles		{ shift->{viewing_angles}		}
33
34sub set_width			{ shift->{width}		= $_[1]	}
35sub set_height			{ shift->{height}		= $_[1]	}
36sub set_aspect_ratio		{ shift->{aspect_ratio}		= $_[1]	}
37sub set_video_mode		{ shift->{video_mode}		= $_[1]	}
38sub set_letterboxed		{ shift->{letterboxed}		= $_[1]	}
39sub set_frames			{ shift->{frames}		= $_[1] }
40sub set_runtime			{ shift->{runtime}		= $_[1] }
41sub set_frame_rate		{ shift->{frame_rate}		= $_[1] }
42sub set_bitrates		{ shift->{bitrates}		= $_[1] }
43sub set_audio_tracks		{ shift->{audio_tracks}		= $_[1] }
44sub set_probe_output		{ shift->{probe_output}		= $_[1]	}
45sub set_audio_probe_output	{ shift->{audio_probe_output}	= $_[1]	}
46sub set_chapters		{ shift->{chapters}		= $_[1]	}
47sub set_viewing_angles		{ shift->{viewing_angles}	= $_[1]	}
48
49sub analyze {
50    my $class = shift;
51    my %par   = @_;
52    my ( $probe_output, $title ) = @par{ 'probe_output', 'title' };
53
54    my ( $width,  $height,  $aspect_ratio, $video_mode, $letterboxed );
55    my ( $frames, $runtime, $frame_rate,   $chapters,   $angles );
56
57    ($width)        = $probe_output =~ /frame\s+size:\s*-g\s+(\d+)/;
58    ($height)       = $probe_output =~ /frame\s+size:\s*-g\s+\d+x(\d+)/;
59    ($aspect_ratio) = $probe_output =~ /aspect\s*ratio:\s*(\d+:\d+)/;
60    ($video_mode)   = $probe_output =~ /dvd_reader.*?(pal|ntsc)/i;
61    ($letterboxed)  = $probe_output =~ /dvd_reader.*?(letterboxed)/;
62    ($frames)       = $probe_output =~ /V:\s*(\d+)\s*frames/;
63    ($runtime)      = $probe_output =~ /playback time:.*?(\d+)\s*sec/;
64    ($frame_rate)   = $probe_output =~ /frame\s+rate:\s+-f\s+([\d.]+)/;
65    ($chapters)     = $probe_output =~ /(\d+)\s+chapter/;
66    ($angles)       = $probe_output =~ /(\d+)\s+angle/;
67
68    $letterboxed = $letterboxed ? 1 : 0;
69    $video_mode  = lc($video_mode);
70
71    my ( $size, %bitrates, $bitrate );
72    while ( $probe_output =~ /CD:\s*(\d+)/g ) {
73        $size = $1;
74        ($bitrate) = $probe_output =~ /CD:\s*$size.*?\@\s*([\d.]+)\s*kbps/;
75        ( $bitrates{$size} ) = int($bitrate);
76    }
77
78    my (@audio_tracks);
79    while ( $probe_output
80        =~ /audio\s+track:\s*-a\s*(\d+).*?-e\s+(\d+),(\d+),(\d+)/g ) {
81        if ( $2 and $3 ) {
82            $audio_tracks[$1] = {
83                sample_rate  => $2,
84                sample_width => $3,
85                bitrate      => undef,    # later set by analyze_audio
86                tc_option_n  => undef,    # later set by analyze_audio
87                scan_result  => undef,    # later set by Title->scan
88            };
89        }
90    }
91
92    my $i = 0;
93    while (
94        $probe_output =~ /\(dvd_reader.c\)\s+([^\s]+)\s+(\w+).*?(\d+)Ch/g ) {
95        $audio_tracks[$i]->{type}     = lc($1);
96        $audio_tracks[$i]->{lang}     = lc($2);
97        $audio_tracks[$i]->{channels} = $3;
98        ++$i;
99    }
100
101    # Audio
102
103    my @audio_track_objects;
104
105    $i = 0;
106    foreach my $audio (@audio_tracks) {
107        push @audio_track_objects,
108            Video::DVDRip::Audio->new(
109            title           => $title,
110            type            => $audio->{type},
111            lang            => $audio->{lang},
112            channels        => $audio->{channels},
113            sample_rate     => $audio->{sample_rate},
114            tc_nr           => $i,
115            tc_target_track => ( $i == 0 ? 0 : -1 ),
116            tc_audio_codec  => "mp3",
117            tc_bitrate      => 128,
118            tc_mp3_quality  => 0,
119            tc_samplerate   => $audio->{sample_rate},
120            );
121        ++$i;
122    }
123
124    # Subtitles
125
126    my %subtitles;
127    my $sid;
128    while ( $probe_output =~ /subtitle\s+(\d+)=<([^>]+)>/g ) {
129        $sid = $1 + 0;
130        $subtitles{$sid} = Video::DVDRip::Subtitle->new(
131            id    => $sid,
132            lang  => $2,
133            title => $title,
134        );
135    }
136
137    # Chapter frame counter
138    my ( $timecode, $last_timecode );
139    for ( my $i = 2; $i <= $chapters; ++$i ) {
140        ($timecode) = ( $probe_output =~ /CHAPTER0?$i=([\d:.]+)/ );
141        next if $timecode eq '';
142        $timecode =~ /(\d+):(\d+):(\d+)\.(\d+)/;
143        $timecode = $1 * 3600 + $2 * 60 + $3 + $4 / 1000;
144        $timecode = int( $timecode * $frame_rate );
145        $title->chapter_frames->{ $i - 1 } = $timecode - $last_timecode;
146        $last_timecode = $timecode;
147    }
148
149    $title->chapter_frames->{$chapters} = $frames - $timecode if $timecode;
150
151    $title->set_width($width);
152    $title->set_height($height);
153    $title->set_aspect_ratio($aspect_ratio);
154    $title->set_video_mode($video_mode);
155    $title->set_letterboxed($letterboxed);
156    $title->set_frames($frames);
157    $title->set_runtime($runtime);
158    $title->set_frame_rate("$frame_rate");
159    $title->set_bitrates( \%bitrates );
160    $title->set_audio_tracks( \@audio_track_objects );
161    $title->set_chapters($chapters);
162    $title->set_viewing_angles($angles);
163    $title->set_dvd_probe_output($probe_output);
164    $title->set_audio_channel( @audio_tracks ? 0 : -1 );
165
166    $title->set_subtitles( \%subtitles );
167    $title->set_selected_subtitle_id(0) if defined $sid;
168
169    1;
170}
171
172sub analyze_audio {
173    my $self = shift;
174    my %par  = @_;
175    my ( $probe_output, $title ) = @par{ 'probe_output', 'title' };
176
177    $title->set_vob_probe_output($probe_output);
178
179    #-- probe audio bitrates
180    my @lines = split( /\n/, $probe_output );
181    my $nr;
182    for ( my $i = 0; $i < @lines; ++$i ) {
183        if ( $lines[$i] =~ /audio\s+track:\s+-a\s+(\d+).*?-n\s+([x0-9]+)/ ) {
184            $nr = $1;
185            next if not defined $title->audio_tracks->[$nr];
186            $title->audio_tracks->[$nr]->set_tc_option_n($2);
187            ++$i;
188            $lines[$i] =~ /bitrate\s*=(\d+)/;
189            $title->audio_tracks->[$nr]->set_bitrate($1);
190            $title->audio_tracks->[$nr]->set_tc_ac3_bitrate($1);
191        }
192    }
193
194    #-- probe frame rate (probing from DVD sometimes reports
195    #-- wrong framerates for NTSC movies, so we'll correct this here)
196    my ($frame_rate) = $probe_output =~ /frame\s+rate:\s+-f\s+([\d.]+)/;
197    $title->set_frame_rate("$frame_rate");
198
199    1;
200}
201
202sub analyze_scan {
203    my $class = shift;
204    my %par   = @_;
205    my ( $scan_output, $audio, $count )
206        = @par{ 'scan_output', 'audio', 'count' };
207
208    my ($volume_rescale);
209    ($volume_rescale) = $scan_output =~ /rescale=([\d.]+)/;
210
211    return if $volume_rescale eq '';
212
213    if ( $audio->volume_rescale > $volume_rescale || $count == 0 ) {
214        $audio->set_scan_output($scan_output);
215        $audio->set_volume_rescale($volume_rescale);
216        $audio->set_tc_volume_rescale($volume_rescale);
217    }
218
219    1;
220}
221
222sub analyze_lsdvd {
223    my $class = shift;
224    my %par   = @_;
225    my ( $probe_output, $project, $cb_title_probed )
226        = @par{ 'probe_output', 'project', 'cb_title_probed' };
227
228    $probe_output =~ s/EXECFLOW_OK//;
229    $probe_output =~ s/^our//;
230
231    my %lsdvd;
232    eval $probe_output;
233    die "Error compiling lsdvd output: $@. Output was:\n$probe_output" if $@;
234
235    my %titles;
236    $project->content->set_titles( \%titles );
237
238    foreach my $track ( @{ $lsdvd{track} } ) {
239        my $title = $titles{ $track->{ix} } = Video::DVDRip::Title->new(
240            nr      => $track->{ix},
241            project => $project,
242        );
243
244        my @audio_tracks;
245        foreach my $audio ( @{ $track->{audio} } ) {
246            push @audio_tracks,
247                Video::DVDRip::Audio->new(
248                title           => $title,
249                type            => $audio->{format},
250                lang            => $audio->{langcode},
251                channels        => $audio->{channels},
252                sample_rate     => $audio->{frequency},
253                tc_nr           => $audio->{ix} - 1,
254                tc_target_track => ( $audio->{ix} == 1 ? 0 : -1 ),
255                tc_audio_codec  => "mp3",
256                tc_bitrate      => 128,
257                tc_mp3_quality  => 0,
258                tc_samplerate   => $audio->{frequency},
259                );
260        }
261
262        my %subtitles;
263        foreach my $sub ( @{ $track->{subp} } ) {
264            my $sid = hex( $sub->{streamid} ) - 32;
265            $subtitles{$sid} = Video::DVDRip::Subtitle->new(
266                id    => $sid,
267                lang  => $sub->{langcode},
268                title => $title,
269            );
270        }
271
272        my %chapter_frames;
273        foreach my $chap ( @{ $track->{chapter} } ) {
274            $chapter_frames{ $chap->{ix} }
275                = int( $chap->{length} * $track->{fps} );
276        }
277
278        $track->{aspect} =~ s!/!:!;
279        $title->set_width( $track->{width} );
280        $title->set_height( $track->{height} );
281        $title->set_aspect_ratio( $track->{aspect} );
282        $title->set_video_mode( lc( $track->{format} ) );
283        $title->set_letterboxed( $track->{df} eq 'Letterbox' );
284        $title->set_frames( int( $track->{length} * $track->{fps} ) );
285        $title->set_runtime( int( $track->{length} + 0.5 ) );
286        $title->set_frame_rate( "$track->{fps}" );
287        $title->set_chapters( scalar( @{ $track->{chapter} } ) );
288        $title->set_viewing_angles( $track->{angles} );
289        $title->set_audio_channel( @audio_tracks ? 0 : -1 );
290
291        $title->set_audio_tracks( \@audio_tracks );
292        $title->set_subtitles( \%subtitles );
293        $title->set_chapter_frames( \%chapter_frames );
294        $title->set_selected_subtitle_id(0) if @{ $track->{subp} };
295
296        $title->suggest_transcode_options;
297
298        &$cb_title_probed($title) if $cb_title_probed;
299    }
300
301    1;
302}
303
3041;
305
306