diff --git a/src/ffmpeg.cr b/src/ffmpeg.cr index 5597318..6842686 100644 --- a/src/ffmpeg.cr +++ b/src/ffmpeg.cr @@ -36,4 +36,14 @@ module FFmpeg return "unknown" if success < 0 String.new(buffer.to_unsafe) # unsafe as it's \0 terminated end + + def self.mktag(a : Char, b : Char, c : Char, d : Char) + (a.ord) | (b.ord << 8) | (c.ord << 16) | (d.ord.to_u << 24) + end + + def self.fferrtag(a : Char, b : Char, c : Char, d : Char) + -(mktag(a, b, c, d)) + end + + AVERROR_EOF = fferrtag('E', 'O', 'F', ' ') end diff --git a/src/ffmpeg/codec.cr b/src/ffmpeg/codec.cr index c5a394d..3c3365a 100644 --- a/src/ffmpeg/codec.cr +++ b/src/ffmpeg/codec.cr @@ -63,8 +63,13 @@ class FFmpeg::Codec getter frame : Frame { Frame.new } - def decode(packet : Packet, &) - success = LibAV::Codec.send_packet(@context, packet) + def decode(packet : Packet?, &) + success = if packet + LibAV::Codec.send_packet(@context, packet) + else + # flush the codec for any final frames + LibAV::Codec.send_packet(@context, Pointer(FFmpeg::LibAV::Codec::Packet).null) + end raise "failed to read packet with #{success}: #{FFmpeg.get_error_message(success)}" if success < 0 loop do diff --git a/src/ffmpeg/format.cr b/src/ffmpeg/format.cr index 2249776..ab4f047 100644 --- a/src/ffmpeg/format.cr +++ b/src/ffmpeg/format.cr @@ -118,7 +118,15 @@ class FFmpeg::Format def read(&) status = LibAV::Format.read_frame(@context, packet) - raise "failed to read a frame with #{status}: #{FFmpeg.get_error_message(status)}" if status < 0 + + if status < 0 + case status + when AVERROR_EOF + raise IO::EOFError.new(FFmpeg.get_error_message(status)) + else + raise "failed to read a frame with #{status}: #{FFmpeg.get_error_message(status)}" + end + end begin yield packet diff --git a/src/ffmpeg/video.cr b/src/ffmpeg/video.cr index f07abc9..fdb4787 100644 --- a/src/ffmpeg/video.cr +++ b/src/ffmpeg/video.cr @@ -69,15 +69,21 @@ abstract class FFmpeg::Video seek(0, SeekStyle::Frame) - Log.trace { "extracting frames" } - while !closed? - format.read do |packet| - if packet.stream_index == stream_index - codec.decode(packet) do |frame| - yield frame + begin + Log.trace { "extracting frames" } + while !closed? + format.read do |packet| + if packet.stream_index == stream_index + codec.decode(packet) do |frame| + yield frame + end end end end + rescue error : IO::EOFError + codec.decode(nil) do |frame| + yield frame + end end ensure close