about summary refs log tree commit homepage
path: root/lib/dtas/source
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-09-01 00:07:47 +0000
committerEric Wong <normalperson@yhbt.net>2013-09-01 00:07:47 +0000
commit51180db62b4c91e2f55ee5d553a210e8effbb17f (patch)
tree9a354201c5d04f87c7f64d479b9bf8702a94d464 /lib/dtas/source
parent452629b6ea57664f46a21854a87e941f1a5db812 (diff)
downloaddtas-51180db62b4c91e2f55ee5d553a210e8effbb17f.tar.gz
Some containers (e.g. large VOBs) are not easily probed and require
additional options for avprobe/ffprobe to find audio streams.  We do
this by looping and increasing the duration/size of the probe to
find new audio streams.

This seems to work reasonably well for some DVD rips I have until
seeking is required.  This breaks if the seek point (including seeks
for source effects) exceeds the avprobe/ffprobe -analyzeduration.

Anyways, I recommend extracting the audio stream (without
transcoding) out of the VOB container as the best way to go.
Something like:

  avconv -analyzeduration 2G -probesize 2G \
    -i input.vob -vn -sn -c:a copy -map 0:$STREAM_NR output.ext
Diffstat (limited to 'lib/dtas/source')
-rw-r--r--lib/dtas/source/av.rb2
-rw-r--r--lib/dtas/source/av_ff_common.rb82
-rw-r--r--lib/dtas/source/ff.rb2
3 files changed, 64 insertions, 22 deletions
diff --git a/lib/dtas/source/av.rb b/lib/dtas/source/av.rb
index d44b1a9..e03c89d 100644
--- a/lib/dtas/source/av.rb
+++ b/lib/dtas/source/av.rb
@@ -9,7 +9,7 @@ class DTAS::Source::Av # :nodoc:
 
   AV_DEFAULTS = COMMAND_DEFAULTS.merge(
     "command" =>
-      'avconv -v error $SSPOS -i "$INFILE" $AMAP -f sox - |' \
+      'avconv -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
       'sox -p $SOXFMT - $RGFX',
 
     # this is above ffmpeg because this av is the Debian default and
diff --git a/lib/dtas/source/av_ff_common.rb b/lib/dtas/source/av_ff_common.rb
index 2ae8d69..e513217 100644
--- a/lib/dtas/source/av_ff_common.rb
+++ b/lib/dtas/source/av_ff_common.rb
@@ -25,39 +25,80 @@ module DTAS::Source::AvFfCommon # :nodoc:
     rv
   end
 
+  def __parse_astream(cmd, stream)
+    stream =~ /^codec_type=audio$/ or return
+    as = AStream.new
+    index = nil
+    stream =~ /^index=(\d+)\s*$/nm and index = $1.to_i
+    stream =~ /^duration=([\d\.]+)\s*$/nm and as.duration = $1.to_f
+    stream =~ /^channels=(\d)\s*$/nm and as.channels = $1.to_i
+    stream =~ /^sample_rate=([\d\.]+)\s*$/nm and as.rate = $1.to_i
+    index or raise "BUG: no audio index from #{xs(cmd)}"
+    yield(index, as)
+  end
+
+  def probe_ok?(status, err_str)
+    return false if Process::Status === status
+    return false if err_str =~ /Unable to find a suitable output format for/
+    true
+  end
+
   def av_ff_ok?
     @duration = nil
     @format = DTAS::Format.new
     @format.bits = 32 # always, since we still use the "sox" format
     @comments = {}
     @astreams = []
-    cmd = %W(#@av_ff_probe -show_streams -show_format #@infile)
-    err = ""
-    s = qx(@env, cmd, err_str: err, no_raise: true)
-    return false if Process::Status === s
-    return false if err =~ /Unable to find a suitable output format for/
-    s.scan(%r{^\[STREAM\]\n(.*?)\n\[/STREAM\]\n}mn) do |_|
-      stream = $1
-      if stream =~ /^codec_type=audio$/
-        as = AStream.new
-        index = nil
-        stream =~ /^index=(\d+)\s*$/nm and index = $1.to_i
-        stream =~ /^duration=([\d\.]+)\s*$/nm and as.duration = $1.to_f
-        stream =~ /^channels=(\d)\s*$/nm and as.channels = $1.to_i
-        stream =~ /^sample_rate=([\d\.]+)\s*$/nm and as.rate = $1.to_i
-        index or raise "BUG: no audio index from #{xs(cmd)}"
-
-        # some streams have zero channels
-        @astreams[index] = as if as.channels > 0 && as.rate > 0
+
+    # needed for VOB and other formats which scatter metadata all over the
+    # place and
+    @probe_harder = nil
+    incomplete = []
+    prev_cmd = []
+
+    begin # loop
+      cmd = %W(#@av_ff_probe)
+
+      # using the max known duration as a analyzeduration seems to work
+      # for the few VOBs I've tested, but seeking is still broken.
+      max_duration = 0
+      incomplete.each do |as|
+        as && as.duration or next
+        max_duration = as.duration if as.duration > max_duration
       end
-    end
+      if max_duration > 0
+        usec = max_duration.round * 1000000
+        usec = "2G" if usec >= 0x7fffffff # limited to INT_MAX :<
+        @probe_harder = %W(-analyzeduration #{usec} -probesize 2G)
+        cmd.concat(@probe_harder)
+      end
+      cmd.concat(%W(-show_streams -show_format #@infile))
+      break if cmd == prev_cmd
+
+      err = ""
+      s = qx(@env, cmd, err_str: err, no_raise: true)
+      return false unless probe_ok?(s, err)
+      s.scan(%r{^\[STREAM\]\n(.*?)\n\[/STREAM\]\n}mn) do |_|
+        __parse_astream(cmd, $1) do |index, as|
+          # incomplete streams may have zero channels
+          if as.channels > 0 && as.rate > 0
+            @astreams[index] = as
+            incomplete[index] = nil
+          else
+            incomplete[index] = as
+          end
+        end
+      end
+      prev_cmd = cmd
+    end while incomplete.compact[0]
+
     s.scan(%r{^\[FORMAT\]\n(.*?)\n\[/FORMAT\]\n}m) do |_|
       f = $1
       f =~ /^duration=([\d\.]+)\s*$/nm and @duration = $1.to_f
       # TODO: multi-line/multi-value/repeated tags
       f.gsub!(/^TAG:([^=]+)=(.*)$/ni) { |_| @comments[$1.upcase] = $2 }
     end
-    ! @astreams.empty?
+    ! @astreams.compact.empty?
   end
 
   def sspos(offset)
@@ -105,6 +146,7 @@ module DTAS::Source::AvFfCommon # :nodoc:
 
     e = @env.merge!(player_format.to_env)
 
+    e["PROBE"] = @probe_harder ? @probe_harder.join(' ') : nil
     # make sure these are visible to the source command...
     e["INFILE"] = @infile
     e["AMAP"] = amap
diff --git a/lib/dtas/source/ff.rb b/lib/dtas/source/ff.rb
index fa4bbf7..9f1cffc 100644
--- a/lib/dtas/source/ff.rb
+++ b/lib/dtas/source/ff.rb
@@ -11,7 +11,7 @@ class DTAS::Source::Ff  # :nodoc:
 
   FF_DEFAULTS = COMMAND_DEFAULTS.merge(
     "command" =>
-      'ffmpeg -v error $SSPOS -i "$INFILE" $AMAP -f sox - |' \
+      'ffmpeg -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
       'sox -p $SOXFMT - $RGFX',
 
     # I haven't tested this much since av is in Debian stable and ff is not