class FLV::FLVStream
Attributes
Public Class Methods
Source
# File lib/flv/stream.rb, line 48 def initialize(in_stream, out_stream = nil, stream_log = false) @stream_log = stream_log ? (File.open('stream.log', File::CREAT|File::WRONLY|File::TRUNC) rescue AMFStringBuffer.new) : AMFStringBuffer.new @in_stream = in_stream @out_stream = out_stream || in_stream unless eof? begin read_header read_tags rescue Object => e log e raise e ensure @stream_log.close end else @version = 1 @type_flags_audio = false @type_flags_video = false @extra_data = '' @tags = [] end end
Public Instance Methods
Source
# File lib/flv/stream.rb, line 127 def add_meta_tag(meta_data = {}) meta_tag = FLVMetaTag.new meta_tag.event = 'onMetaData' meta_tag['framerate'] = framerate meta_tag['duration'] = duration meta_tag['lasttimestamp'] = lasttimestamp meta_tag['videosize'] = videosize meta_tag['audiosize'] = audiosize meta_tag['datasize'] = 0 # calculate after tag was added meta_tag['filesize'] = 0 # calculate after tag was added meta_tag['width'] = (width == 0 && on_meta_data_tag) ? on_meta_data_tag.meta_data['width'] : width meta_tag['height'] = (height == 0 && on_meta_data_tag) ? on_meta_data_tag.meta_data['height'] : height meta_tag['videodatarate'] = videodatarate meta_tag['audiodatarate'] = audiodatarate meta_tag['lastkeyframetimestamp'] = lastkeyframetimestamp meta_tag['audiocodecid'] = audiocodecid meta_tag['videocodecid'] = videocodecid meta_tag['audiodelay'] = audiodelay meta_tag['canSeekToEnd'] = canSeekToEnd meta_tag['stereo'] = stereo meta_tag['audiosamplerate'] = audiosamplerate meta_tag['audiosamplesize'] = audiosamplesize meta_tag['cuePoints'] = cue_points meta_tag['keyframes'] = keyframes meta_tag['hasVideo'] = has_video? meta_tag['hasAudio'] = has_audio? meta_tag['hasMetadata'] = true meta_tag['hasCuePoints'] = has_cue_points? meta_tag['hasKeyframes'] = has_keyframes? meta_tag.meta_data.merge!(meta_data) add_tags(meta_tag) # recalculate values those need meta tag data size or presence meta_tag['keyframes'] = keyframes meta_tag['datasize'] = datasize meta_tag['filesize'] = filesize meta_tag['hasMetadata'] = has_meta_data? end
Source
# File lib/flv/stream.rb, line 346 def audiocodecid audio_tags.first && audio_tags.first.sound_format end
Source
# File lib/flv/stream.rb, line 327 def audiodatarate data_size = audio_tags.inject(0) do |size, tag| size += tag.data_size end return data_size == 0 ? 0 : data_size / duration * 8 / 1000 # kBits/sec end
Source
# File lib/flv/stream.rb, line 355 def audiodelay return 0 unless has_video? video_tags.first.timestamp.nil? ? 0 : video_tags.first.timestamp / 1000.0 end
Source
# File lib/flv/stream.rb, line 338 def audiosamplerate audio_tags.first && audio_tags.first.sound_rate end
Source
# File lib/flv/stream.rb, line 342 def audiosamplesize audio_tags.first && audio_tags.first.sound_sample_size end
Source
# File lib/flv/stream.rb, line 297 def audiosize audio_tags.inject(0) { |size, tag| size += tag.size } end
Source
# File lib/flv/stream.rb, line 360 def canSeekToEnd return true unless has_video? video_tags.last.frame_type == FLVVideoTag::KEYFRAME end
Source
# File lib/flv/stream.rb, line 376 def cue_points on_cue_point_tags.collect { |tag| tag.meta_data } end
Source
# File lib/flv/stream.rb, line 112 def cut(options = []) @tags.delete_if { |tag| tag.timestamp < ( options[:in_point] || 0 ) || tag.timestamp > ( options[:out_point] || tags.last.timestamp ) } if options[:collapse] difference = @tags.first.timestamp @tags.each { |tag| tag.timestamp -= difference } end empty_tag_type_cache end
Source
# File lib/flv/stream.rb, line 301 def datasize videosize + audiosize + (meta_tags.inject(0) { |size, tag| size += tag.size}) end
Source
# File lib/flv/stream.rb, line 193 def empty_tag_type_cache @video_tags_cache = nil @keyframe_video_tags_cache = nil @audio_tags_cache = nil @meta_tags_cache = nil @on_cue_point_tags_cache = nil end
views on tags
Source
# File lib/flv/stream.rb, line 305 def filesize # header + data + backpointers @data_offset + datasize + ((@tags.length + 1) * 4) end
Source
# File lib/flv/stream.rb, line 121 def find_nearest_keyframe_video_tag(position) keyframe_video_tags.sort do |tag_a, tag_b| (position - tag_a.timestamp).abs <=> (position - tag_b.timestamp).abs end.first end
Source
# File lib/flv/stream.rb, line 250 def frame_sequence return nil unless has_video? raise(FLVStreamError, 'File has to contain at least 2 video tags to calculate frame sequence') if video_tags.length < 2 @frame_sequence ||= begin sequences = video_tags.collect do |tag| # find all sequences video_tags[video_tags.index(tag) + 1].timestamp - tag.timestamp unless tag == video_tags.last end.compact uniq_sequences = (sequences.uniq - [0]).sort # remove 0 and try smallest intervall first sequence_appearances = uniq_sequences.collect { |sequence| sequences.find_all { |_sequence| sequence == _sequence }.size } # count apperance of each sequence uniq_sequences[ sequence_appearances.index( sequence_appearances.max ) ] # return the sequence that appears most end end
FIXME: Could be less complicate and run faster
Source
# File lib/flv/stream.rb, line 268 def framerate return nil unless has_video? frame_sequence == 0 ? 0 : 1000 / frame_sequence end
Source
# File lib/flv/stream.rb, line 239 def has_cue_points? on_cue_point_tags.size > 0 end
Source
# File lib/flv/stream.rb, line 243 def has_keyframes? keyframe_video_tags.size > 0 end
Source
# File lib/flv/stream.rb, line 235 def has_meta_data? !on_meta_data_tag.nil? end
Source
# File lib/flv/stream.rb, line 315 def height return nil unless has_video? video_tags.first.height || 0 end
Source
# File lib/flv/stream.rb, line 365 def keyframes object = Object.new calculate_tag_byte_offsets object.instance_variable_set( :@times, keyframe_video_tags.collect { |video_tag| video_tag.timestamp / 1000.0 } ) object.instance_variable_set( :@filepositions, keyframe_video_tags.collect { |video_tag| video_tag.byte_offset } ) return object end
Source
# File lib/flv/stream.rb, line 288 def lastkeyframetimestamp return nil unless has_video? (keyframe_video_tags.last.nil? || keyframe_video_tags.last.timestamp.nil?) ? 0 : keyframe_video_tags.last.timestamp / 1000.0 end
Source
# File lib/flv/stream.rb, line 277 def lasttimestamp last_tag = if has_video? video_tags.last elsif has_audio? audio_tags.last else tags.last end last_tag.timestamp.nil? ? 0 : last_tag.timestamp / 1000.0 end
Source
# File lib/flv/stream.rb, line 219 def on_meta_data_tag @tags.find { |tag| tag.kind_of?(FLVMetaTag) && tag.event == 'onMetaData' } # FIXME: Cannot be cached end
Source
# File lib/flv/stream.rb, line 334 def stereo audio_tags.first && audio_tags.first.sound_type == FLVAudioTag::STEREO end
Source
# File lib/flv/stream.rb, line 350 def videocodecid return nil unless has_video? video_tags.first.codec_id end
Source
# File lib/flv/stream.rb, line 320 def videodatarate data_size = video_tags.inject(0) do |size, tag| size += tag.data_size end return data_size == 0 ? 0 : data_size / duration * 8 / 1000 # kBits/sec end
Source
# File lib/flv/stream.rb, line 293 def videosize video_tags.inject(0) { |size, tag| size += tag.size } end
Source
# File lib/flv/stream.rb, line 310 def width return nil unless has_video? video_tags.first.width || 0 end
Source
# File lib/flv/stream.rb, line 169 def write begin @out_stream.seek( 0 ) rescue Object => e end write_header write_tags begin @out_stream.truncate( @out_stream.pos ) rescue Object => e end end
Private Instance Methods
Source
# File lib/flv/stream.rb, line 385 def calculate_tag_byte_offsets @tags.inject(@data_offset + 4) { |offset, tag| tag.byte_offset = offset; offset += 4 + tag.size } end
Source
# File lib/flv/stream.rb, line 389 def read_header begin @signature = @in_stream.read__STRING(3) log "File signature: #{@signature}" raise(FLVStreamError, 'IO is not a FLV stream. Wrong signature.') if @signature != 'FLV' @version = @in_stream.read__UI8 log "File version: #{@version}" type_flags = @in_stream.read__UI8 @type_flags_audio = (type_flags & 4) == 1 log "File has audio: #{@type_flags_audio}" @type_flags_video = (type_flags & 1) == 1 log "File has video: #{@type_flags_video}" @data_offset = @in_stream.read__UI32 log "File header size: #{@data_offset}" @extra_data = @in_stream.read__STRING @data_offset - 9 log "File header extra data: #{@extra_data}" rescue IOError => e raise IOError, "IO Error while reading FLV header. #{e.message}", e.backtrace end end
Source
# File lib/flv/stream.rb, line 416 def write_header begin @out_stream.write__STRING 'FLV' @out_stream.write__UI8 1 type_flags = 0 type_flags += 4 if has_audio? type_flags += 1 if has_video? @out_stream.write__UI8 type_flags @out_stream.write__UI32 9 + @extra_data.length @out_stream.write__STRING @extra_data rescue IOError => e raise IOError, "IO Error while writing FLV header. #{e.message}", e.backtrace end end