diff --git a/Sources/SwiftFFmpeg/AVCodecParser.swift b/Sources/SwiftFFmpeg/AVCodecParser.swift index f6dcd2a..968af4c 100644 --- a/Sources/SwiftFFmpeg/AVCodecParser.swift +++ b/Sources/SwiftFFmpeg/AVCodecParser.swift @@ -3,104 +3,93 @@ // SwiftFFmpeg // // Created by sunlubo on 2018/7/1. +// Copyright © 2018-2020 sun. All rights reserved. // import CFFmpeg -// MARK: - AVCodecParser - -typealias CAVCodecParser = CFFmpeg.AVCodecParser - -public struct AVCodecParser { - let cParserPtr: UnsafeMutablePointer - var cParser: CAVCodecParser { cParserPtr.pointee } - - init(cParserPtr: UnsafeMutablePointer) { - self.cParserPtr = cParserPtr - } - - /// several codec IDs are permitted - public var codecIds: [AVCodecID] { - let list = [ - cParser.codec_ids.0, cParser.codec_ids.1, cParser.codec_ids.2, cParser.codec_ids.3, - ] - return list.map({ AVCodecID(UInt32($0)) }).filter({ $0 != .none }) - } - - /// Get all registered codec parsers. - public static var supportedParsers: [AVCodecParser] { - var list = [AVCodecParser]() - var state: UnsafeMutableRawPointer? - while let fmtPtr = av_parser_iterate(&state) { - list.append(AVCodecParser(cParserPtr: fmtPtr.mutable)) - } - return list - } +public protocol AVCodecParserDelegate: AnyObject { + /// Called when a packet parsed. + func packetParsed(_ packet: AVPacket) } -// MARK: - AVCodecParserContext - -public typealias AVCodecParserResult = ( - UnsafeMutablePointer?, // The parsed buffer or nil if not yet finished. - Int, // The number of bytes of the parsed buffer or zero if not yet finished. - Int // The number of bytes of the input bitstream used. -) - -typealias CAVCodecParserContext = CFFmpeg.AVCodecParserContext +public final class AVCodecParser { + private var native: UnsafeMutablePointer + private var codecContext: AVCodecContext + private weak var delegate: AVCodecParserDelegate? -public final class AVCodecParserContext { - private let codecContext: AVCodecContext - private let cContextPtr: UnsafeMutablePointer - private var cContext: CAVCodecParserContext { cContextPtr.pointee } - - public init?(codecContext: AVCodecContext) { - precondition(codecContext.codec != nil, "'AVCodecContext.codec' must not be nil.") - - guard let ctxPtr = av_parser_init(Int32(codecContext.codec!.id.rawValue)) else { - return nil + public init(codecContext: AVCodecContext, delegate: AVCodecParserDelegate) throws { + guard let codecId = codecContext.codec?.id, + let native = av_parser_init(Int32(codecId.rawValue)) + else { + throw AVError.decoderNotFound } + self.native = native self.codecContext = codecContext - self.cContextPtr = ctxPtr + self.delegate = delegate + } + + deinit { + av_parser_close(native) } - /// Parse a packet. + /// Parse packets. /// /// - Parameters: - /// - data: input buffer. - /// - size: buffer size in bytes without the padding. - /// I.e. the full buffer size is assumed to be `buf_size + AVConstant.inputBufferPaddingSize`. - /// To signal EOF, this should be 0 (so that the last frame can be output). - /// - pts: input presentation timestamp. - /// - dts: input decoding timestamp. - /// - pos: input byte position in stream. - /// - Returns: The parsed result. - /// - Throws: AVError + /// - data: The buffer to parse. + /// - pts: The presentation timestamp. + /// - dts: The decoding timestamp. + /// - pos: The byte position in stream. public func parse( - data: UnsafePointer, - size: Int, + data: UnsafeBufferPointer, pts: Int64 = AVTimestamp.noPTS, dts: Int64 = AVTimestamp.noPTS, pos: Int64 = 0 - ) throws -> AVCodecParserResult { - var buf: UnsafeMutablePointer? - var bufSize: Int32 = 0 - let ret = av_parser_parse2( - cContextPtr, - codecContext.cContextPtr, - &buf, - &bufSize, - data, - Int32(size), - pts, - dts, - pos - ) - try throwIfFail(ret) + ) { + var data = data + var outData: UnsafeMutablePointer? + var outSize: Int32 = 0 + repeat { + let size = av_parser_parse2( + native, + codecContext.cContextPtr, + &outData, &outSize, + data.baseAddress, Int32(data.count), + pts, dts, pos + ) + data = UnsafeBufferPointer(rebasing: data[Int(size)...]) + + guard outSize != 0 else { + continue + } + + let packet = AVPacket() + packet.data = outData + packet.size = Int(outSize) + delegate?.packetParsed(packet) + } while data.count != 0 + } +} + +extension AVCodecParser { - return (buf, Int(bufSize), Int(ret)) + /// Get all registered codec parsers. + public static var supportedParsers: [AVCodecID] { + var codecIds: [AVCodecID] = [] + var state: UnsafeMutableRawPointer? + while let ptr = av_parser_iterate(&state) { + let list = [ + UInt32(ptr.pointee.codec_ids.0), + UInt32(ptr.pointee.codec_ids.1), + UInt32(ptr.pointee.codec_ids.2), + UInt32(ptr.pointee.codec_ids.3), + ].map(AVCodecID.init(rawValue:)).filter({ $0 != .none }) + codecIds.append(contentsOf: list) + } + return codecIds } - deinit { - av_parser_close(cContextPtr) + public convenience init(codec: AVCodec, delegate: AVCodecParserDelegate) throws { + try self.init(codecContext: AVCodecContext(codec: codec), delegate: delegate) } } diff --git a/Sources/SwiftFFmpegExamples/decode_audio.swift b/Sources/SwiftFFmpegExamples/decode_audio.swift index 8ad48be..2f104e9 100644 --- a/Sources/SwiftFFmpegExamples/decode_audio.swift +++ b/Sources/SwiftFFmpegExamples/decode_audio.swift @@ -3,106 +3,78 @@ // SwiftFFmpegExamples // // Created by sunlubo on 2019/1/9. +// Copyright © 2019-2020 sun. All rights reserved. // +import Foundation import SwiftFFmpeg -#if canImport(Darwin) -import Darwin -#else -import Glibc -#endif +class DecodeAudio { + var codecContext: AVCodecContext + var frame = AVFrame() + var data = Data() -private func decode( - codecCtx: AVCodecContext, - frame: AVFrame, - pkt: AVPacket?, - file: UnsafeMutablePointer -) throws { - try codecCtx.sendPacket(pkt) + init() { + let codec = AVCodec.findDecoderById(.MP2)! + codecContext = AVCodecContext(codec: codec) + } - while true { - do { - try codecCtx.receiveFrame(frame) - } catch let err as AVError where err == .tryAgain || err == .eof { - break - } + func save() throws { + try codecContext.openCodec() - let dataSize = codecCtx.sampleFormat.bytesPerSample - for i in 0...allocate( - capacity: inbufSize + AVConstant.inputBufferPaddingSize) - inbuf.initialize(to: 0) - defer { inbuf.deallocate() } + func decode(_ packet: AVPacket) throws { + try codecContext.sendPacket(packet) + while true { + do { + try codecContext.receiveFrame(frame) + } catch let err as AVError where err == .tryAgain || err == .eof { + break + } - // decode until eof - var size = fread(inbuf, 1, inbufSize, inFile) - var data = inbuf - while size > 0 { - // use the parser to split the data into frames - let (buf, bufSize, used) = try parser.parse(data: data, size: size) - pkt.data = buf - pkt.size = bufSize + let size = codecContext.sampleFormat.bytesPerSample + for i in 0.. 0 { - try decode(codecCtx: codecCtx, frame: frame, pkt: pkt, file: outFile) + frame.unref() } + } +} - if size < refillThresh { - memmove(inbuf, data, size) - data = inbuf - let len = fread(data.advanced(by: size), 1, inbufSize - size, inFile) - if len > 0 { - size += len - } - } +func decode_audio() throws { + if CommandLine.argc < 4 { + print("Usage: \(CommandLine.arguments[0]) \(CommandLine.arguments[1]) input_file output_file") + return } - // flush the decoder - pkt.data = nil - pkt.size = 0 - try decode(codecCtx: codecCtx, frame: frame, pkt: pkt, file: outFile) + try DecodeAudio().save() } diff --git a/Sources/SwiftFFmpegExamples/decode_video.swift b/Sources/SwiftFFmpegExamples/decode_video.swift index d26f73d..3818dd0 100644 --- a/Sources/SwiftFFmpegExamples/decode_video.swift +++ b/Sources/SwiftFFmpegExamples/decode_video.swift @@ -3,54 +3,70 @@ // SwiftFFmpegExamples // // Created by sunlubo on 2019/1/9. +// Copyright © 2019-2020 sun. All rights reserved. // +import Foundation import SwiftFFmpeg -#if canImport(Darwin) -import Darwin -#else -import Glibc -#endif +class DecodeVideo { + var codecContext: AVCodecContext + var frame = AVFrame() -private func saveToPGM( - _ buf: UnsafeMutablePointer, - _ wrap: Int, - _ xsize: Int, - _ ysize: Int, - _ filename: String -) { - let file = fopen(filename, "w") - defer { fclose(file) } + init() { + let codec = AVCodec.findDecoderById(.H264)! + codecContext = AVCodecContext(codec: codec) + } + + func save() throws { + try codecContext.openCodec() - let header = String(format: "P5\n%d %d\n%d\n", xsize, ysize, 255) - fwrite(header, 1, header.utf8.count, file) - for i in 0...allocate( - capacity: inbufSize + AVConstant.inputBufferPaddingSize) - inbuf.initialize(to: 0) - defer { inbuf.deallocate() } - - while feof(file) == 0 { - // read raw data from the input file - var size = fread(inbuf, 1, inbufSize, file) - if size == 0 { - break - } - - // use the parser to split the data into frames - var data = inbuf - while size > 0 { - let (buf, bufSize, used) = try parser.parse(data: data, size: size) - pkt.data = buf - pkt.size = bufSize - - data = data.advanced(by: used) - size -= used - - if pkt.size > 0 { - try decode(codecCtx: codecCtx, frame: frame, pkt: pkt, output: output) - } - } - } - - // flush the decoder - try decode(codecCtx: codecCtx, frame: frame, pkt: nil, output: output) + try DecodeVideo().save() }