import CoreMedia
import FFmpegKit
import Libavcodec
import Libavfilter
import Libavformat

func toDictionary(_ native: OpaquePointer?) -> [String: String] {
    var dict = [String: String]()
    if let native {
        var prev: UnsafeMutablePointer<AVDictionaryEntry>?
        while let tag = av_dict_get(native, "", prev, AV_DICT_IGNORE_SUFFIX) {
            dict[String(cString: tag.pointee.key)] = String(cString: tag.pointee.value)
            prev = tag
        }
    }
    return dict
}

extension UnsafeMutablePointer where Pointee == AVCodecContext {
    func getFormat() {
        pointee.get_format = { ctx, fmt -> AVPixelFormat in
            guard let fmt, let ctx else {
                return AV_PIX_FMT_NONE
            }
            var i = 0
            while fmt[i] != AV_PIX_FMT_NONE {
                if fmt[i] == AV_PIX_FMT_VIDEOTOOLBOX {
                    let deviceCtx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VIDEOTOOLBOX)
                    if deviceCtx == nil {
                        break
                    }
                    // 只要有hw_device_ctx就可以了。不需要hw_frames_ctx
                    ctx.pointee.hw_device_ctx = deviceCtx
//                    var framesCtx = av_hwframe_ctx_alloc(deviceCtx)
//                    if let framesCtx {
//                        let framesCtxData = UnsafeMutableRawPointer(framesCtx.pointee.data)
//                            .bindMemory(to: AVHWFramesContext.self, capacity: 1)
//                        framesCtxData.pointee.format = AV_PIX_FMT_VIDEOTOOLBOX
//                        framesCtxData.pointee.sw_format = ctx.pointee.pix_fmt.bestPixelFormat
//                        framesCtxData.pointee.width = ctx.pointee.width
//                        framesCtxData.pointee.height = ctx.pointee.height
//                    }
//                    if av_hwframe_ctx_init(framesCtx) != 0 {
//                        av_buffer_unref(&framesCtx)
//                        break
//                    }
//                    ctx.pointee.hw_frames_ctx = framesCtx
                    return fmt[i]
                }
                i += 1
            }
            return fmt[0]
        }
    }
}

extension AVCodecContext {
    func parseASSEvents() -> Int {
        var subtitleASSEvents = 10
        if subtitle_header_size > 0, let events = String(data: Data(bytes: subtitle_header, count: Int(subtitle_header_size)), encoding: .ascii), let eventsRange = events.range(of: "[Events]") {
            var range = eventsRange.upperBound ..< events.endIndex
            if let eventsRange = events.range(of: "Format:", options: String.CompareOptions(rawValue: 0), range: range, locale: nil) {
                range = eventsRange.upperBound ..< events.endIndex
                if let eventsRange = events.rangeOfCharacter(from: CharacterSet.newlines, options: String.CompareOptions(rawValue: 0), range: range) {
                    range = range.lowerBound ..< eventsRange.upperBound
                    let format = events[range]
                    let fields = format.components(separatedBy: ",")
                    let text = fields.last
                    if let text, text.trimmingCharacters(in: .whitespacesAndNewlines) == "Text" {
                        subtitleASSEvents = fields.count
                    }
                }
            }
        }
        return subtitleASSEvents
    }
}

extension AVCodecParameters {
    mutating func createContext(options: KSOptions?) throws -> UnsafeMutablePointer<AVCodecContext> {
        var codecContextOption = avcodec_alloc_context3(nil)
        guard let codecContext = codecContextOption else {
            throw NSError(errorCode: .codecContextCreate)
        }
        var result = avcodec_parameters_to_context(codecContext, &self)
        guard result == 0 else {
            avcodec_free_context(&codecContextOption)
            throw NSError(errorCode: .codecContextSetParam, avErrorCode: result)
        }
        if codec_type == AVMEDIA_TYPE_VIDEO, options?.hardwareDecode ?? false {
            codecContext.getFormat()
        }
        guard let codec = avcodec_find_decoder(codecContext.pointee.codec_id) else {
            avcodec_free_context(&codecContextOption)
            throw NSError(errorCode: .codecContextFindDecoder, avErrorCode: result)
        }
        codecContext.pointee.codec_id = codec.pointee.id
        codecContext.pointee.flags2 |= AV_CODEC_FLAG2_FAST
        if options?.codecLowDelay == true {
            codecContext.pointee.flags |= AV_CODEC_FLAG_LOW_DELAY
        }
        var avOptions = options?.decoderOptions.avOptions
        if let options {
            var lowres = options.lowres
            if lowres > codec.pointee.max_lowres {
                lowres = codec.pointee.max_lowres
            }
            codecContext.pointee.lowres = Int32(lowres)
            if lowres > 0 {
                av_dict_set_int(&avOptions, "lowres", Int64(lowres), 0)
            }
        }
        result = avcodec_open2(codecContext, codec, &avOptions)
        av_dict_free(&avOptions)
        guard result == 0 else {
            avcodec_free_context(&codecContextOption)
            throw NSError(errorCode: .codesContextOpen, avErrorCode: result)
        }
        return codecContext
    }
}

/**
 Clients who specify AVVideoColorPropertiesKey must specify a color primary, transfer function, and Y'CbCr matrix.
 Most clients will want to specify HD, which consists of:

 AVVideoColorPrimaries_ITU_R_709_2
 AVVideoTransferFunction_ITU_R_709_2
 AVVideoYCbCrMatrix_ITU_R_709_2

 If you require SD colorimetry use:

 AVVideoColorPrimaries_SMPTE_C
 AVVideoTransferFunction_ITU_R_709_2
 AVVideoYCbCrMatrix_ITU_R_601_4

 If you require wide gamut HD colorimetry, you can use:

 AVVideoColorPrimaries_P3_D65
 AVVideoTransferFunction_ITU_R_709_2
 AVVideoYCbCrMatrix_ITU_R_709_2

 If you require 10-bit wide gamut HD colorimetry, you can use:

 AVVideoColorPrimaries_P3_D65
 AVVideoTransferFunction_ITU_R_2100_HLG
 AVVideoYCbCrMatrix_ITU_R_709_2
 */
extension AVColorPrimaries {
    var colorPrimaries: CFString? {
        switch self {
        case AVCOL_PRI_BT470BG:
            return kCVImageBufferColorPrimaries_EBU_3213
        case AVCOL_PRI_SMPTE170M:
            return kCVImageBufferColorPrimaries_SMPTE_C
        case AVCOL_PRI_BT709:
            return kCVImageBufferColorPrimaries_ITU_R_709_2
        case AVCOL_PRI_BT2020:
            return kCVImageBufferColorPrimaries_ITU_R_2020
        default:
            return CVColorPrimariesGetStringForIntegerCodePoint(Int32(rawValue))?.takeUnretainedValue()
        }
    }
}

extension AVColorTransferCharacteristic {
    var transferFunction: CFString? {
        switch self {
        case AVCOL_TRC_SMPTE2084:
            return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ
        case AVCOL_TRC_BT2020_10, AVCOL_TRC_BT2020_12:
            return kCVImageBufferTransferFunction_ITU_R_2020
        case AVCOL_TRC_BT709:
            return kCVImageBufferTransferFunction_ITU_R_709_2
        case AVCOL_TRC_SMPTE240M:
            return kCVImageBufferTransferFunction_SMPTE_240M_1995
        case AVCOL_TRC_LINEAR:
            return kCVImageBufferTransferFunction_Linear
        case AVCOL_TRC_SMPTE428:
            return kCVImageBufferTransferFunction_SMPTE_ST_428_1
        case AVCOL_TRC_ARIB_STD_B67:
            return kCVImageBufferTransferFunction_ITU_R_2100_HLG
        case AVCOL_TRC_GAMMA22, AVCOL_TRC_GAMMA28:
            return kCVImageBufferTransferFunction_UseGamma
        default:
            return CVTransferFunctionGetStringForIntegerCodePoint(Int32(rawValue))?.takeUnretainedValue()
        }
    }
}

extension AVColorSpace {
    var ycbcrMatrix: CFString? {
        switch self {
        case AVCOL_SPC_BT709:
            return kCVImageBufferYCbCrMatrix_ITU_R_709_2
        case AVCOL_SPC_BT470BG, AVCOL_SPC_SMPTE170M:
            return kCVImageBufferYCbCrMatrix_ITU_R_601_4
        case AVCOL_SPC_SMPTE240M:
            return kCVImageBufferYCbCrMatrix_SMPTE_240M_1995
        case AVCOL_SPC_BT2020_CL, AVCOL_SPC_BT2020_NCL:
            return kCVImageBufferYCbCrMatrix_ITU_R_2020
        default:
            return CVYCbCrMatrixGetStringForIntegerCodePoint(Int32(rawValue))?.takeUnretainedValue()
        }
    }
}

extension AVChromaLocation {
    var chroma: CFString? {
        switch self {
        case AVCHROMA_LOC_LEFT:
            return kCVImageBufferChromaLocation_Left
        case AVCHROMA_LOC_CENTER:
            return kCVImageBufferChromaLocation_Center
        case AVCHROMA_LOC_TOP:
            return kCVImageBufferChromaLocation_Top
        case AVCHROMA_LOC_BOTTOM:
            return kCVImageBufferChromaLocation_Bottom
        case AVCHROMA_LOC_TOPLEFT:
            return kCVImageBufferChromaLocation_TopLeft
        case AVCHROMA_LOC_BOTTOMLEFT:
            return kCVImageBufferChromaLocation_BottomLeft
        default:
            return nil
        }
    }
}

extension AVPixelFormat {
    var bitDepth: Int32 {
        let descriptor = av_pix_fmt_desc_get(self)
        return descriptor?.pointee.comp.0.depth ?? 8
    }

    var planeCount: UInt8 {
        if let desc = av_pix_fmt_desc_get(self) {
            switch desc.pointee.nb_components {
            case 3:
                return UInt8(desc.pointee.comp.2.plane + 1)
            case 2:
                return UInt8(desc.pointee.comp.1.plane + 1)
            default:
                return UInt8(desc.pointee.comp.0.plane + 1)
            }
        } else {
            return 1
        }
    }

    var leftShift: UInt8 {
        if [AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV444P10LE].contains(self) {
            return 6
        } else {
            return 0
        }
    }

    // videotoolbox_best_pixel_format
    var bestPixelFormat: AVPixelFormat {
        if let desc = av_pix_fmt_desc_get(self) {
            if desc.pointee.flags & UInt64(AV_PIX_FMT_FLAG_ALPHA) != 0 {
                return AV_PIX_FMT_AYUV64LE
            }
            let depth = desc.pointee.comp.0.depth
            if depth > 10 {
                return desc.pointee.log2_chroma_w == 0 ? AV_PIX_FMT_P416LE : AV_PIX_FMT_P216LE
            }
            if desc.pointee.log2_chroma_w == 0 {
                return depth <= 8 ? AV_PIX_FMT_NV24 : AV_PIX_FMT_P410LE
            }
            if desc.pointee.log2_chroma_h == 0 {
                return depth <= 8 ? AV_PIX_FMT_NV16 : AV_PIX_FMT_P210LE
            }
            return depth <= 8 ? AV_PIX_FMT_NV12 : AV_PIX_FMT_P010LE
        } else {
            return AV_PIX_FMT_NV12
        }
    }

    // swiftlint:disable cyclomatic_complexity
    // avfoundation.m
    func osType(fullRange: Bool = false) -> OSType? {
        switch self {
        case AV_PIX_FMT_MONOBLACK: return kCVPixelFormatType_1Monochrome
//        case AV_PIX_FMT_PAL8: return kCVPixelFormatType_32RGBA
        case AV_PIX_FMT_GRAY8: return kCVPixelFormatType_OneComponent8
        case AV_PIX_FMT_RGB555BE: return kCVPixelFormatType_16BE555
        case AV_PIX_FMT_RGB555LE: return kCVPixelFormatType_16LE555
        case AV_PIX_FMT_RGB565BE: return kCVPixelFormatType_16BE565
        case AV_PIX_FMT_RGB565LE: return kCVPixelFormatType_16LE565
//             PixelBufferPool 无法支持24BGR
//        case AV_PIX_FMT_BGR24: return kCVPixelFormatType_24BGR
        case AV_PIX_FMT_RGB24: return kCVPixelFormatType_24RGB
        case AV_PIX_FMT_0RGB: return kCVPixelFormatType_32ARGB
        case AV_PIX_FMT_ARGB: return kCVPixelFormatType_32ARGB
        case AV_PIX_FMT_BGR0: return kCVPixelFormatType_32BGRA
        case AV_PIX_FMT_BGRA: return kCVPixelFormatType_32BGRA
        case AV_PIX_FMT_0BGR: return kCVPixelFormatType_32ABGR
        case AV_PIX_FMT_RGB0: return kCVPixelFormatType_32RGBA
        case AV_PIX_FMT_RGBA: return kCVPixelFormatType_32RGBA
        case AV_PIX_FMT_BGR48BE, AV_PIX_FMT_BGR48LE: return kCVPixelFormatType_48RGB
        case AV_PIX_FMT_NV12: return fullRange ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
        //  AVSampleBufferDisplayLayer不能显示 kCVPixelFormatType_420YpCbCr8PlanarFullRange,所以换成是kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
        case AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P: return fullRange ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_420YpCbCr8Planar
        case AV_PIX_FMT_P010BE, AV_PIX_FMT_P010LE, AV_PIX_FMT_YUV420P10BE, AV_PIX_FMT_YUV420P10LE: return fullRange ? kCVPixelFormatType_420YpCbCr10BiPlanarFullRange : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
        case AV_PIX_FMT_UYVY422: return kCVPixelFormatType_422YpCbCr8
        case AV_PIX_FMT_YUYV422: return kCVPixelFormatType_422YpCbCr8_yuvs
        case AV_PIX_FMT_NV16: return fullRange ? kCVPixelFormatType_422YpCbCr8BiPlanarFullRange : kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange
        case AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P: return fullRange ? kCVPixelFormatType_422YpCbCr8BiPlanarFullRange : kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange
        case AV_PIX_FMT_Y210BE, AV_PIX_FMT_Y210LE: return kCVPixelFormatType_422YpCbCr10
        case AV_PIX_FMT_P210BE, AV_PIX_FMT_P210LE, AV_PIX_FMT_YUV422P10BE, AV_PIX_FMT_YUV422P10LE: return fullRange ? kCVPixelFormatType_422YpCbCr10BiPlanarFullRange : kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange
        case AV_PIX_FMT_P216BE, AV_PIX_FMT_P216LE, AV_PIX_FMT_YUV422P16BE, AV_PIX_FMT_YUV422P16LE: return kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange
        case AV_PIX_FMT_NV24, AV_PIX_FMT_YUV444P: return fullRange ? kCVPixelFormatType_444YpCbCr8BiPlanarFullRange : kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange
        case AV_PIX_FMT_YUVA444P: return kCVPixelFormatType_4444YpCbCrA8R
        case AV_PIX_FMT_P410BE, AV_PIX_FMT_P410LE, AV_PIX_FMT_YUV444P10BE, AV_PIX_FMT_YUV444P10LE: return fullRange ? kCVPixelFormatType_444YpCbCr10BiPlanarFullRange : kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange
        case AV_PIX_FMT_P416BE, AV_PIX_FMT_P416LE: return kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange
        case AV_PIX_FMT_AYUV64BE, AV_PIX_FMT_AYUV64LE: return kCVPixelFormatType_4444AYpCbCr16
        case AV_PIX_FMT_YUVA444P16BE, AV_PIX_FMT_YUVA444P16LE: return kCVPixelFormatType_4444AYpCbCr16
        default:
            return nil
        }
    }
    // swiftlint:enable cyclomatic_complexity
}

extension AVCodecID {
    var mediaSubType: CMFormatDescription.MediaSubType {
        switch self {
        case AV_CODEC_ID_H263:
            return .h263
        case AV_CODEC_ID_H264:
            return .h264
        case AV_CODEC_ID_HEVC:
            return .hevc
        case AV_CODEC_ID_MPEG1VIDEO:
            return .mpeg1Video
        case AV_CODEC_ID_MPEG2VIDEO:
            return .mpeg2Video
        case AV_CODEC_ID_MPEG4:
            return .mpeg4Video
        case AV_CODEC_ID_VP9:
            return CMFormatDescription.MediaSubType(rawValue: kCMVideoCodecType_VP9)
        case AV_CODEC_ID_AAC:
            return .mpeg4AAC
        case AV_CODEC_ID_AC3:
            return .ac3
        case AV_CODEC_ID_ADPCM_IMA_QT:
            return .appleIMA4
        case AV_CODEC_ID_ALAC:
            return .appleLossless
        case AV_CODEC_ID_AMR_NB:
            return .amr
        case AV_CODEC_ID_EAC3:
            return .enhancedAC3
        case AV_CODEC_ID_GSM_MS:
            return .microsoftGSM
        case AV_CODEC_ID_ILBC:
            return .iLBC
        case AV_CODEC_ID_MP1:
            return .mpegLayer1
        case AV_CODEC_ID_MP2:
            return .mpegLayer2
        case AV_CODEC_ID_MP3:
            return .mpegLayer3
        case AV_CODEC_ID_PCM_ALAW:
            return .aLaw
        case AV_CODEC_ID_PCM_MULAW:
            return .uLaw
        case AV_CODEC_ID_QDMC:
            return .qDesign
        case AV_CODEC_ID_QDM2:
            return .qDesign2
        default:
            return CMFormatDescription.MediaSubType(rawValue: 0)
        }
    }
}

extension AVRational {
    var size: CGSize {
        num > 0 && den > 0 ? CGSize(width: Int(num), height: Int(den)) : CGSize(width: 1, height: 1)
    }
}

extension AVBufferSrcParameters: Equatable {
    public static func == (lhs: AVBufferSrcParameters, rhs: AVBufferSrcParameters) -> Bool {
        lhs.format == rhs.format && lhs.width == rhs.width && lhs.height == rhs.height && lhs.sample_aspect_ratio == rhs.sample_aspect_ratio && lhs.sample_rate == rhs.sample_rate && lhs.ch_layout == rhs.ch_layout
    }

    var arg: String {
        if sample_rate > 0 {
            let fmt = String(cString: av_get_sample_fmt_name(AVSampleFormat(rawValue: format)))
            return "sample_rate=\(sample_rate):sample_fmt=\(fmt):time_base=\(time_base.num)/\(time_base.den):channels=\(ch_layout.nb_channels):channel_layout=\(ch_layout.description)"
        } else {
            return "video_size=\(width)x\(height):pix_fmt=\(format):time_base=\(time_base.num)/\(time_base.den):pixel_aspect=\(sample_aspect_ratio.num)/\(sample_aspect_ratio.den)"
        }
    }
}

extension AVChannelLayout: Equatable {
    public static func == (lhs: AVChannelLayout, rhs: AVChannelLayout) -> Bool {
        var lhs = lhs
        var rhs = rhs
        return av_channel_layout_compare(&lhs, &rhs) == 0
    }
}

extension AVChannelLayout: CustomStringConvertible {
    static let defaultValue = AVChannelLayout(order: AV_CHANNEL_ORDER_NATIVE, nb_channels: 2, u: AVChannelLayout.__Unnamed_union_u(mask: swift_AV_CH_LAYOUT_STEREO), opaque: nil)
    var layoutTag: AudioChannelLayoutTag? {
        KSLog("[audio] FFmepg AVChannelLayout: \(self) order: \(order) mask: \(u.mask)")
        let tag = layoutMapTuple.first { _, mask in
            u.mask == mask
        }?.tag
        if let tag {
            return tag
        } else {
            KSLog("[audio] can not find AudioChannelLayoutTag FFmepg channelLayout: \(self) order: \(order) mask: \(u.mask)")
            return nil
        }
    }

    public var description: String {
        var channelLayout = self
        var str = [Int8](repeating: 0, count: 64)
        _ = av_channel_layout_describe(&channelLayout, &str, str.count)
        return String(cString: str)
    }
}

extension AVRational: Equatable {
    public static func == (lhs: AVRational, rhs: AVRational) -> Bool {
        lhs.num == rhs.num && rhs.den == rhs.den
    }
}

public struct AVError: Error, Equatable {
    public var code: Int32
    public var message: String

    init(code: Int32) {
        self.code = code
        message = String(avErrorCode: code)
    }
}

public extension Dictionary where Key == String {
    var avOptions: OpaquePointer? {
        var avOptions: OpaquePointer?
        forEach { key, value in
            if let i = value as? Int64 {
                av_dict_set_int(&avOptions, key, i, 0)
            } else if let i = value as? Int {
                av_dict_set_int(&avOptions, key, Int64(i), 0)
            } else if let string = value as? String {
                av_dict_set(&avOptions, key, string, 0)
            } else if let dic = value as? Dictionary {
                let string = dic.map { "\($0.0)=\($0.1)" }.joined(separator: "\r\n")
                av_dict_set(&avOptions, key, string, 0)
            } else if let array = value as? [String] {
                let string = array.joined(separator: "+")
                av_dict_set(&avOptions, key, string, 0)
            }
        }
        return avOptions
    }
}

extension String {
    init(avErrorCode code: Int32) {
        let buf = UnsafeMutablePointer<Int8>.allocate(capacity: Int(AV_ERROR_MAX_STRING_SIZE))
        buf.initialize(repeating: 0, count: Int(AV_ERROR_MAX_STRING_SIZE))
        defer { buf.deallocate() }
        self = String(cString: av_make_error_string(buf, Int(AV_ERROR_MAX_STRING_SIZE), code))
    }
}

public extension NSError {
    convenience init(errorCode: KSPlayerErrorCode, avErrorCode: Int32) {
        let underlyingError = AVError(code: avErrorCode)
        self.init(errorCode: errorCode, userInfo: [NSUnderlyingErrorKey: underlyingError])
    }
}

public extension AVError {
    /// Resource temporarily unavailable
    static let tryAgain = AVError(code: swift_AVERROR(EAGAIN))
    /// Invalid argument
    static let invalidArgument = AVError(code: swift_AVERROR(EINVAL))
    /// Cannot allocate memory
    static let outOfMemory = AVError(code: swift_AVERROR(ENOMEM))
    /// The value is out of range
    static let outOfRange = AVError(code: swift_AVERROR(ERANGE))
    /// The value is not valid
    static let invalidValue = AVError(code: swift_AVERROR(EINVAL))
    /// Function not implemented
    static let noSystem = AVError(code: swift_AVERROR(ENOSYS))

    /// Bitstream filter not found
    static let bitstreamFilterNotFound = AVError(code: swift_AVERROR_BSF_NOT_FOUND)
    /// Internal bug, also see `bug2`
    static let bug = AVError(code: swift_AVERROR_BUG)
    /// Buffer too small
    static let bufferTooSmall = AVError(code: swift_AVERROR_BUFFER_TOO_SMALL)
    /// Decoder not found
    static let decoderNotFound = AVError(code: swift_AVERROR_DECODER_NOT_FOUND)
    /// Demuxer not found
    static let demuxerNotFound = AVError(code: swift_AVERROR_DEMUXER_NOT_FOUND)
    /// Encoder not found
    static let encoderNotFound = AVError(code: swift_AVERROR_ENCODER_NOT_FOUND)
    /// End of file
    static let eof = AVError(code: swift_AVERROR_EOF)
    /// Immediate exit was requested; the called function should not be restarted
    static let exit = AVError(code: swift_AVERROR_EXIT)
    /// Generic error in an external library
    static let external = AVError(code: swift_AVERROR_EXTERNAL)
    /// Filter not found
    static let filterNotFound = AVError(code: swift_AVERROR_FILTER_NOT_FOUND)
    /// Invalid data found when processing input
    static let invalidData = AVError(code: swift_AVERROR_INVALIDDATA)
    /// Muxer not found
    static let muxerNotFound = AVError(code: swift_AVERROR_MUXER_NOT_FOUND)
    /// Option not found
    static let optionNotFound = AVError(code: swift_AVERROR_OPTION_NOT_FOUND)
    /// Not yet implemented in FFmpeg, patches welcome
    static let patchWelcome = AVError(code: swift_AVERROR_PATCHWELCOME)
    /// Protocol not found
    static let protocolNotFound = AVError(code: swift_AVERROR_PROTOCOL_NOT_FOUND)
    /// Stream not found
    static let streamNotFound = AVError(code: swift_AVERROR_STREAM_NOT_FOUND)
    /// This is semantically identical to `bug`. It has been introduced in Libav after our `bug` and
    /// with a modified value.
    static let bug2 = AVError(code: swift_AVERROR_BUG2)
    /// Unknown error, typically from an external library
    static let unknown = AVError(code: swift_AVERROR_UNKNOWN)
    ///  Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
    static let experimental = AVError(code: swift_AVERROR_EXPERIMENTAL)
    /// Input changed between calls. Reconfiguration is required. (can be OR-ed with `outputChanged`)
    static let inputChanged = AVError(code: swift_AVERROR_INPUT_CHANGED)
    /// Output changed between calls. Reconfiguration is required. (can be OR-ed with `inputChanged`)
    static let outputChanged = AVError(code: swift_AVERROR_OUTPUT_CHANGED)

    /* HTTP & RTSP errors */
    static let httpBadRequest = AVError(code: swift_AVERROR_HTTP_BAD_REQUEST)
    static let httpUnauthorized = AVError(code: swift_AVERROR_HTTP_UNAUTHORIZED)
    static let httpForbidden = AVError(code: swift_AVERROR_HTTP_FORBIDDEN)
    static let httpNotFound = AVError(code: swift_AVERROR_HTTP_NOT_FOUND)
    static let httpOther4xx = AVError(code: swift_AVERROR_HTTP_OTHER_4XX)
    static let httpServerError = AVError(code: swift_AVERROR_HTTP_SERVER_ERROR)
}
