ffi-libav 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +1 -0
- data/README.md +114 -3
- data/Rakefile +27 -0
- data/examples/afs_test.rb +92 -0
- data/lib/ffi-libav.rb +1 -0
- data/lib/ffi/libav.i +1 -0
- data/lib/ffi/libav.rb +34 -1
- data/lib/libav/frame.rb +24 -12
- data/lib/libav/reader.rb +138 -9
- data/lib/libav/stream.rb +342 -64
- data/lib/libav/version.rb +1 -1
- data/spec/frame_spec.rb +274 -0
- data/spec/reader_spec.rb +22 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/stream_spec.rb +684 -0
- metadata +14 -10
data/lib/libav/stream.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
require 'ffi/libav'
|
2
|
+
require 'thread'
|
2
3
|
|
3
4
|
# Generic Stream class. Most of the logic resides in Libav::Stream::Video.
|
4
5
|
module Libav::Stream
|
6
|
+
extend Forwardable
|
5
7
|
include FFI::Libav
|
6
8
|
|
7
|
-
|
9
|
+
# Exception raised by Stream#seek when an AFS seek is unable to find the
|
10
|
+
# target frame. This can happen for some codecs such as MPEG-TS when
|
11
|
+
# attempting to access the earliest frames.
|
12
|
+
class FrameNotFound < Exception; end
|
13
|
+
|
14
|
+
attr_reader :reader, :av_stream, :av_codec_ctx, :buffer
|
15
|
+
def_delegator :@av_stream, :[], :[]
|
8
16
|
|
9
17
|
def initialize(p={})
|
10
18
|
@reader = p[:reader] or raise ArgumentError, "no :reader"
|
@@ -34,28 +42,29 @@ module Libav::Stream
|
|
34
42
|
@av_stream[:index]
|
35
43
|
end
|
36
44
|
|
37
|
-
def decode_frame(packet)
|
38
|
-
return false
|
39
|
-
raise NotImplementedError, "decode_frame() not defined for #{self.class}"
|
40
|
-
end
|
41
|
-
|
42
45
|
# Loop through each frame of this stream
|
43
|
-
|
44
|
-
|
46
|
+
#
|
47
|
+
# Arguments:
|
48
|
+
# [:buffer] Number of frames to buffer
|
49
|
+
#
|
50
|
+
# Note that when using the :buffer argument, the caller MUST call
|
51
|
+
# Frame#release when it is done processing a frame.
|
52
|
+
#
|
53
|
+
def each_frame(opt={}, &block)
|
54
|
+
@reader.each_frame(opt.merge({ :stream => index }), &block)
|
45
55
|
end
|
46
56
|
|
47
57
|
# Get the next frame in the stream
|
48
58
|
def next_frame
|
49
|
-
|
50
|
-
each_frame { |f| frame = f; break }
|
51
|
-
frame
|
59
|
+
each_frame { |f| break f }
|
52
60
|
end
|
53
61
|
|
54
62
|
# Skip some +n+ frames in the stream
|
55
63
|
def skip_frames(n)
|
64
|
+
# XXX not sure if this is true
|
56
65
|
raise RuntimeError, "Cannot skip frames when discarding all frames" if
|
57
66
|
discard == :all
|
58
|
-
each_frame { |f| n -= 1
|
67
|
+
each_frame { |f| f.release; n -= 1; break if n == 0}
|
59
68
|
end
|
60
69
|
|
61
70
|
# Seek to a specific location within the stream; the location can be either
|
@@ -63,32 +72,158 @@ module Libav::Stream
|
|
63
72
|
#
|
64
73
|
# Arguments:
|
65
74
|
# [:pts] PTS location
|
66
|
-
# [:
|
75
|
+
# [:byte] Byte location
|
67
76
|
# [:backward] Seek backward
|
68
77
|
# [:any] Seek to non-key frames
|
69
78
|
#
|
79
|
+
# Examples:
|
80
|
+
# seek :frame => 3
|
81
|
+
# seek :pts => 90218390
|
82
|
+
# seek :pos => 0
|
83
|
+
#
|
84
|
+
# Last index of afs data should contain last frame read.
|
70
85
|
def seek(p={})
|
71
|
-
p = { :pts => p } unless p.is_a? Hash
|
72
86
|
|
73
|
-
raise ArgumentError, ":pts and :
|
74
|
-
|
87
|
+
raise ArgumentError, ":pts, :frame, and :byte are mutually exclusive" if
|
88
|
+
([:byte, :frame, :pts] & p.keys).size != 1
|
75
89
|
|
76
|
-
|
77
|
-
flags = 0
|
78
|
-
flags |= AVSEEK_FLAG_BYTE if p[:
|
79
|
-
flags |=
|
90
|
+
# Default seek arguments
|
91
|
+
flags = p[:backward] ? AVSEEK_FLAG_BACKWARD : 0
|
92
|
+
flags |= AVSEEK_FLAG_BYTE if p[:byte]
|
93
|
+
flags |= AVSEEK_FLAG_FRAME if p[:frame]
|
80
94
|
flags |= AVSEEK_FLAG_ANY if p[:any]
|
95
|
+
seek_args = [[p[:pts] || p[:frame] || p[:byte], flags]]
|
96
|
+
|
97
|
+
# If we have afs data, and our target frame is within the data, replace
|
98
|
+
# seek_args with an array of arguments for seeking to each key frame
|
99
|
+
# preceding our target frame, in reverse-chronological order. The idea is
|
100
|
+
# that sometimes libav seek puts us someplace strange. If we start at the
|
101
|
+
# closest key frame to our target frame, and the work backwards in the
|
102
|
+
# stream, libav will eventually put us in a place where we can read to the
|
103
|
+
# target frame.
|
104
|
+
if @afs and !@afs.empty? and
|
105
|
+
(p[:frame] && p[:frame] <= @afs.last[0] or
|
106
|
+
p[:pts] && p[:pts] <= @afs.last[1] or
|
107
|
+
p[:byte] && p[:byte] <= @afs.last[2])
|
108
|
+
|
109
|
+
# Note that we set the flags to AVSEEK_FLAG_BACKWARD for each of our arg
|
110
|
+
# sets. This is just because by observation the BACKWARD flag seems to
|
111
|
+
# give us better results regardless of the direction of our seek.
|
112
|
+
seek_args = @afs.select do |data|
|
113
|
+
p[:frame] && data[0] < p[:frame] or
|
114
|
+
p[:pts] && data[1] < p[:pts] or
|
115
|
+
p[:byte] && data[2] && data[2] < p[:byte]
|
116
|
+
end.map { |d| [ d[1], AVSEEK_FLAG_BACKWARD, true ] }.reverse
|
117
|
+
|
118
|
+
# Throw the seek 0 into the end of the list as a fall back
|
119
|
+
seek_args.push [0, AVSEEK_FLAG_BACKWARD, true]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Disable afs updating because we're about to seek. If the seek ends up
|
123
|
+
# within our afs data, it will be re-enabled.
|
124
|
+
@update_afs = false
|
125
|
+
|
126
|
+
# We will fill this frame in the next loop, and use it after the loop is
|
127
|
+
# complete.
|
128
|
+
frame = nil
|
129
|
+
|
130
|
+
# Loop through each set of arguments provided. Seek to the timestamp in
|
131
|
+
# the arguments and then verify that we haven't gone past our target.
|
132
|
+
seek_args.each do |ts, flags, afs|
|
133
|
+
|
134
|
+
# Flush the codec so we don't get buffered frames after the seek
|
135
|
+
avcodec_flush_buffers(@av_codec_ctx)
|
136
|
+
|
137
|
+
# Kick off our seek
|
138
|
+
rc = avformat_seek_file(@reader.av_format_ctx, @av_stream[:index],
|
139
|
+
ts, ts, ts, flags)
|
140
|
+
raise RuntimeError, "avformat_seek_file(#{ts}, #{flags.to_s(16)})" +
|
141
|
+
" failed, #{rc}" if rc < 0
|
142
|
+
|
143
|
+
# If we performed an afs seek, we need to enable afs data updating, and
|
144
|
+
# if not we need to disable it.
|
145
|
+
@update_afs = (afs == true)
|
146
|
+
|
147
|
+
# We also need to clear the frame and pts oafsets. If this is a afs
|
148
|
+
# seek, these will be re-set to their new values later.
|
149
|
+
@frame_oafset = 0
|
150
|
+
@pts_oafset = 0
|
151
|
+
|
152
|
+
# If this wasn't an afs seek, we are done here.
|
153
|
+
return true unless afs
|
154
|
+
|
155
|
+
# Grab the next frame, and see if it precedes, or is, our target frame.
|
156
|
+
# If no frame is returned, we hit EOF, so try seeking a little earlier.
|
157
|
+
frame = next_frame or next
|
158
|
+
frame.release
|
159
|
+
|
160
|
+
# Although the code is only needed in this loop when we're seeking by
|
161
|
+
# :frame, we're going to adjust our frame oafset here. It's a little
|
162
|
+
# slower, but less complicated overall.
|
163
|
+
#
|
164
|
+
# Find the afs entry for this frame (it's guaranteed to be a key
|
165
|
+
# frame), and use that to adjust the number of the frame we just read.
|
166
|
+
#
|
167
|
+
# If we're unable to find a match in the afs data, then we've gone past
|
168
|
+
# the end of the afs data, and we should try another seek timestamp.
|
169
|
+
seek_afs = @afs.find { |f| f[1] == frame.pts } or next
|
170
|
+
raise RuntimeError, "afs data mismatch: afs #{seek_afs}, " +
|
171
|
+
"frame [#{frame.number}, #{frame.pts}, #{frame.pos}]" if
|
172
|
+
frame.pos != seek_afs[2]
|
173
|
+
|
174
|
+
# Adjust our frame oafset, and use that to adjust our frame number
|
175
|
+
@frame_oafset = -1 * (frame.number - seek_afs[0])
|
176
|
+
frame.number += @frame_oafset
|
177
|
+
|
178
|
+
# If this frame precedes or is our target frame, then the seek was
|
179
|
+
# successful, and we can break out of this loop.
|
180
|
+
break if p[:pts] && p[:pts] >= frame.pts or
|
181
|
+
p[:frame] && p[:frame] >= frame.number or
|
182
|
+
p[:byte] && p[:byte] >= frame.pos
|
183
|
+
|
184
|
+
# This frame was after our target frame, so ignore it.
|
185
|
+
frame = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
# If we went through all the seek_args without finding a single frame
|
189
|
+
# before our target frame, throw an exception.
|
190
|
+
unless frame
|
191
|
+
mode = (p.keys & [:pts, :frame, :byte]).first
|
192
|
+
raise FrameNotFound, "Unable to find frame {%s: %d}" % [mode, p[mode]]
|
193
|
+
end
|
194
|
+
|
195
|
+
# Rewind the frame so we can access it in the next loop
|
196
|
+
rewind(1)
|
197
|
+
|
198
|
+
# Also mark afs as updating because we're within the known afs data region.
|
199
|
+
@update_afs = true
|
200
|
+
|
201
|
+
# Now that we have corrected oafsets, we need to read the next few frames
|
202
|
+
# until we encounter the target frame or a frame after it (possible for
|
203
|
+
# pts values that are slightly off).
|
204
|
+
each_frame do |frame|
|
205
|
+
frame.release
|
206
|
+
break if p[:pts] && frame.pts >= p[:pts] or
|
207
|
+
p[:frame] && frame.number >= p[:frame] or
|
208
|
+
p[:byte] && frame.pos >= p[:byte]
|
209
|
+
end
|
210
|
+
|
211
|
+
# Alright, let's rewind by one frame so the next call to #each_frame will
|
212
|
+
# yield the frame they requested.
|
213
|
+
rewind(1)
|
214
|
+
|
215
|
+
return true
|
216
|
+
end
|
81
217
|
|
82
|
-
|
83
|
-
|
84
|
-
true
|
218
|
+
def format_afs(a)
|
219
|
+
"{frame: %d, pts: %d, pos: %d}" % [*a]
|
85
220
|
end
|
86
221
|
end
|
87
222
|
|
88
223
|
class Libav::Stream::Video
|
89
224
|
include Libav::Stream
|
90
225
|
|
91
|
-
attr_reader :
|
226
|
+
attr_reader :width, :height, :pixel_format, :reader, :afs
|
92
227
|
|
93
228
|
def initialize(p={})
|
94
229
|
super(p)
|
@@ -97,14 +232,23 @@ class Libav::Stream::Video
|
|
97
232
|
@width = p[:widht] || @av_codec_ctx[:width]
|
98
233
|
@height = p[:height] || @av_codec_ctx[:height]
|
99
234
|
@pixel_format = p[:pixel_format] || @av_codec_ctx[:pix_fmt]
|
100
|
-
init_scaling
|
101
235
|
|
102
|
-
# Our
|
103
|
-
@
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
236
|
+
# Our stream index info
|
237
|
+
@afs = p[:afs]
|
238
|
+
@update_afs = @afs.nil? == false
|
239
|
+
|
240
|
+
# Our array and queues for raw frames and scaled frames
|
241
|
+
@raw_frames = []
|
242
|
+
@raw_queue = Queue.new
|
243
|
+
@scaled_frames = []
|
244
|
+
@scaled_queue = Queue.new
|
245
|
+
|
246
|
+
# Number of frames to buffer (default is disabled, 0)
|
247
|
+
@buffer = 0
|
248
|
+
|
249
|
+
# When this is set to true when all raw and scaled frames have been set up
|
250
|
+
# by setup().
|
251
|
+
@decode_ready = false
|
108
252
|
|
109
253
|
# Pointer used to denote that decode frame was successful
|
110
254
|
@frame_finished = FFI::MemoryPointer.new :int
|
@@ -118,7 +262,8 @@ class Libav::Stream::Video
|
|
118
262
|
# address. Instead of allocating and freeing repeatedly, we're going to
|
119
263
|
# alloc it once now and reuse it for each decoded frame.
|
120
264
|
@last_dts = nil
|
121
|
-
@
|
265
|
+
@last_pos = nil
|
266
|
+
@opaque = FFI::MemoryPointer.new :uint64, 2
|
122
267
|
|
123
268
|
@av_codec_ctx[:get_buffer] = \
|
124
269
|
FFI::Function.new(:int, [AVCodecContext.ptr, AVFrame.ptr]) do |ctx,frame|
|
@@ -127,77 +272,202 @@ class Libav::Stream::Video
|
|
127
272
|
ret = avcodec_default_get_buffer(ctx, frame)
|
128
273
|
|
129
274
|
# Update the :opaque field point at a copy of the last pts we've seen.
|
130
|
-
@opaque.
|
275
|
+
@opaque.put_int64(0, @last_dts)
|
276
|
+
@opaque.put_uint64(8, @last_pos)
|
131
277
|
frame[:opaque] = @opaque
|
132
278
|
|
133
279
|
ret
|
134
280
|
end
|
281
|
+
|
282
|
+
# Initialize our frame number oafset, and our pts oafset. These two are
|
283
|
+
# modified by seek(), and used by decode_frame().
|
284
|
+
@frame_oafset = 0
|
285
|
+
@pts_oafset = 0
|
286
|
+
|
135
287
|
end
|
136
288
|
|
137
289
|
def fps
|
138
|
-
@av_stream[:r_frame_rate]
|
290
|
+
@av_stream[:r_frame_rate].to_f
|
139
291
|
end
|
140
292
|
|
141
293
|
# Set the +width+ of the frames returned by +decode_frame+
|
142
294
|
def width=(width)
|
295
|
+
return if width == @width
|
143
296
|
@width = width
|
144
|
-
|
297
|
+
teardown
|
145
298
|
end
|
146
299
|
|
147
300
|
# Set the +height+ of the frames returned by +decode_frame+
|
148
301
|
def height=(height)
|
302
|
+
return if height == @height
|
149
303
|
@height = height
|
150
|
-
|
304
|
+
teardown
|
151
305
|
end
|
152
306
|
|
153
307
|
# Set the +pixel format+ of the frames returned by +decode_frame+
|
154
308
|
def pixel_format=(pixel_format)
|
309
|
+
return if pixel_format == @pixel_format
|
155
310
|
@pixel_format = pixel_format
|
156
|
-
|
311
|
+
teardown
|
312
|
+
end
|
313
|
+
|
314
|
+
# Set the buffer size
|
315
|
+
def buffer=(v)
|
316
|
+
return if v == @buffer
|
317
|
+
@buffer = v
|
318
|
+
teardown
|
319
|
+
end
|
320
|
+
|
321
|
+
# Check to see if this frame is being scaled
|
322
|
+
def scaling?
|
323
|
+
@av_codec_ctx[:width] != @width or
|
324
|
+
@av_codec_ctx[:height] != @height or
|
325
|
+
@av_codec_ctx[:pix_fmt] != @pixel_format
|
157
326
|
end
|
158
327
|
|
159
328
|
# Called by Libav::Reader.each_frame to decode each frame
|
160
329
|
def decode_frame(packet)
|
161
|
-
|
330
|
+
setup unless @decode_ready
|
162
331
|
|
332
|
+
# Save off our dts and pos for the buffer allocation callback we declared
|
333
|
+
# in #initialize
|
163
334
|
@last_dts = packet[:dts]
|
164
|
-
|
165
|
-
|
335
|
+
@last_pos = packet[:pos]
|
336
|
+
|
337
|
+
# Grab our raw frame off the raw frames queue. This will block if the
|
338
|
+
# caller is still using all the previous frames.
|
339
|
+
raw_frame = @raw_queue.shift
|
340
|
+
|
341
|
+
# Let the reader know we're stomping on this frame
|
342
|
+
@reader.frame_dirty(raw_frame)
|
343
|
+
|
344
|
+
# Call the decode function on our packet
|
345
|
+
avcodec_get_frame_defaults(raw_frame.av_frame)
|
346
|
+
rc = avcodec_decode_video2(@av_codec_ctx, raw_frame.av_frame,
|
166
347
|
@frame_finished, packet)
|
167
|
-
raise RuntimeError, "avcodec_decode_video2() failed, rc=#{rc}" if rc < 0
|
168
|
-
return if @frame_finished.read_int == 0
|
169
348
|
|
170
|
-
|
171
|
-
|
349
|
+
# Now, if we didn't get a frame, for one reason or another, let's throw the
|
350
|
+
# raw frame back on our queue.
|
351
|
+
if rc < 0 or @frame_finished.read_int == 0
|
352
|
+
@raw_queue.push raw_frame
|
353
|
+
return nil
|
354
|
+
end
|
355
|
+
|
356
|
+
raw_frame.number = @av_codec_ctx[:frame_number].to_i + @frame_oafset
|
357
|
+
raw_frame.pts = raw_frame.av_frame[:opaque].get_int64(0) + @pts_oafset
|
358
|
+
raw_frame.pos = raw_frame.av_frame[:opaque].get_uint64(8)
|
359
|
+
|
360
|
+
# AFS Data is broken down as follows:
|
361
|
+
# [ [frame number, pts, pos, true], # entry for first key frame
|
362
|
+
# [frame number, pts, pos, true], # entry for second key frame
|
363
|
+
# [frame number, pts, pos, true], # entry for N-th key frame
|
364
|
+
# [frame number, pts, pos, false], # optional, last non-key frame
|
365
|
+
# ]
|
366
|
+
if @update_afs and (@afs.empty? or raw_frame.number > @afs.last[0])
|
367
|
+
@afs.pop unless @afs.empty? or @afs.last[-1] == true
|
368
|
+
@afs << [ raw_frame.number, raw_frame.pts,
|
369
|
+
raw_frame.pos, raw_frame.key_frame? ]
|
370
|
+
end
|
371
|
+
|
372
|
+
# If we're scaling, or not buffering, throw the raw frame back on the
|
373
|
+
# queue; it's the only one we have
|
374
|
+
@raw_queue.push raw_frame if @swscale_ctx or @buffer == 0
|
375
|
+
|
376
|
+
# If we're not scaling at this point, we need to return the raw frame to
|
377
|
+
# the caller. This is the non-buffering, non-scaling return point.
|
378
|
+
return raw_frame unless @swscale_ctx
|
379
|
+
|
380
|
+
# Let's grab a scaled frame from our queue
|
381
|
+
scaled_frame = @scaled_queue.shift
|
382
|
+
|
383
|
+
# Let the reader know we're stomping on this frame
|
384
|
+
@reader.frame_dirty(scaled_frame)
|
385
|
+
|
386
|
+
# scale the frame
|
387
|
+
raw_frame.scale(:scale_ctx => @swscale_ctx,
|
388
|
+
:output_frame => scaled_frame)
|
389
|
+
|
390
|
+
# Throw the scaled frame back on the queue if we're not buffering
|
391
|
+
@scaled_queue.push scaled_frame if @buffer == 0
|
392
|
+
|
393
|
+
scaled_frame
|
394
|
+
end
|
172
395
|
|
173
|
-
|
396
|
+
def release_frame(frame)
|
397
|
+
@scaled_queue.push frame if @scaled_frames.include? frame
|
398
|
+
@raw_queue.push frame if @raw_frames.include? frame
|
399
|
+
end
|
400
|
+
|
401
|
+
def rewind(count=nil)
|
402
|
+
@reader.rewind(count, :stream => self)
|
403
|
+
end
|
174
404
|
|
175
|
-
|
176
|
-
|
405
|
+
# This method will make the stream release all references to buffered frames.
|
406
|
+
# The buffers will be recreated the next time #decode_frame is called.
|
407
|
+
def release_all_frames
|
408
|
+
teardown
|
177
409
|
end
|
178
410
|
|
179
411
|
private
|
180
412
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
413
|
+
# Initialize our raw and scaled frames along with our scaling context
|
414
|
+
def setup
|
415
|
+
return if @decode_ready
|
416
|
+
|
417
|
+
# call teardown() now just to make sure everything is released
|
418
|
+
teardown
|
419
|
+
|
420
|
+
# If @buffer is set to zero, we aren't buffering, but we will need one raw
|
421
|
+
# frame and one scaled frame.
|
422
|
+
buffer = @buffer
|
423
|
+
buffer += 1 if buffer == 0
|
424
|
+
|
425
|
+
# Let's allocate our raw frames. If we're scaling, we only need one raw
|
426
|
+
# frame, otherwise we'll need more raw frames.
|
427
|
+
( scaling? ? 1 : buffer ).times do
|
428
|
+
frame = Libav::Frame::Video.new :stream => self, :alloc => false,
|
429
|
+
:width => @av_codec_ctx[:width],
|
430
|
+
:height => @av_codec_ctx[:height],
|
431
|
+
:pixel_format => @av_codec_ctx[:pix_fmt]
|
432
|
+
@raw_frames.push frame
|
433
|
+
@raw_queue.push frame
|
434
|
+
end
|
435
|
+
|
436
|
+
# If we're scaling, allocate our scaled frames and scaling context
|
437
|
+
if scaling?
|
438
|
+
|
439
|
+
# allocate our scaled frames
|
440
|
+
buffer.times do
|
441
|
+
frame = Libav::Frame::Video.new(:width => @width,
|
442
|
+
:height => @height,
|
443
|
+
:pixel_format => @pixel_format,
|
444
|
+
:stream => self)
|
445
|
+
@scaled_frames.push frame
|
446
|
+
@scaled_queue.push frame
|
447
|
+
end
|
185
448
|
|
186
|
-
|
187
|
-
|
188
|
-
|
449
|
+
# Let's throw together a scaling context
|
450
|
+
@swscale_ctx = sws_getContext(@av_codec_ctx[:width],
|
451
|
+
@av_codec_ctx[:height],
|
452
|
+
@av_codec_ctx[:pix_fmt],
|
453
|
+
@width, @height, @pixel_format,
|
454
|
+
SWS_BICUBIC, nil, nil, nil) or
|
455
|
+
raise NoMemoryError, "sws_getContext() failed"
|
456
|
+
end
|
189
457
|
|
190
|
-
@
|
191
|
-
|
192
|
-
@av_codec_ctx[:pix_fmt],
|
193
|
-
@width, @height, @pixel_format,
|
194
|
-
SWS_BICUBIC, nil, nil, nil) or
|
195
|
-
raise NoMemoryError, "sws_getContext() failed"
|
458
|
+
@decode_ready = true
|
459
|
+
end
|
196
460
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
461
|
+
# Release all references to our frames (raw & scaled), and free our scaling
|
462
|
+
# context.
|
463
|
+
def teardown
|
464
|
+
@raw_frames.clear
|
465
|
+
@raw_queue.clear
|
466
|
+
@scaled_frames.clear
|
467
|
+
@scaled_queue.clear
|
468
|
+
sws_freeContext(@swscale_ctx) if @swscale_ctx
|
469
|
+
@swscale_ctx = nil
|
470
|
+
@decode_ready = false
|
201
471
|
end
|
202
472
|
end
|
203
473
|
|
@@ -208,4 +478,12 @@ class Libav::Stream::Unsupported
|
|
208
478
|
super(p)
|
209
479
|
self.discard = :all
|
210
480
|
end
|
481
|
+
|
482
|
+
def buffer=(v)
|
483
|
+
# nothing to be done here
|
484
|
+
end
|
485
|
+
|
486
|
+
def decode_frame(packet)
|
487
|
+
return nil
|
488
|
+
end
|
211
489
|
end
|