Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ pkg
## PROJECT::SPECIFIC
script/*
tmp/*
Gemfile.lock
Gemfile.lock
*.gem
54 changes: 52 additions & 2 deletions lib/ffmpeg/encoding_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ def to_s
end

def width
self[:resolution].split("x").first.to_i rescue nil
self["resolution"].split("x").first.to_i rescue nil
end

def height
self[:resolution].split("x").last.to_i rescue nil
self["resolution"].split("x").last.to_i rescue nil
end

def reverse_resolution
return "Resolution not found" if height.nil? || width.nil?
@reverse_resolution = height.to_s + "x" + width.to_s
self["resolution"] = @reverse_resolution
end

private
Expand Down Expand Up @@ -154,6 +160,50 @@ def convert_watermark(value)
"-i #{value}"
end

#---------------------------------

def convert_cpu_used(value)
"-cpu-used #{value}"
end

def convert_quality(value)
"-deadline #{value}"
end

def convert_qmin(value)
"-qmin #{value}"
end

def convert_qmax(value)
"-qmax #{value}"
end

def convert_pass(value)
"-pass #{value}"
end

def convert_passlogfile(value)
"-passlogfile #{value}"
end

def convert_video_profile(value)
"-profile:v #{value}"
end

def convert_crf(value)
"-crf #{value}"
end

def convert_strict(value)
"-strict #{value}"
end

def convert_transpose(value)
"-vf transpose=#{value}"
end

#---------------------------------

def convert_watermark_filter(value)
case value[:position].to_s
when "LT"
Expand Down
28 changes: 21 additions & 7 deletions lib/ffmpeg/movie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class Movie
attr_reader :audio_stream, :audio_codec, :audio_bitrate, :audio_sample_rate, :audio_channels
attr_reader :container

UNSUPPORTED_CODEC_PATTERN = /^Unsupported codec with id (\d+) for input stream (\d+)$/

def initialize(path)
raise Errno::ENOENT, "the file '#{path}' does not exist" unless File.exist?(path)

Expand Down Expand Up @@ -50,9 +52,9 @@ def initialize(path)

@bitrate = metadata[:format][:bit_rate].to_i

unless video_streams.empty?
# TODO: Handle multiple video codecs (is that possible?)
video_stream = video_streams.first
# TODO: Handle multiple video codecs (is that possible?)
video_stream = video_streams.first
unless video_stream.nil?
@video_codec = video_stream[:codec_name]
@colorspace = video_stream[:pix_fmt]
@width = video_stream[:width]
Expand All @@ -76,9 +78,9 @@ def initialize(path)
end
end

unless audio_streams.empty?
# TODO: Handle multiple audio codecs
audio_stream = audio_streams.first
# TODO: Handle multiple audio codecs
audio_stream = audio_streams.first
unless audio_stream.nil?
@audio_channels = audio_stream[:channels].to_i
@audio_codec = audio_stream[:codec_name]
@audio_sample_rate = audio_stream[:sample_rate].to_i
Expand All @@ -89,12 +91,24 @@ def initialize(path)

end

unsupported_stream_ids = unsupported_streams(std_error)
nil_or_unsupported = -> (stream) { stream.nil? || unsupported_stream_ids.include?(stream[:index]) }

@invalid = true if nil_or_unsupported.(video_stream) && nil_or_unsupported.(audio_stream)
@invalid = true if metadata.key?(:error)
@invalid = true if std_error.include?("Unsupported codec")
@invalid = true if std_error.include?("is not supported")
@invalid = true if std_error.include?("could not find codec parameters")
end

def unsupported_streams(std_error)
[].tap do |stream_indices|
std_error.each_line do |line|
match = line.match(UNSUPPORTED_CODEC_PATTERN)
stream_indices << match[2].to_i if match
end
end
end

def valid?
not @invalid
end
Expand Down
15 changes: 11 additions & 4 deletions lib/ffmpeg/transcoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def initialize(movie, output_file, options = EncodingOptions.new, transcoder_opt
else
raise ArgumentError, "Unknown options format '#{options.class}', should be either EncodingOptions, Hash or String."
end

@transcoder_options = transcoder_options
@errors = []

apply_transcoder_options
apply_transpose
end

def run(&block)
Expand Down Expand Up @@ -102,19 +103,25 @@ def validate_output_file(&block)
def apply_transcoder_options
# if true runs #validate_output_file
@transcoder_options[:validate] = @transcoder_options.fetch(:validate) { true }

return if @movie.calculated_aspect_ratio.nil?
case @transcoder_options[:preserve_aspect_ratio].to_s
when "width"
new_height = @raw_options.width / @movie.calculated_aspect_ratio
new_height = new_height.ceil.even? ? new_height.ceil : new_height.floor
new_height += 1 if new_height.odd? # needed if new_height ended up with no decimals in the first place
@raw_options[:resolution] = "#{@raw_options.width}x#{new_height}"
@raw_options["resolution"] = "#{@raw_options.width}x#{new_height}"
when "height"
new_width = @raw_options.height * @movie.calculated_aspect_ratio
new_width = new_width.ceil.even? ? new_width.ceil : new_width.floor
new_width += 1 if new_width.odd?
@raw_options[:resolution] = "#{new_width}x#{@raw_options.height}"
@raw_options["resolution"] = "#{new_width}x#{@raw_options.height}"
end
end

def apply_transpose
case @raw_options["transpose"].to_i
when 1, 3
@raw_options.reverse_resolution
end
end

Expand Down
25 changes: 22 additions & 3 deletions spec/ffmpeg/movie_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ module FFMPEG
end

context "given a non movie file" do
let(:movie) do
movie = Movie.new(__FILE__)
end
let(:movie) { Movie.new(__FILE__) }

it "should not be valid" do
movie.should_not be_valid
Expand Down Expand Up @@ -109,6 +107,14 @@ module FFMPEG
end
end

context "given an ios9 mov file (with superfluous data streams)" do
let(:movie) { Movie.new("#{fixture_path}/movies/ios9.mov") }

it "should be valid" do
movie.should be_valid
end
end

context "given a broken mp4 file" do
let(:movie) { Movie.new("#{fixture_path}/movies/broken.mp4") }

Expand Down Expand Up @@ -195,6 +201,19 @@ module FFMPEG
Movie.new(__FILE__)
end

it "should be valid" do
movie.should be_valid
end
end

context "given a file with non supported audio and video" do
let(:fixture_file) { 'file_with_non_supported_audio_and_video_stdout.txt' }
let(:movie) do
fake_stderr = StringIO.new(File.read("#{fixture_path}/outputs/file_with_non_supported_audio_and_video_stderr.txt"))
Open3.stub(:popen3).and_yield(nil,fake_output,fake_stderr)
Movie.new(__FILE__)
end

it "should not be valid" do
movie.should_not be_valid
end
Expand Down
Binary file added spec/fixtures/movies/ios9.mov
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ffprobe version 2.1.1-static Copyright (c) 2007-2013 the FFmpeg developers
built on Feb 4 2014 13:01:52 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
configuration: --prefix=/home/ryan/Source/ffmpeg-static/target --extra-cflags='-I/home/ryan/Source/ffmpeg-static/target/include -static' --extra-ldflags='-L/home/ryan/Source/ffmpeg-static/target/lib -lm -static' --extra-version=static --disable-debug --disable-shared --enable-static --extra-cflags=--static --disable-ffplay --disable-ffserver --disable-doc --enable-gpl --enable-pthreads --enable-postproc --enable-gray --enable-runtime-cpudetect --disable-libfaac --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-bzlib --enable-zlib --enable-nonfree --enable-version3 --enable-libvpx --disable-devices --disable-decoder=aac
libavutil 52. 48.101 / 52. 48.101
libavcodec 55. 39.101 / 55. 39.101
libavformat 55. 19.104 / 55. 19.104
libavdevice 55. 5.100 / 55. 5.100
libavfilter 3. 90.100 / 3. 90.100
libswscale 2. 5.101 / 2. 5.101
libswresample 0. 17.104 / 0. 17.104
libpostproc 52. 3.100 / 52. 3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'bigbucksbunny_trailer_720p.mov':
Metadata:
major_brand : qt
minor_version : 537199360
compatible_brands: qt
creation_time : 2008-03-18 12:45:48
Duration: 00:00:33.00, start: 0.000000, bitrate: 4318 kb/s
Stream #0:0(eng): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720, 3945 kb/s, 25 fps, 25 tbr, 600 tbn, 1200 tbc (default)
Metadata:
creation_time : 2008-03-18 12:45:48
handler_name : Apple Alias Data Handler
Stream #0:1(eng): Audio: aac (mp4a / 0x6134706D), 48000 Hz, 5.1(side), 428 kb/s (default)
Metadata:
creation_time : 2008-03-18 12:45:48
handler_name : Apple Alias Data Handler
Unsupported codec with id 37923 for input stream 0
Unsupported codec with id 86018 for input stream 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "Main",
"codec_type": "video",
"codec_time_base": "1/1200",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1280,
"height": 720,
"has_b_frames": 0,
"sample_aspect_ratio": "0:1",
"display_aspect_ratio": "0:1",
"pix_fmt": "yuv420p",
"level": 31,
"r_frame_rate": "25/1",
"avg_frame_rate": "25/1",
"time_base": "1/600",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 19488,
"duration": "32.480000",
"bit_rate": "3945184",
"nb_frames": "812",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0
},
"tags": {
"creation_time": "2008-03-18 12:45:48",
"language": "eng",
"handler_name": "Apple Alias Data Handler"
}
},
{
"index": 1,
"codec_type": "audio",
"codec_time_base": "0/1",
"codec_tag_string": "mp4a",
"codec_tag": "0x6134706d",
"sample_rate": "48000",
"channels": 6,
"channel_layout": "5.1(side)",
"bits_per_sample": 0,
"r_frame_rate": "0/0",
"avg_frame_rate": "0/0",
"time_base": "1/48000",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 1584128,
"duration": "33.002667",
"bit_rate": "428605",
"nb_frames": "1547",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0
},
"tags": {
"creation_time": "2008-03-18 12:45:48",
"language": "eng",
"handler_name": "Apple Alias Data Handler"
}
}
],
"format": {
"filename": "bigbucksbunny_trailer_720p.mov",
"nb_streams": 2,
"nb_programs": 0,
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"format_long_name": "QuickTime / MOV",
"start_time": "0.000000",
"duration": "32.995000",
"size": "17810888",
"bit_rate": "4318445",
"probe_score": 100,
"tags": {
"major_brand": "qt ",
"minor_version": "537199360",
"compatible_brands": "qt ",
"creation_time": "2008-03-18 12:45:48"
}
}
}