class Libav::Stream::Video
Attributes
afs[R]
height[R]
pixel_format[R]
reader[R]
width[R]
Public Class Methods
new(p={})
click to toggle source
Calls superclass method
Libav::Stream::new
# File lib/libav/stream.rb, line 228 def initialize(p={}) super(p) # Handle frame width and height and setup any scaling necessary @width = p[:widht] || @av_codec_ctx[:width] @height = p[:height] || @av_codec_ctx[:height] @pixel_format = p[:pixel_format] || @av_codec_ctx[:pix_fmt] # Our stream index info @afs = p[:afs] @update_afs = @afs.nil? == false # Our array and queues for raw frames and scaled frames @raw_frames = [] @raw_queue = Queue.new @scaled_frames = [] @scaled_queue = Queue.new # Number of frames to buffer (default is disabled, 0) @buffer = 0 # When this is set to true when all raw and scaled frames have been set up # by setup(). @decode_ready = false # Pointer used to denote that decode frame was successful @frame_finished = FFI::MemoryPointer.new :int # Elaborate schemes to capture an accurate pts value. When the buffer for # the frame is allocated, we pull the dts from the last packet processed # (set in decode_frame()), and set it in the opaque field of the frame. # # Since we may be running on a 32-bit platform, we can't just shove the # 64-bit dts in the :opaque pointer, so we have to alloc some space for the # address. Instead of allocating and freeing repeatedly, we're going to # alloc it once now and reuse it for each decoded frame. @last_dts = nil @last_pos = nil @opaque = FFI::MemoryPointer.new :uint64, 2 @av_codec_ctx[:get_buffer] = \ FFI::Function.new(:int, [AVCodecContext.ptr, AVFrame.ptr]) do |ctx,frame| # Use the default method to get the buffer ret = avcodec_default_get_buffer(ctx, frame) # Update the :opaque field point at a copy of the last pts we've seen. @opaque.put_int64(0, @last_dts) @opaque.put_uint64(8, @last_pos) frame[:opaque] = @opaque ret end # Initialize our frame number oafset, and our pts oafset. These two are # modified by seek(), and used by decode_frame(). @frame_oafset = 0 @pts_oafset = 0 end
Public Instance Methods
buffer=(v)
click to toggle source
Set the buffer size
# File lib/libav/stream.rb, line 318 def buffer=(v) return if v == @buffer @buffer = v teardown end
decode_frame(packet)
click to toggle source
Called by Libav::Reader.each_frame
to decode each frame
# File lib/libav/stream.rb, line 332 def decode_frame(packet) setup unless @decode_ready # Save off our dts and pos for the buffer allocation callback we declared # in #initialize @last_dts = packet[:dts] @last_pos = packet[:pos] # Grab our raw frame off the raw frames queue. This will block if the # caller is still using all the previous frames. raw_frame = @raw_queue.shift # Let the reader know we're stomping on this frame @reader.frame_dirty(raw_frame) # Call the decode function on our packet avcodec_get_frame_defaults(raw_frame.av_frame) rc = avcodec_decode_video2(@av_codec_ctx, raw_frame.av_frame, @frame_finished, packet) # Now, if we didn't get a frame, for one reason or another, let's throw the # raw frame back on our queue. if rc < 0 or @frame_finished.read_int == 0 @raw_queue.push raw_frame return nil end raw_frame.number = @av_codec_ctx[:frame_number].to_i + @frame_oafset raw_frame.pts = raw_frame.av_frame[:opaque].get_int64(0) + @pts_oafset raw_frame.pos = raw_frame.av_frame[:opaque].get_uint64(8) # AFS Data is broken down as follows: # [ [frame number, pts, pos, true], # entry for first key frame # [frame number, pts, pos, true], # entry for second key frame # [frame number, pts, pos, true], # entry for N-th key frame # [frame number, pts, pos, false], # optional, last non-key frame # ] if @update_afs and (@afs.empty? or raw_frame.number > @afs.last[0]) @afs.pop unless @afs.empty? or @afs.last[-1] == true @afs << [ raw_frame.number, raw_frame.pts, raw_frame.pos, raw_frame.key_frame? ] end # If we're scaling, or not buffering, throw the raw frame back on the # queue; it's the only one we have @raw_queue.push raw_frame if @swscale_ctx or @buffer == 0 # If we're not scaling at this point, we need to return the raw frame to # the caller. This is the non-buffering, non-scaling return point. return raw_frame unless @swscale_ctx # Let's grab a scaled frame from our queue scaled_frame = @scaled_queue.shift # Let the reader know we're stomping on this frame @reader.frame_dirty(scaled_frame) # scale the frame raw_frame.scale(:scale_ctx => @swscale_ctx, :output_frame => scaled_frame) # Throw the scaled frame back on the queue if we're not buffering @scaled_queue.push scaled_frame if @buffer == 0 scaled_frame end
fps()
click to toggle source
# File lib/libav/stream.rb, line 289 def fps fps = @av_stream[:r_frame_rate].to_f # Some codecs don't set frame rate, so we'll use 1/timebase fps = 1/@av_stream[:time_base].to_f if fps.nan? fps end
height=(height)
click to toggle source
Set the height
of the frames returned by decode_frame
# File lib/libav/stream.rb, line 304 def height=(height) return if height == @height @height = height teardown end
pixel_format=(pixel_format)
click to toggle source
Set the +pixel format+ of the frames returned by decode_frame
# File lib/libav/stream.rb, line 311 def pixel_format=(pixel_format) return if pixel_format == @pixel_format @pixel_format = pixel_format teardown end
release_all_frames()
click to toggle source
This method will make the stream release all references to buffered frames. The buffers will be recreated the next time decode_frame
is called.
# File lib/libav/stream.rb, line 410 def release_all_frames teardown end
release_frame(frame)
click to toggle source
# File lib/libav/stream.rb, line 399 def release_frame(frame) @scaled_queue.push frame if @scaled_frames.include? frame @raw_queue.push frame if @raw_frames.include? frame end
rewind(count=nil)
click to toggle source
# File lib/libav/stream.rb, line 404 def rewind(count=nil) @reader.rewind(count, :stream => self) end
scaling?()
click to toggle source
Check to see if this frame is being scaled
# File lib/libav/stream.rb, line 325 def scaling? @av_codec_ctx[:width] != @width or @av_codec_ctx[:height] != @height or @av_codec_ctx[:pix_fmt] != @pixel_format end
width=(width)
click to toggle source
Set the width
of the frames returned by decode_frame
# File lib/libav/stream.rb, line 297 def width=(width) return if width == @width @width = width teardown end
Private Instance Methods
setup()
click to toggle source
Initialize our raw and scaled frames along with our scaling context
# File lib/libav/stream.rb, line 417 def setup return if @decode_ready # call teardown() now just to make sure everything is released teardown # If @buffer is set to zero, we aren't buffering, but we will need one raw # frame and one scaled frame. buffer = @buffer buffer += 1 if buffer == 0 # Let's allocate our raw frames. If we're scaling, we only need one raw # frame, otherwise we'll need more raw frames. ( scaling? ? 1 : buffer ).times do frame = Libav::Frame::Video.new :stream => self, :alloc => false, :width => @av_codec_ctx[:width], :height => @av_codec_ctx[:height], :pixel_format => @av_codec_ctx[:pix_fmt] @raw_frames.push frame @raw_queue.push frame end # If we're scaling, allocate our scaled frames and scaling context if scaling? # allocate our scaled frames buffer.times do frame = Libav::Frame::Video.new(:width => @width, :height => @height, :pixel_format => @pixel_format, :stream => self) @scaled_frames.push frame @scaled_queue.push frame end # Let's throw together a scaling context @swscale_ctx = sws_getCachedContext(nil, @av_codec_ctx[:width], @av_codec_ctx[:height], @av_codec_ctx[:pix_fmt], @width, @height, @pixel_format, SWS_BICUBIC, nil, nil, nil) or raise NoMemoryError, "sws_getCachedContext() failed" end @decode_ready = true end
teardown()
click to toggle source
Release all references to our frames (raw & scaled), and free our scaling context.
# File lib/libav/stream.rb, line 466 def teardown @raw_frames.clear @raw_queue.clear @scaled_frames.clear @scaled_queue.clear sws_freeContext(@swscale_ctx) if @swscale_ctx @swscale_ctx = nil @decode_ready = false end