Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 208 lines (171 sloc) 6.81 KB
#!/usr/bin/env ruby
#
# Copyright (c) 2009-2013 Carson McDonald
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
require 'rubygems'
require 'fileutils'
class HSTransfer
QUIT='quit'
MULTIRATE_INDEX='mr_index'
def self.init_and_start_transfer_thread(log, config)
hstransfer = HSTransfer.new(log, config)
hstransfer.start_transfer_thread
return hstransfer
end
def <<(transfer_item)
@transfer_queue << transfer_item
end
def stop_transfer_thread
@transfer_queue << QUIT
@transfer_thread.join
end
def initialize(log, config)
@transfer_queue = Queue.new
@log = log
@config = config
end
def start_transfer_thread
@transfer_thread = Thread.new do
@log.info('Transfer thread started');
while (value = @transfer_queue.pop)
@log.info("Transfer initiated with value = *#{value}*");
if value == QUIT
break
elsif value == MULTIRATE_INDEX
create_and_transfer_multirate_index
else
begin
create_index_and_run_transfer(value)
rescue
@log.error("Error running transfer: " + $!)
end
end
@log.info('Transfer done');
end
@log.info('Transfer thread terminated');
end
end
def self.can_scp
begin
require 'net/scp'
return true
rescue LoadError
return false
end
end
def self.can_ftp
begin
require 'net/ftp'
return true
rescue LoadError
return false
end
end
def self.can_s3
begin
require 'right_aws'
return true
rescue LoadError
return false
end
end
def self.can_cf
begin
require 'cloudfiles'
return true
rescue LoadError
return false
end
end
private
def create_and_transfer_multirate_index
@log.debug('Creating multirate index')
File.open("tmp.index.multi.m3u8", 'w') do |index_file|
index_file.write("#EXTM3U\n")
@config['encoding_profile'].each do |encoding_profile_name|
encoding_profile = @config[encoding_profile_name]
index_file.write("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=#{encoding_profile['bandwidth']}\n")
index_name = "%s_%s.m3u8" % [@config['index_prefix'], encoding_profile_name]
index_file.write("#{@config['url_prefix']}#{index_name}\n")
end
end
@log.debug('Transfering multirate index')
transfer_file("tmp.index.multi.m3u8", "#{@config["index_prefix"]}_multi.m3u8")
end
def create_index(index_segment_count, segment_duration, output_prefix, encoding_profile, http_prefix, first_segment, last_segment, stream_end)
@log.debug('Creating index');
File.open("tmp.index.#{encoding_profile}.m3u8", 'w') do |index_file|
index_file.write("#EXTM3U\n")
index_file.write("#EXT-X-TARGETDURATION:#{segment_duration}\n")
index_file.write("#EXT-X-MEDIA-SEQUENCE:#{last_segment >= index_segment_count ? last_segment-(index_segment_count-1) : 1}\n")
first_segment.upto(last_segment) do | segment_index |
if segment_index > last_segment - index_segment_count
index_file.write("#EXTINF:#{segment_duration},\n")
index_file.write("#{http_prefix}#{output_prefix}_#{encoding_profile}-%05u.ts\n" % segment_index)
end
end
index_file.write("#EXT-X-ENDLIST\n") if stream_end
end
@log.debug('Done creating index');
end
def create_index_and_run_transfer(value)
(first_segment, last_segment, stream_end, encoding_profile) = value.strip.split(%r{,\s*})
create_index(@config['index_segment_count'], @config['segment_length'], @config['segment_prefix'], encoding_profile, @config['url_prefix'], first_segment.to_i, last_segment.to_i, stream_end.to_i == 1)
# Transfer the index
final_index = "%s_%s.m3u8" % [@config['index_prefix'], encoding_profile]
transfer_file("tmp.index.#{encoding_profile}.m3u8", "#{final_index}")
# Transfer the video stream
video_filename = "#{@config['temp_dir']}/#{@config['segment_prefix']}_#{encoding_profile}-%05u.ts" % last_segment.to_i
dest_video_filename = "#{@config['segment_prefix']}_#{encoding_profile}-%05u.ts" % last_segment.to_i
transfer_file(video_filename, dest_video_filename)
end
def transfer_file(source_file, destination_file)
transfer_config = @config[@config['transfer_profile']]
case transfer_config['transfer_type']
when 'copy'
FileUtils.copy(source_file, transfer_config['directory'] + '/' + destination_file)
when 'ftp'
require 'net/ftp'
Net::FTP.open(transfer_config['remote_host']) do |ftp|
ftp.login(transfer_config['user_name'], transfer_config['password'])
files = ftp.chdir(transfer_config['directory'])
ftp.putbinaryfile(source_file, destination_file)
end
when 'scp'
require 'net/scp'
if transfer_config.has_key?('password')
Net::SCP.upload!(transfer_config['remote_host'], transfer_config['user_name'], source_file, transfer_config['directory'] + '/' + destination_file, :password => transfer_config['password'])
else
Net::SCP.upload!(transfer_config['remote_host'], transfer_config['user_name'], source_file, transfer_config['directory'] + '/' + destination_file)
end
when 's3'
require 'right_aws'
s3 = RightAws::S3Interface.new(transfer_config['aws_api_key'], transfer_config['aws_api_secret'])
content_type = source_file =~ /.*\.m3u8$/ ? 'application/vnd.apple.mpegurl' : 'video/MP2T'
@log.debug("Content type: #{content_type}")
s3.put(transfer_config['bucket_name'], "#{transfer_config['key_prefix']}/#{destination_file}", File.open(source_file), {'x-amz-acl' => 'public-read', 'content-type' => content_type})
when 'cf'
require 'cloudfiles'
cf = CloudFiles::Connection.new(:username => transfer_config['username'], :api_key => transfer_config['api_key'])
container = cf.container(transfer_config['container'])
object = container.create_object "#{transfer_config['key_prefix']}/#{destination_file}", false
object.write File.open(source_file)
else
@log.error("Unknown transfer type: #{transfer_config['transfer_type']}")
end
File.unlink(source_file)
end
end