Class: XMorph::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/xmorph/base.rb

Direct Known Subclasses

Transcode

Constant Summary collapse

IGNORE =
"ignore"
VALIDATE =
"validate"
ALLOWED_ASPECT_RATIO =
"allowed_aspect_ratio"
ALLOWED_HEIGHT =
"allowed_height"
ALLOWED_WIDTH =
"allowed_width"
ALLOWED_FRAME_RATE =
"allowed_frame_rate"
ALLOWED_VIDEO_BIT_RATE =
"allowed_video_bit_rate"
ALLOWED_SCAN_TYPE =
"allowed_scan_type"
PRESENCE_OF_AUDIO_TRACK =
"presence_of_audio_track"
ALLOWED_NUMBER_OF_AUDIO_TRACKS =
"allowed_number_of_audio_tracks"
ALLOWED_AUDIO_CODECS =
"allowed_audio_codecs"
ALLOWED_AUDIO_BIT_RATE =
"allowed_audio_bit_rate"
ALLOWED_NUMBER_OF_AUDIO_CHANNELS =
"allowed_number_of_audio_channels"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(asset_path) ⇒ Base

Returns a new instance of Base.



34
35
36
37
# File 'lib/xmorph/base.rb', line 34

def initialize(asset_path)
  self.asset_path = asset_path
  self.set_profiles
end

Instance Attribute Details

#asset_pathObject

Returns the value of attribute asset_path.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def asset_path
  @asset_path
end

#default_audio_checksObject

Returns the value of attribute default_audio_checks.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def default_audio_checks
  @default_audio_checks
end

#default_video_checksObject

Returns the value of attribute default_video_checks.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def default_video_checks
  @default_video_checks
end

#errorObject

Returns the value of attribute error.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def error
  @error
end

#loggerObject

Returns the value of attribute logger.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def logger
  @logger
end

#mediainfo_outputObject

Returns the value of attribute mediainfo_output.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def mediainfo_output
  @mediainfo_output
end

#profile_nameObject

Returns the value of attribute profile_name.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def profile_name
  @profile_name
end

#profilesObject

Returns the value of attribute profiles.



20
21
22
# File 'lib/xmorph/base.rb', line 20

def profiles
  @profiles
end

Class Method Details

.get_transcode_template(host, domain, name, asset_path, return_loaded_filepath = false) ⇒ Object

Raises:



39
40
41
42
43
44
45
46
47
# File 'lib/xmorph/base.rb', line 39

def self.get_transcode_template(host, domain, name, asset_path, return_loaded_filepath=false)
  return nil unless domain
  file_path = File.join(self.root, "xmorph", "customers", host, domain, "transcode.rb")
  raise TranscoderError.new("Transcoding profile doesn't exist for account, #{name}.") unless File.exist? file_path
  XMorph::Base.logger.debug("XMorph#get_transcode_template: loading file #{file_path} to transcode #{asset_path}")
  load file_path
  return Transcode.new(asset_path), file_path if return_loaded_filepath
  return Transcode.new(asset_path)
end

.loggerObject



26
27
28
# File 'lib/xmorph/base.rb', line 26

def self.logger
  @@logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
end

.logger=(logger) ⇒ Object



30
31
32
# File 'lib/xmorph/base.rb', line 30

def self.logger=(logger)
  @@logger = logger
end

.rootObject



22
23
24
# File 'lib/xmorph/base.rb', line 22

def self.root
  File.dirname __dir__
end

Instance Method Details

#get_asset_detailsObject

Raises:



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/xmorph/base.rb', line 49

def get_asset_details
  mediainfo_command = "#{File.dirname(self.class.root)}/bin/mediainfo --output=XML #{self.asset_path}"
  success, response = Util.run_cmd_with_response(mediainfo_command)
  raise TranscoderError.new("Failed to get mediainfo for the asset.") unless success
  begin
    self.mediainfo_output = Util.mediainfo_xml_to_hash(response)[1]
  rescue => e
    raise TranscoderError.new("Failed to get mediainfo for the asset.")
  end
  set_validations
end

#get_profile(return_profile_name = false) ⇒ Object



81
82
83
84
85
86
87
88
89
# File 'lib/xmorph/base.rb', line 81

def get_profile(return_profile_name=false)
  self.set_profile_name
  if (self.profile_name.blank? or self.profiles[self.profile_name].blank?)
    raise TranscoderError.new(self.error || "Media doesnt match any transcoding profiles.")
  end
  XMorph::Base.logger.debug("XMorph#get_profile: Using ffmpeg command: #{self.profiles[self.profile_name]} to transcode #{asset_path}")
  return self.profile_name, self.profiles[self.profile_name] if return_profile_name
  return self.profiles[self.profile_name]
end

#perform_default_audio_validationsObject



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/xmorph/base.rb', line 198

def perform_default_audio_validations
  mediainfo = self.mediainfo_output
  audio_tracks = mediainfo["Audio"]
  default_checks = self.default_audio_checks
    
  number_of_audio_tracks = audio_tracks.count
  if number_of_audio_tracks > 0
    audio_codecs = []; bit_rate = []; number_of_audio_channels = []
    audio_tracks.each do |a|
      audio_codecs << a["Format"]
      bit_rate << (a["Bit_rate"] || a["Nominal_bit_rate"]).split(" ")[0].to_f if (a["Bit_rate"] || a["Nominal_bit_rate"]).present?
      number_of_audio_channels << a["Channel_s_"].split(" ")[0].to_i if a["Channel_s_"].present?
    end
  end

  if (default_checks[PRESENCE_OF_AUDIO_TRACK] != IGNORE) and (number_of_audio_tracks <= 0)
    self.error = "No audio tracks found" 
    return false
  end

  errors = []
  missing = []
  if number_of_audio_tracks > 0
    if default_checks[ALLOWED_NUMBER_OF_AUDIO_TRACKS] != IGNORE
      errors << "Unexpected number of audio tracks #{number_of_audio_tracks}. We support #{default_checks[ALLOWED_NUMBER_OF_AUDIO_TRACKS]} tracks" unless default_checks[ALLOWED_NUMBER_OF_AUDIO_TRACKS].include? number_of_audio_tracks
    end
    if default_checks[ALLOWED_AUDIO_BIT_RATE] != IGNORE
      if bit_rate.empty? || (bit_rate.uniq.include? nil)
        missing << "Bit rate"  
      else  
        unsupported_bit_rate = bit_rate.map{|b| b unless default_checks[ALLOWED_AUDIO_BIT_RATE].include? b}
        errors << "Unexpected Bit rate #{unsupported_bit_rate}. We support #{default_checks[ALLOWED_AUDIO_BIT_RATE]}" unless unsupported_bit_rate.empty?
      end
    end
    if default_checks[ALLOWED_AUDIO_CODECS] != IGNORE
      if audio_codecs.empty? || (audio_codecs.uniq.include? nil)
        missing << "audio codecs"  
      else
        audio_codecs = audio_codecs.map{|a| a.downcase}
        unsupported_audio_codecs = audio_codecs - default_checks[ALLOWED_AUDIO_CODECS].to_a
        errors << "Unexpected audio codecs #{unsupported_audio_codecs}. We support #{default_checks[ALLOWED_AUDIO_CODECS]}" unless unsupported_audio_codecs.empty?
      end
    end
    if default_checks[ALLOWED_NUMBER_OF_AUDIO_CHANNELS] != IGNORE
      if number_of_audio_channels.empty? || (number_of_audio_channels.uniq.include? nil)
        missing << "number of audio channels" 
      else
        unsupported_number_of_audio_channels = number_of_audio_channels - default_checks[ALLOWED_NUMBER_OF_AUDIO_CHANNELS].to_a
        errors << "Unexpected number of audio channels #{unsupported_number_of_audio_channels}. We support #{default_checks[ALLOWED_NUMBER_OF_AUDIO_CHANNELS]} channels" unless unsupported_number_of_audio_channels.empty?
      end
    end
  end
  unless missing.empty?
    self.error = "Couldn't find #{missing.join(",")} of the Video correctly"
    return false
  end
  unless errors.empty?
    self.error = errors.join("\n")
    return false
  end
  return true
end

#perform_default_video_validationsObject

def audio_checks

{
  PRESENCE_OF_AUDIO_TRACK => IGNORE || VALIDATE,
  ALLOWED_NUMBER_OF_AUDIO_TRACKS => (1..16)|| [2, 4, 6, 8] || IGNORE,
  ALLOWED_AUDIO_CODECS => ['aac', 'ac3', 'mp2', 'mp4'] || IGNORE,
  ALLOWED_AUDIO_BIT_RATE => (120..317) || [192, 317] || IGNORE,
  ALLOWED_NUMBER_OF_AUDIO_CHANNELS => (1..16) || [1, 2] || IGNORE,
}

end



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/xmorph/base.rb', line 126

def perform_default_video_validations
  mediainfo = self.mediainfo_output
  video_info = mediainfo["Video"]
  default_checks = self.default_video_checks

  aspect_ratio = video_info["Display_aspect_ratio"]
  height = (video_info["Original_height"] || video_info["Height"])
  width = (video_info["Original_width"] || video_info["Width"])
  frame_rate = video_info["Frame_rate"]
  bit_rate = video_info["Bit_rate"] || video_info["Nominal_bit_rate"]
  scan_type = video_info["Scan_type"]

  errors = []
  missing = []
  if default_checks[ALLOWED_ASPECT_RATIO] != IGNORE 
    if aspect_ratio.present?
      errors << "Unexpected Aspect ratio #{aspect_ratio}. We support #{default_checks[ALLOWED_ASPECT_RATIO]}" unless default_checks[ALLOWED_ASPECT_RATIO].include? aspect_ratio
    else    
      missing << "Aspect ratio"   
    end
  end
  if default_checks[ALLOWED_HEIGHT] != IGNORE  
    if height.present?
      height = height.split("pixels")[0].gsub(/ /,"").to_i
      errors << "Unexpected Height #{height}. We support #{default_checks[ALLOWED_HEIGHT]}" unless default_checks[ALLOWED_HEIGHT].include? height
    else
      missing << "Height"
    end
  end
  if default_checks[ALLOWED_WIDTH] != IGNORE    
    if width.present?
      width = width.split("pixels")[0].gsub(/ /,"").to_i
      errors << "Unexpected Width #{width}. We support #{default_checks[ALLOWED_WIDTH]}" unless default_checks[ALLOWED_WIDTH].include? width
    else
      missing << "Width"
    end
  end
  if default_checks[ALLOWED_FRAME_RATE] != IGNORE 
    if frame_rate.present?
      frame_rate = frame_rate.split(" ")[0].to_f 
      errors << "Unexpected Frame rate #{frame_rate}. We support #{default_checks[ALLOWED_FRAME_RATE]}" unless default_checks[ALLOWED_FRAME_RATE].include? frame_rate
    else
      missing << "Frame rate"
    end
  end
  if default_checks[ALLOWED_VIDEO_BIT_RATE] != IGNORE 
    if bit_rate.present?
      bit_rate = bit_rate.split(" ")[0].to_f
      errors << "Unexpected Bit rate #{bit_rate}. We support #{default_checks[ALLOWED_VIDEO_BIT_RATE]}" unless default_checks[ALLOWED_VIDEO_BIT_RATE].include? bit_rate
    else
      missing << "Bit rate"
    end
  end
  if default_checks[ALLOWED_SCAN_TYPE] != IGNORE 
    if scan_type.present? 
      errors << "Unexpected Scan type #{scan_type}. We support #{default_checks[ALLOWED_SCAN_TYPE]}" unless default_checks[ALLOWED_SCAN_TYPE].include? scan_type.downcase
    else
      missing << "Scan type"
    end
  end

  unless missing.empty?
    self.error = "Couldn't find #{missing.join(",")} of the Video correctly"
    return false
  end
  unless errors.empty?
    self.error = errors.join("\n")
    return false
  end
  return true
end

#post_processObject



98
99
100
# File 'lib/xmorph/base.rb', line 98

def post_process
  #do some post processing
end

#set_validationsObject



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/xmorph/base.rb', line 61

def set_validations
  begin
    self.default_video_checks = self.video_checks
  rescue => e
    raise TranscoderError.new("Default video validation requirements are not defined.")
  end
  begin
    self.default_audio_checks = self.audio_checks
  rescue => e
    raise TranscoderError.new("Default audio validation requirements are not defined.")
  end
  return true
end

#transcode(cmd) ⇒ Object

Raises:



91
92
93
94
95
96
# File 'lib/xmorph/base.rb', line 91

def transcode(cmd)
  raise TranscoderError.new("Command passed to transcode is empty.") unless cmd
  status, response = Util.run_cmd_with_response(cmd)
  raise TranscoderError.new(self.error || response.split("\n").last) unless status
  return status
end

#validate_assetObject

Raises:



75
76
77
78
79
# File 'lib/xmorph/base.rb', line 75

def validate_asset
  raise TranscoderError.new(self.error || "Failed to perform default video validations.") unless perform_default_video_validations
  raise TranscoderError.new(self.error || "Failed to perform default audio validations.") unless perform_default_audio_validations
  return true
end