Class: Libav::Stream::Video

Inherits:
Object
  • Object
show all
Includes:
Libav::Stream
Defined in:
lib/libav/stream.rb

Instance Attribute Summary collapse

Attributes included from Libav::Stream

#av_codec_ctx, #av_stream, #buffer

Instance Method Summary collapse

Methods included from Libav::Stream

#discard, #discard=, #each_frame, #format_afs, #index, #next_frame, #seek, #skip_frames, #type

Constructor Details

#initialize(p = {}) ⇒ Video

Returns a new instance of Video.



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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# 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

Instance Attribute Details

#afsObject (readonly)

Returns the value of attribute afs.



226
227
228
# File 'lib/libav/stream.rb', line 226

def afs
  @afs
end

#heightObject

Returns the value of attribute height.



226
227
228
# File 'lib/libav/stream.rb', line 226

def height
  @height
end

#pixel_formatObject

Returns the value of attribute pixel_format.



226
227
228
# File 'lib/libav/stream.rb', line 226

def pixel_format
  @pixel_format
end

#readerObject (readonly)

Returns the value of attribute reader.



226
227
228
# File 'lib/libav/stream.rb', line 226

def reader
  @reader
end

#widthObject

Returns the value of attribute width.



226
227
228
# File 'lib/libav/stream.rb', line 226

def width
  @width
end

Instance Method Details

#buffer=(v) ⇒ Object

Set the buffer size



318
319
320
321
322
# File 'lib/libav/stream.rb', line 318

def buffer=(v)
  return if v == @buffer
  @buffer = v
  teardown
end

#decode_frame(packet) ⇒ Object

Called by Libav::Reader.each_frame to decode each frame



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# 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

#fpsObject



289
290
291
292
293
294
# 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

#release_all_framesObject

This method will make the stream release all references to buffered frames. The buffers will be recreated the next time #decode_frame is called.



410
411
412
# File 'lib/libav/stream.rb', line 410

def release_all_frames
  teardown
end

#release_frame(frame) ⇒ Object



399
400
401
402
# 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) ⇒ Object



404
405
406
# File 'lib/libav/stream.rb', line 404

def rewind(count=nil)
  @reader.rewind(count, :stream => self)
end

#scaling?Boolean

Check to see if this frame is being scaled

Returns:

  • (Boolean)


325
326
327
328
329
# 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