active_encode 0.8.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +26 -17
  3. data/.rubocop.yml +7 -3
  4. data/.rubocop_todo.yml +8 -1
  5. data/CONTRIBUTING.md +42 -12
  6. data/Gemfile +11 -11
  7. data/README.md +64 -10
  8. data/active_encode.gemspec +2 -4
  9. data/app/controllers/active_encode/encode_record_controller.rb +1 -1
  10. data/app/jobs/active_encode/polling_job.rb +1 -1
  11. data/app/models/active_encode/encode_record.rb +1 -1
  12. data/guides/media_convert_adapter.md +208 -0
  13. data/lib/active_encode/base.rb +1 -1
  14. data/lib/active_encode/core.rb +14 -14
  15. data/lib/active_encode/engine_adapter.rb +13 -13
  16. data/lib/active_encode/engine_adapters/elastic_transcoder_adapter.rb +158 -158
  17. data/lib/active_encode/engine_adapters/ffmpeg_adapter.rb +14 -3
  18. data/lib/active_encode/engine_adapters/matterhorn_adapter.rb +204 -202
  19. data/lib/active_encode/engine_adapters/media_convert_adapter.rb +421 -217
  20. data/lib/active_encode/engine_adapters/media_convert_output.rb +67 -5
  21. data/lib/active_encode/engine_adapters/pass_through_adapter.rb +3 -3
  22. data/lib/active_encode/engine_adapters/zencoder_adapter.rb +114 -114
  23. data/lib/active_encode/errors.rb +1 -1
  24. data/lib/active_encode/persistence.rb +19 -19
  25. data/lib/active_encode/version.rb +1 -1
  26. data/lib/file_locator.rb +6 -6
  27. data/spec/fixtures/ffmpeg/cancelled-id/exit_status.code +1 -0
  28. data/spec/fixtures/ffmpeg/completed-id/exit_status.code +1 -0
  29. data/spec/fixtures/ffmpeg/completed-with-warnings-id/error.log +3 -0
  30. data/spec/fixtures/ffmpeg/completed-with-warnings-id/exit_status.code +1 -0
  31. data/spec/fixtures/ffmpeg/completed-with-warnings-id/input_metadata +102 -0
  32. data/spec/fixtures/ffmpeg/completed-with-warnings-id/output_metadata-high +90 -0
  33. data/spec/fixtures/ffmpeg/completed-with-warnings-id/output_metadata-low +90 -0
  34. data/spec/fixtures/ffmpeg/completed-with-warnings-id/pid +1 -0
  35. data/spec/fixtures/ffmpeg/completed-with-warnings-id/progress +11 -0
  36. data/spec/fixtures/ffmpeg/completed-with-warnings-id/video-high.mp4 +0 -0
  37. data/spec/fixtures/ffmpeg/completed-with-warnings-id/video-low.mp4 +0 -0
  38. data/spec/fixtures/ffmpeg/failed-id/exit_status.code +1 -0
  39. data/spec/integration/ffmpeg_adapter_spec.rb +50 -1
  40. data/spec/integration/matterhorn_adapter_spec.rb +1 -2
  41. data/spec/integration/media_convert_adapter_spec.rb +91 -0
  42. data/spec/integration/pass_through_adapter_spec.rb +2 -2
  43. data/spec/integration/zencoder_adapter_spec.rb +3 -3
  44. data/spec/units/core_spec.rb +1 -1
  45. data/spec/units/file_locator_spec.rb +3 -3
  46. data/spec/units/status_spec.rb +1 -1
  47. metadata +50 -19
@@ -30,13 +30,32 @@ module ActiveEncode
30
30
  "XAVC" => :xavc_settings
31
31
  }.freeze
32
32
 
33
- def tech_metadata(settings, output)
34
- url = output.dig('outputFilePaths', 0)
33
+ # @param output_url [String] url, expected to be `s3://`
34
+ # @param output_settings [Aws::MediaConvert::Types::Output]
35
+ # @param output_detail_settings [Aws::MediaConvert::Types::OutputDetail]
36
+ def tech_metadata_from_settings(output_url:, output_settings:, output_detail_settings:)
35
37
  {
36
- width: output.dig('videoDetails', 'widthInPx'),
37
- height: output.dig('videoDetails', 'heightInPx'),
38
+ width: output_detail_settings.video_details.width_in_px,
39
+ height: output_detail_settings.video_details.height_in_px,
40
+ frame_rate: extract_video_frame_rate(output_settings),
41
+ duration: output_detail_settings.duration_in_ms,
42
+ audio_codec: extract_audio_codec(output_settings),
43
+ video_codec: extract_video_codec(output_settings),
44
+ audio_bitrate: extract_audio_bitrate(output_settings),
45
+ video_bitrate: extract_video_bitrate(output_settings),
46
+ url: output_url,
47
+ label: (output_url ? File.basename(output_url) : output_settings.name_modifier),
48
+ suffix: output_settings.name_modifier
49
+ }
50
+ end
51
+
52
+ def tech_metadata_from_logged(settings, logged_output)
53
+ url = logged_output.dig('outputFilePaths', 0)
54
+ {
55
+ width: logged_output.dig('videoDetails', 'widthInPx'),
56
+ height: logged_output.dig('videoDetails', 'heightInPx'),
38
57
  frame_rate: extract_video_frame_rate(settings),
39
- duration: output['durationInMs'],
58
+ duration: logged_output['durationInMs'],
40
59
  audio_codec: extract_audio_codec(settings),
41
60
  video_codec: extract_video_codec(settings),
42
61
  audio_bitrate: extract_audio_bitrate(settings),
@@ -47,6 +66,49 @@ module ActiveEncode
47
66
  }
48
67
  end
49
68
 
69
+ # constructs an `s3:` output URL from the MediaConvert job params, the same
70
+ # way MediaConvert will.
71
+ #
72
+ # @example
73
+ # construct_output_filename(
74
+ # destination: "s3://bucket/base_name",
75
+ # original_filename: "foo.mp3",
76
+ # name_modifier: "-1080",
77
+ # suffix: "m3u8")
78
+ # # => "s3://bucket/base_name-1080.m3u8"
79
+ #
80
+ # @example
81
+ # construct_output_filename(
82
+ # destination: "s3://bucket/directory_end_in_slash/",
83
+ # original_filename: "original-filename.mp3",
84
+ # name_modifier: "-1080",
85
+ # suffix: "m3u8")
86
+ # # => "s3://bucket/directory_end_in_slash/original_filename-1080.m3u8"
87
+ #
88
+ # @example
89
+ # construct_output_filename(
90
+ # destination: "s3://bucket/directory_end_in_slash/",
91
+ # original_filename: "original-filename.mp3",
92
+ # name_modifier: nil,
93
+ # suffix: "m3u8")
94
+ # # => "s3://bucket/directory_end_in_slash/original_filename.m3u8"
95
+ def construct_output_url(destination:, file_input_url:, name_modifier:, file_suffix:)
96
+ output = destination
97
+
98
+ # MediaConvert operates such that if you give a destination ending in '/',
99
+ # it'll use the original file name as part of output url.
100
+ if output.end_with?('/')
101
+ # ".*" on the end will strip extension off
102
+ output += File.basename(file_input_url, '.*')
103
+ end
104
+
105
+ output += name_modifier if name_modifier
106
+
107
+ output += "." + file_suffix
108
+
109
+ output
110
+ end
111
+
50
112
  def extract_audio_codec(settings)
51
113
  settings.audio_descriptions.first.codec_settings.codec
52
114
  rescue
@@ -18,7 +18,7 @@ module ActiveEncode
18
18
 
19
19
  def create(input_url, options = {})
20
20
  # Decode file uris for ffmpeg (mediainfo works either way)
21
- input_url = URI.decode(input_url) if input_url.starts_with? "file:///"
21
+ input_url = Addressable::URI.unencode(input_url) if input_url.starts_with? "file:///"
22
22
 
23
23
  new_encode = ActiveEncode::Base.new(input_url, options)
24
24
  new_encode.id = SecureRandom.uuid
@@ -73,7 +73,7 @@ module ActiveEncode
73
73
  new_encode.percent_complete = 1
74
74
  new_encode.errors = [e.full_message]
75
75
  write_errors new_encode
76
- return new_encode
76
+ new_encode
77
77
  end
78
78
 
79
79
  # Return encode object from file system
@@ -110,7 +110,7 @@ module ActiveEncode
110
110
  encode.percent_complete = 1
111
111
  encode.errors = [e.full_message]
112
112
  write_errors encode
113
- return encode
113
+ encode
114
114
  end
115
115
 
116
116
  # Cancel ongoing encode using pid file
@@ -19,138 +19,138 @@ module ActiveEncode
19
19
 
20
20
  private
21
21
 
22
- def get_job_details(job_id)
23
- Zencoder::Job.details(job_id)
24
- end
22
+ def get_job_details(job_id)
23
+ Zencoder::Job.details(job_id)
24
+ end
25
+
26
+ def get_job_progress(job_id)
27
+ Zencoder::Job.progress(job_id)
28
+ end
25
29
 
26
- def get_job_progress(job_id)
27
- Zencoder::Job.progress(job_id)
30
+ def build_encode(job_details)
31
+ return nil if job_details.nil?
32
+ encode = ActiveEncode::Base.new(convert_input(job_details), convert_options(job_details))
33
+ encode.id = job_details.body["job"]["id"].to_s
34
+ encode.state = convert_state(get_job_state(job_details))
35
+ job_progress = get_job_progress(encode.id)
36
+ encode.current_operations = convert_current_operations(job_progress)
37
+ encode.percent_complete = convert_percent_complete(job_progress, job_details)
38
+ encode.created_at = job_details.body["job"]["created_at"]
39
+ encode.updated_at = job_details.body["job"]["updated_at"]
40
+ encode.errors = []
41
+
42
+ encode.output = convert_output(job_details, job_progress)
43
+
44
+ encode.input.id = job_details.body["job"]["input_media_file"]["id"].to_s
45
+ encode.input.errors = convert_input_errors(job_details)
46
+ tech_md = convert_tech_metadata(job_details.body["job"]["input_media_file"])
47
+ [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
48
+ :audio_bitrate, :video_bitrate, :file_size].each do |field|
49
+ encode.input.send("#{field}=", tech_md[field])
28
50
  end
51
+ encode.input.state = convert_state(job_details.body["job"]["input_media_file"]["state"])
52
+ encode.input.created_at = job_details.body["job"]["input_media_file"]["created_at"]
53
+ encode.input.updated_at = job_details.body["job"]["input_media_file"]["updated_at"]
29
54
 
30
- def build_encode(job_details)
31
- return nil if job_details.nil?
32
- encode = ActiveEncode::Base.new(convert_input(job_details), convert_options(job_details))
33
- encode.id = job_details.body["job"]["id"].to_s
34
- encode.state = convert_state(get_job_state(job_details))
35
- job_progress = get_job_progress(encode.id)
36
- encode.current_operations = convert_current_operations(job_progress)
37
- encode.percent_complete = convert_percent_complete(job_progress, job_details)
38
- encode.created_at = job_details.body["job"]["created_at"]
39
- encode.updated_at = job_details.body["job"]["updated_at"]
40
- encode.errors = []
41
-
42
- encode.output = convert_output(job_details, job_progress)
43
-
44
- encode.input.id = job_details.body["job"]["input_media_file"]["id"].to_s
45
- encode.input.errors = convert_input_errors(job_details)
46
- tech_md = convert_tech_metadata(job_details.body["job"]["input_media_file"])
47
- [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
48
- :audio_bitrate, :video_bitrate, :file_size].each do |field|
49
- encode.input.send("#{field}=", tech_md[field])
50
- end
51
- encode.input.state = convert_state(job_details.body["job"]["input_media_file"]["state"])
52
- encode.input.created_at = job_details.body["job"]["input_media_file"]["created_at"]
53
- encode.input.updated_at = job_details.body["job"]["input_media_file"]["updated_at"]
55
+ encode
56
+ end
54
57
 
55
- encode
58
+ def convert_state(state)
59
+ case state
60
+ when "assigning", "pending", "waiting", "processing" # Should there be a queued state?
61
+ :running
62
+ when "cancelled"
63
+ :cancelled
64
+ when "failed", "no_input"
65
+ :failed
66
+ when "finished"
67
+ :completed
56
68
  end
69
+ end
57
70
 
58
- def convert_state(state)
59
- case state
60
- when "assigning", "pending", "waiting", "processing" # Should there be a queued state?
61
- :running
62
- when "cancelled"
63
- :cancelled
64
- when "failed", "no_input"
65
- :failed
66
- when "finished"
67
- :completed
68
- end
69
- end
71
+ def get_job_state(job_details)
72
+ job_details.body["job"]["state"]
73
+ end
70
74
 
71
- def get_job_state(job_details)
72
- job_details.body["job"]["state"]
73
- end
75
+ def convert_current_operations(job_progress)
76
+ current_ops = []
77
+ job_progress.body["outputs"].each { |output| current_ops << output["current_event"] unless output["current_event"].nil? }
78
+ current_ops
79
+ end
74
80
 
75
- def convert_current_operations(job_progress)
76
- current_ops = []
77
- job_progress.body["outputs"].each { |output| current_ops << output["current_event"] unless output["current_event"].nil? }
78
- current_ops
79
- end
81
+ def convert_percent_complete(job_progress, job_details)
82
+ percent = job_progress.body["progress"]
83
+ percent ||= 100 if convert_state(get_job_state(job_details)) == :completed
84
+ percent ||= 0
85
+ percent
86
+ end
80
87
 
81
- def convert_percent_complete(job_progress, job_details)
82
- percent = job_progress.body["progress"]
83
- percent ||= 100 if convert_state(get_job_state(job_details)) == :completed
84
- percent ||= 0
85
- percent
86
- end
88
+ def convert_input(job_details)
89
+ job_details.body["job"]["input_media_file"]["url"]
90
+ end
87
91
 
88
- def convert_input(job_details)
89
- job_details.body["job"]["input_media_file"]["url"]
90
- end
92
+ def convert_options(_job_details)
93
+ {}
94
+ end
91
95
 
92
- def convert_options(_job_details)
93
- {}
94
- end
96
+ def convert_output(job_details, job_progress)
97
+ job_details.body["job"]["output_media_files"].collect do |o|
98
+ output = ActiveEncode::Output.new
99
+ output.id = o["id"].to_s
100
+ output.label = o["label"]
101
+ output.url = o["url"]
102
+ output.errors = Array(o["error_message"])
95
103
 
96
- def convert_output(job_details, job_progress)
97
- job_details.body["job"]["output_media_files"].collect do |o|
98
- output = ActiveEncode::Output.new
99
- output.id = o["id"].to_s
100
- output.label = o["label"]
101
- output.url = o["url"]
102
- output.errors = Array(o["error_message"])
103
-
104
- tech_md = convert_tech_metadata(o)
105
- [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
106
- :audio_bitrate, :video_bitrate, :file_size].each do |field|
107
- output.send("#{field}=", tech_md[field])
108
- end
109
- output_progress = job_progress.body["outputs"].find { |out_prog| out_prog["id"] = output.id }
110
- output.state = convert_state(output_progress["state"])
111
- output.created_at = o["created_at"]
112
- output.updated_at = o["updated_at"]
113
- output
104
+ tech_md = convert_tech_metadata(o)
105
+ [:width, :height, :frame_rate, :duration, :checksum, :audio_codec, :video_codec,
106
+ :audio_bitrate, :video_bitrate, :file_size].each do |field|
107
+ output.send("#{field}=", tech_md[field])
114
108
  end
109
+ output_progress = job_progress.body["outputs"].find { |out_prog| out_prog["id"] = output.id }
110
+ output.state = convert_state(output_progress["state"])
111
+ output.created_at = o["created_at"]
112
+ output.updated_at = o["updated_at"]
113
+ output
115
114
  end
115
+ end
116
116
 
117
- def convert_input_errors(job_details)
118
- Array(job_details.body["job"]["input_media_file"]["error_message"])
119
- end
117
+ def convert_input_errors(job_details)
118
+ Array(job_details.body["job"]["input_media_file"]["error_message"])
119
+ end
120
120
 
121
- def convert_tech_metadata(media_file)
122
- return {} if media_file.nil?
123
-
124
- metadata = {}
125
- media_file.each_pair do |key, value|
126
- next if value.blank?
127
- case key
128
- when "md5_checksum"
129
- metadata[:checksum] = value
130
- when "format"
131
- metadata[:mime_type] = value
132
- when "duration_in_ms"
133
- metadata[:duration] = value
134
- when "audio_codec"
135
- metadata[:audio_codec] = value
136
- when "channels"
137
- metadata[:audio_channels] = value
138
- when "audio_bitrate_in_kbps"
139
- metadata[:audio_bitrate] = value
140
- when "video_codec"
141
- metadata[:video_codec] = value
142
- when "frame_rate"
143
- metadata[:frame_rate] = value
144
- when "video_bitrate_in_kbps"
145
- metadata[:video_bitrate] = value
146
- when "width"
147
- metadata[:width] = value
148
- when "height"
149
- metadata[:height] = value
150
- end
121
+ def convert_tech_metadata(media_file)
122
+ return {} if media_file.nil?
123
+
124
+ metadata = {}
125
+ media_file.each_pair do |key, value|
126
+ next if value.blank?
127
+ case key
128
+ when "md5_checksum"
129
+ metadata[:checksum] = value
130
+ when "format"
131
+ metadata[:mime_type] = value
132
+ when "duration_in_ms"
133
+ metadata[:duration] = value
134
+ when "audio_codec"
135
+ metadata[:audio_codec] = value
136
+ when "channels"
137
+ metadata[:audio_channels] = value
138
+ when "audio_bitrate_in_kbps"
139
+ metadata[:audio_bitrate] = value
140
+ when "video_codec"
141
+ metadata[:video_codec] = value
142
+ when "frame_rate"
143
+ metadata[:frame_rate] = value
144
+ when "video_bitrate_in_kbps"
145
+ metadata[:video_bitrate] = value
146
+ when "width"
147
+ metadata[:width] = value
148
+ when "height"
149
+ metadata[:height] = value
151
150
  end
152
- metadata
153
151
  end
152
+ metadata
153
+ end
154
154
  end
155
155
  end
156
156
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- module ActiveEncode #:nodoc:
2
+ module ActiveEncode # :nodoc:
3
3
  class NotFound < RuntimeError; end
4
4
  class NotRunningError < RuntimeError; end
5
5
  class CancelError < RuntimeError; end
@@ -27,25 +27,25 @@ module ActiveEncode
27
27
 
28
28
  private
29
29
 
30
- def persist(encode_attributes)
31
- model = ActiveEncode::EncodeRecord.find_or_initialize_by(global_id: encode_attributes[:global_id])
32
- model.update(encode_attributes) # Don't fail if persisting doesn't succeed?
33
- end
30
+ def persist(encode_attributes)
31
+ model = ActiveEncode::EncodeRecord.find_or_initialize_by(global_id: encode_attributes[:global_id])
32
+ model.update(encode_attributes) # Don't fail if persisting doesn't succeed?
33
+ end
34
34
 
35
- def persistence_model_attributes(encode, create_options = nil)
36
- attributes = {
37
- global_id: encode.to_global_id.to_s,
38
- state: encode.state,
39
- adapter: encode.class.engine_adapter.class.name,
40
- title: encode.input.url.to_s,
41
- # Need to ensure that these values come through or else validations will fail
42
- created_at: encode.created_at,
43
- updated_at: encode.updated_at,
44
- raw_object: encode.to_json,
45
- progress: encode.percent_complete
46
- }
47
- attributes[:create_options] = create_options.to_json if create_options.present?
48
- attributes
49
- end
35
+ def persistence_model_attributes(encode, create_options = nil)
36
+ attributes = {
37
+ global_id: encode.to_global_id.to_s,
38
+ state: encode.state,
39
+ adapter: encode.class.engine_adapter.class.name,
40
+ title: encode.input.url.to_s,
41
+ # Need to ensure that these values come through or else validations will fail
42
+ created_at: encode.created_at,
43
+ updated_at: encode.updated_at,
44
+ raw_object: encode.to_json,
45
+ progress: encode.percent_complete
46
+ }
47
+ attributes[:create_options] = create_options.to_json if create_options.present?
48
+ attributes
49
+ end
50
50
  end
51
51
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ActiveEncode
3
- VERSION = '0.8.2'
3
+ VERSION = '1.0.0'
4
4
  end
data/lib/file_locator.rb CHANGED
@@ -10,8 +10,8 @@ class FileLocator
10
10
 
11
11
  def initialize(uri)
12
12
  uri = Addressable::URI.parse(uri)
13
- @bucket = URI.decode(uri.host)
14
- @key = URI.decode(uri.path).sub(%r{^/*(.+)/*$}, '\1')
13
+ @bucket = Addressable::URI.unencode(uri.host)
14
+ @key = Addressable::URI.unencode(uri.path).sub(%r{^/*(.+)/*$}, '\1')
15
15
  end
16
16
 
17
17
  def object
@@ -26,21 +26,21 @@ class FileLocator
26
26
  def uri
27
27
  if @uri.nil?
28
28
  if source.is_a? File
29
- @uri = Addressable::URI.parse("file://#{URI.encode(File.expand_path(source))}")
29
+ @uri = Addressable::URI.parse("file://#{Addressable::URI.encode(File.expand_path(source))}")
30
30
  else
31
31
  encoded_source = source
32
32
  begin
33
33
  @uri = Addressable::URI.parse(encoded_source)
34
34
  rescue URI::InvalidURIError
35
35
  if encoded_source == source
36
- encoded_source = URI.encode(encoded_source)
36
+ encoded_source = Addressable::URI.encode(encoded_source)
37
37
  retry
38
38
  else
39
39
  raise
40
40
  end
41
41
  end
42
42
 
43
- @uri = Addressable::URI.parse("file://#{URI.encode(File.expand_path(source))}") if @uri.scheme.nil?
43
+ @uri = Addressable::URI.parse("file://#{Addressable::URI.encode(File.expand_path(source))}") if @uri.scheme.nil?
44
44
  end
45
45
  end
46
46
  @uri
@@ -51,7 +51,7 @@ class FileLocator
51
51
  when 's3'
52
52
  S3File.new(uri).object.presigned_url(:get)
53
53
  when 'file'
54
- URI.decode(uri.path)
54
+ Addressable::URI.unencode(uri.path)
55
55
  else
56
56
  @uri.to_s
57
57
  end
@@ -0,0 +1,3 @@
1
+ [mpeg2video @ 0x6e48440] slice mismatch
2
+ [mpeg2video @ 0x6e48440] Warning MVs not available
3
+ [mpeg2video @ 0x8f89a0] concealing 45 DC, 45 AC, 45 MV errors in P frame
@@ -0,0 +1,102 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <MediaInfo
3
+ xmlns="https://mediaarea.net/mediainfo"
4
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5
+ xsi:schemaLocation="https://mediaarea.net/mediainfo https://mediaarea.net/mediainfo/mediainfo_2_0.xsd"
6
+ version="2.0">
7
+ <creatingLibrary version="18.05" url="https://mediaarea.net/MediaInfo">MediaInfoLib</creatingLibrary>
8
+ <media ref="/home/pdinh/Downloads/videoshort.mp4">
9
+ <track type="General">
10
+ <VideoCount>1</VideoCount>
11
+ <AudioCount>1</AudioCount>
12
+ <FileExtension>mp4</FileExtension>
13
+ <Format>MPEG-4</Format>
14
+ <Format_Profile>Base Media</Format_Profile>
15
+ <CodecID>mp42</CodecID>
16
+ <FileSize>199160</FileSize>
17
+ <Duration>6.315</Duration>
18
+ <OverallBitRate_Mode>VBR</OverallBitRate_Mode>
19
+ <OverallBitRate>252301</OverallBitRate>
20
+ <FrameRate>23.719</FrameRate>
21
+ <FrameCount>149</FrameCount>
22
+ <StreamSize>5679</StreamSize>
23
+ <HeaderSize>160</HeaderSize>
24
+ <DataSize>193489</DataSize>
25
+ <FooterSize>5511</FooterSize>
26
+ <IsStreamable>No</IsStreamable>
27
+ <Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
28
+ <Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
29
+ <File_Modified_Date>UTC 2017-12-14 19:29:35</File_Modified_Date>
30
+ <File_Modified_Date_Local>2017-12-14 14:29:35</File_Modified_Date_Local>
31
+ <Encoded_Application>HandBrake 0.9.4 2009112300</Encoded_Application>
32
+ </track>
33
+ <track type="Video">
34
+ <StreamOrder>0</StreamOrder>
35
+ <ID>1</ID>
36
+ <Format>AVC</Format>
37
+ <Format_Profile>Main</Format_Profile>
38
+ <Format_Level>1.1</Format_Level>
39
+ <Format_Settings_CABAC>Yes</Format_Settings_CABAC>
40
+ <Format_Settings_RefFrames>2</Format_Settings_RefFrames>
41
+ <CodecID>avc1</CodecID>
42
+ <Duration>6.282</Duration>
43
+ <BitRate>74477</BitRate>
44
+ <Width>200</Width>
45
+ <Height>110</Height>
46
+ <Stored_Width>208</Stored_Width>
47
+ <Stored_Height>112</Stored_Height>
48
+ <Sampled_Width>200</Sampled_Width>
49
+ <Sampled_Height>110</Sampled_Height>
50
+ <PixelAspectRatio>1.000</PixelAspectRatio>
51
+ <DisplayAspectRatio>1.818</DisplayAspectRatio>
52
+ <Rotation>0.000</Rotation>
53
+ <FrameRate_Mode>VFR</FrameRate_Mode>
54
+ <FrameRate>23.719</FrameRate>
55
+ <FrameRate_Minimum>12.500</FrameRate_Minimum>
56
+ <FrameRate_Maximum>24.390</FrameRate_Maximum>
57
+ <FrameRate_Original>24.000</FrameRate_Original>
58
+ <FrameCount>149</FrameCount>
59
+ <ColorSpace>YUV</ColorSpace>
60
+ <ChromaSubsampling>4:2:0</ChromaSubsampling>
61
+ <BitDepth>8</BitDepth>
62
+ <ScanType>Progressive</ScanType>
63
+ <StreamSize>58482</StreamSize>
64
+ <Encoded_Library>x264 - core 79 r1347 5ddd61b</Encoded_Library>
65
+ <Encoded_Library_Name>x264</Encoded_Library_Name>
66
+ <Encoded_Library_Version>core 79 r1347 5ddd61b</Encoded_Library_Version>
67
+ <Encoded_Library_Settings>cabac=1 / ref=2 / deblock=1:0:0 / analyse=0x1:0x111 / me=hex / subme=6 / psy=1 / psy_rd=1.0:0.0 / mixed_ref=0 / me_range=16 / chroma_me=1 / trellis=0 / 8x8dct=0 / cqm=0 / deadzone=21,11 / chroma_qp_offset=-2 / threads=3 / nr=0 / decimate=1 / mbaff=0 / constrained_intra=0 / bframes=2 / b_pyramid=0 / b_adapt=1 / b_bias=0 / direct=1 / wpredb=0 / wpredp=2 / keyint=240 / keyint_min=24 / scenecut=40 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=25.5 / qcomp=0.60 / qpmin=10 / qpmax=51 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00</Encoded_Library_Settings>
68
+ <Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
69
+ <Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
70
+ <colour_range>Limited</colour_range>
71
+ <colour_description_present>Yes</colour_description_present>
72
+ <colour_primaries>BT.601 NTSC</colour_primaries>
73
+ <transfer_characteristics>BT.709</transfer_characteristics>
74
+ <matrix_coefficients>BT.601</matrix_coefficients>
75
+ </track>
76
+ <track type="Audio">
77
+ <StreamOrder>1</StreamOrder>
78
+ <ID>2</ID>
79
+ <Format>AAC</Format>
80
+ <Format_Profile>LC</Format_Profile>
81
+ <CodecID>mp4a-40-2</CodecID>
82
+ <Duration>6.315</Duration>
83
+ <BitRate_Mode>VBR</BitRate_Mode>
84
+ <BitRate>171030</BitRate>
85
+ <BitRate_Maximum>201736</BitRate_Maximum>
86
+ <Channels>1</Channels>
87
+ <ChannelPositions>Front: C</ChannelPositions>
88
+ <ChannelLayout>C</ChannelLayout>
89
+ <SamplesPerFrame>1024</SamplesPerFrame>
90
+ <SamplingRate>48000</SamplingRate>
91
+ <SamplingCount>303120</SamplingCount>
92
+ <FrameRate>46.875</FrameRate>
93
+ <FrameCount>296</FrameCount>
94
+ <Compression_Mode>Lossy</Compression_Mode>
95
+ <StreamSize>134999</StreamSize>
96
+ <StreamSize_Proportion>0.67784</StreamSize_Proportion>
97
+ <Title>Stereo</Title>
98
+ <Encoded_Date>UTC 2010-09-23 00:37:25</Encoded_Date>
99
+ <Tagged_Date>UTC 2010-09-23 00:37:27</Tagged_Date>
100
+ </track>
101
+ </media>
102
+ </MediaInfo>