Skip to content

Commit ba5ebd1

Browse files
committed
Improvements to caption extraction
This commit also includes a minor change to some testing parameters. When running the tests duration for files missing metadata checks were failing because the return was a second longer than previous. This could be from an update to mediainfo or ffmpeg that is rounding differently?
1 parent 31e5957 commit ba5ebd1

6 files changed

Lines changed: 60 additions & 13 deletions

File tree

lib/active_encode/base.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'active_encode/errors'
55
require 'active_encode/status'
66
require 'active_encode/technical_metadata'
7+
require 'active_encode/subtitle_technical_metadata'
78
require 'active_encode/input'
89
require 'active_encode/output'
910
require 'active_encode/callbacks'

lib/active_encode/engine_adapters/ffmpeg_adapter.rb

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ def create(input_url, options = {})
9292
new_encode.input.duration = fixed_duration(working_path("duration_input_metadata", new_encode.id))
9393
end
9494

95-
options[:subtitle_count] = new_encode.input.subtitle_count if new_encode.input.subtitle_count&.positive?
95+
subtitle_count = new_encode.input.subtitle_count if new_encode.input.subtitle_count&.positive?
9696

9797
new_encode.state = :running
9898
new_encode.percent_complete = 1
9999
new_encode.errors = []
100100

101101
# Run the ffmpeg command and save its pid
102-
command = ffmpeg_command(input_url, new_encode.id, options)
102+
command = ffmpeg_command(input_url, new_encode.id, options, subtitle_count)
103103
# Capture the exit status in a file in order to differentiate warning output in stderr between real process failure
104104
exit_status_file = working_path("exit_status.code", new_encode.id)
105105
command = "#{command}; echo $? > #{exit_status_file}"
@@ -264,21 +264,32 @@ def build_supplemental_outputs(encode)
264264
file.id = "#{encode.input.id}-#{File.basename(file_path)}"
265265
file.created_at = encode.created_at
266266
file.updated_at = File.mtime file_path
267+
# TODO: Add handling for label and language if they are included in stream's metadata
268+
# file.label = tech_metadata
269+
# file.language = tech_metadata
270+
271+
# Subtitle metadata has to be pulled from input because subtitle tracks
272+
# are not encoded into outputs.
273+
metadata_path = working_path("input_metadata", encode.id)
274+
subtitle_track = /(?:caption)(\d)(?:\.vtt)/.match(file_path)[1].to_i
275+
file.assign_subtitle_tech_metadata(get_subtitle_tech_metadata(metadata_path, subtitle_track))
267276

268277
files << file
269278
end
270279

271280
files
272281
end
273282

274-
def ffmpeg_command(input_url, id, opts)
283+
def ffmpeg_command(input_url, id, opts, subtitle_count = nil)
275284
sanitized_filename = ActiveEncode.sanitize_base input_url
276285
output_opt = opts[:outputs].collect do |output|
277286
file_name = "outputs/#{sanitized_filename}-#{output[:label]}.#{output[:extension]}"
278287
" #{output[:ffmpeg_opt]} #{working_path(file_name, id)}"
279288
end.join(" ")
280289

281-
supplemental_file_opt = caption_extraction_options(sanitized_filename, opts[:subtitle_count], id) if opts[:subtitle_count]&.positive?
290+
supplemental_file_opt = if opts[:extract_subtitles] && subtitle_count.present?
291+
caption_extraction_options(sanitized_filename, subtitle_count, id)
292+
end
282293

283294
header_opt = Array(opts[:headers]).map do |k, v|
284295
"#{k}: #{v}\r\n"
@@ -365,6 +376,20 @@ def get_xpath_text(doc, xpath, cast_method)
365376
doc.xpath(xpath).first&.text&.send(cast_method)
366377
end
367378

379+
def get_subtitle_tech_metadata(file_path, track_number)
380+
doc = Nokogiri::XML File.read(file_path)
381+
doc.remove_namespaces!
382+
383+
# XPath starts counting from 1, not 0
384+
{
385+
format: get_xpath_text(doc, "//track[@type='Text'][#{track_number + 1}]/Format/text()", :to_s),
386+
codec: get_xpath_text(doc, "//track[@type='Text'][#{track_number + 1}]/CodecID/text()", :to_s)
387+
# duration: ,
388+
# language: ,
389+
# label:
390+
}
391+
end
392+
368393
def fixed_duration(path)
369394
get_tech_metadata(path)[:duration]
370395
end

lib/active_encode/output.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module ActiveEncode
33
class Output
44
include Status
55
include TechnicalMetadata
6+
include SubtitleTechnicalMetadata
67

78
attr_accessor :id
89
attr_accessor :url
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# frozen_string_literal: true
2+
require 'active_support'
3+
4+
module ActiveEncode
5+
module SubtitleTechnicalMetadata
6+
extend ActiveSupport::Concern
7+
8+
included do
9+
attr_accessor :language
10+
attr_accessor :codec
11+
attr_accessor :format
12+
end
13+
14+
def assign_subtitle_tech_metadata(metadata)
15+
[:language, :codec, :format].each do |field|
16+
send("#{field}=", metadata[field]) if metadata.key?(field)
17+
end
18+
end
19+
end
20+
end

spec/integration/ffmpeg_adapter_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
let!(:work_dir) { stub_const "ActiveEncode::EngineAdapters::FfmpegAdapter::WORK_DIR", @dir }
2222
let(:file) { "file://" + Rails.root.join('..', 'spec', 'fixtures', 'fireworks.mp4').to_s }
2323
let(:created_job) do
24-
ActiveEncode::Base.create(file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: "mp4" }, { label: "high", ffmpeg_opt: "-s 1280x720", extension: "mp4" }])
24+
ActiveEncode::Base.create(file, outputs: [{ label: "low", ffmpeg_opt: "-s 640x480", extension: "mp4" }, { label: "high", ffmpeg_opt: "-s 1280x720", extension: "mp4" }], extract_subtitles: true)
2525
end
2626
let(:running_job) do
2727
allow(Process).to receive(:getpgid).and_return 8888
@@ -141,8 +141,8 @@ def find_encode(id)
141141
end
142142

143143
it "assigns the correct duration to the encode" do
144-
expect(create_without_metadata_job.input.duration).to eq 4_640
145-
expect(find_without_metadata_job.input.duration).to eq 4_640
144+
expect(create_without_metadata_job.input.duration).to eq 4_641.0
145+
expect(find_without_metadata_job.input.duration).to eq 4_641.0
146146
end
147147

148148
context 'when uri encoded' do
@@ -162,8 +162,8 @@ def find_encode(id)
162162
end
163163

164164
it "assigns the correct duration to the encode" do
165-
expect(create_without_metadata_job.input.duration).to eq 4_640
166-
expect(find_without_metadata_job.input.duration).to eq 4_640
165+
expect(create_without_metadata_job.input.duration).to eq 4_641.0
166+
expect(find_without_metadata_job.input.duration).to eq 4_641.0
167167
end
168168
end
169169
end

spec/integration/pass_through_adapter_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ def touch_fixture(id, filename)
134134
end
135135

136136
it "assigns the correct duration to the encode" do
137-
expect(create_without_metadata_job.input.duration).to eq 4_640
138-
expect(find_without_metadata_job.input.duration).to eq 4_640
137+
expect(create_without_metadata_job.input.duration).to eq 4_641.0
138+
expect(find_without_metadata_job.input.duration).to eq 4_641.0
139139
end
140140

141141
context 'when uri encoded' do
@@ -151,8 +151,8 @@ def touch_fixture(id, filename)
151151
end
152152

153153
it "assigns the correct duration to the encode" do
154-
expect(create_without_metadata_job.input.duration).to eq 4_640
155-
expect(find_without_metadata_job.input.duration).to eq 4_640
154+
expect(create_without_metadata_job.input.duration).to eq 4_641.0
155+
expect(find_without_metadata_job.input.duration).to eq 4_641.0
156156
end
157157
end
158158
end

0 commit comments

Comments
 (0)