package ffmpeg /* #include #include */ import "C" import "unsafe" // FormatContext represents the input/output format context type FormatContext struct { ptr *C.AVFormatContext } // AllocFormatContext allocates a format context func AllocFormatContext() *FormatContext { return &FormatContext{ ptr: C.avformat_alloc_context(), } } // Free frees the format context func (fc *FormatContext) Free() { if fc.ptr != nil { C.avformat_close_input(&fc.ptr) fc.ptr = nil } } // FormatContextFromC converts a C pointer to FormatContext func FormatContextFromC(ptr *C.AVFormatContext) *FormatContext { return &FormatContext{ptr: ptr} } // CPtr returns the underlying C pointer func (fc *FormatContext) CPtr() *C.AVFormatContext { return fc.ptr } // OpenInput opens an input file func (fc *FormatContext) OpenInput(url string) error { if fc.ptr == nil { fc.ptr = C.avformat_alloc_context() if fc.ptr == nil { return ErrInvalidInput } } cURL := C.CString(url) defer C.free(unsafe.Pointer(cURL)) ret := C.avformat_open_input(&fc.ptr, cURL, nil, nil) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to open input", Op: "OpenInput", } } return nil } // Close closes the input func (fc *FormatContext) Close() { if fc.ptr != nil { C.avformat_close_input(&fc.ptr) } } // FindStreamInfo finds stream info func (fc *FormatContext) FindStreamInfo() error { if fc.ptr == nil { return ErrInvalidInput } ret := C.avformat_find_stream_info(fc.ptr, nil) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to find stream info", Op: "FindStreamInfo", } } return nil } // NbStreams returns the number of streams func (fc *FormatContext) NbStreams() int { if fc.ptr == nil { return 0 } return int(fc.ptr.nb_streams) } // Streams returns the streams func (fc *FormatContext) Streams() []*Stream { if fc.ptr == nil { return nil } streams := make([]*Stream, fc.NbStreams()) ptrSize := unsafe.Sizeof(fc.ptr.streams) for i := 0; i < fc.NbStreams(); i++ { streamPtr := (**C.AVStream)(unsafe.Pointer(uintptr(unsafe.Pointer(fc.ptr.streams)) + uintptr(i)*ptrSize)) streams[i] = StreamFromC(*streamPtr) } return streams } // VideoStreams returns only video streams func (fc *FormatContext) VideoStreams() []*Stream { streams := fc.Streams() videoStreams := make([]*Stream, 0) for _, s := range streams { if s.Type() == CodecTypeVideo { videoStreams = append(videoStreams, s) } } return videoStreams } // AudioStreams returns only audio streams func (fc *FormatContext) AudioStreams() []*Stream { streams := fc.Streams() audioStreams := make([]*Stream, 0) for _, s := range streams { if s.Type() == CodecTypeAudio { audioStreams = append(audioStreams, s) } } return audioStreams } // ReadPacket reads a packet func (fc *FormatContext) ReadPacket(pkt *Packet) error { if fc.ptr == nil { return ErrInvalidInput } ret := C.av_read_frame(fc.ptr, pkt.CPtr()) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to read packet", Op: "ReadPacket", } } return nil } // WritePacket writes a packet func (fc *FormatContext) WritePacket(pkt *Packet) error { if fc.ptr == nil { return ErrInvalidOutput } ret := C.av_interleaved_write_frame(fc.ptr, pkt.CPtr()) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to write packet", Op: "WritePacket", } } return nil } // DumpFormat dumps format info func (fc *FormatContext) DumpFormat(idx int, url string, isOutput bool) { if fc.ptr == nil { return } cURL := C.CString(url) defer C.free(unsafe.Pointer(cURL)) C.av_dump_format(fc.ptr, C.int(idx), cURL, C.int(boolToInt(isOutput))) } func boolToInt(b bool) int { if b { return 1 } return 0 } // Stream represents a media stream type Stream struct { ptr *C.AVStream } // StreamFromC converts a C pointer to Stream func StreamFromC(ptr *C.AVStream) *Stream { return &Stream{ptr: ptr} } // CPtr returns the underlying C pointer func (s *Stream) CPtr() *C.AVStream { return s.ptr } // Index returns the stream index func (s *Stream) Index() int { if s.ptr == nil { return -1 } return int(s.ptr.index) } // Type returns the codec type func (s *Stream) Type() CodecType { if s.ptr == nil { return CodecType(0) } return CodecType(s.ptr.codecpar.codec_type) } // CodecParameters returns the codec parameters func (s *Stream) CodecParameters() *CodecParameters { if s.ptr == nil { return nil } return CodecParametersFromC(s.ptr.codecpar) } // SetCodecParameters sets the codec parameters func (s *Stream) SetCodecParameters(cp *CodecParameters) error { if s.ptr == nil || cp == nil || cp.ptr == nil { return ErrInvalidCodec } ret := C.avcodec_parameters_copy(s.ptr.codecpar, cp.ptr) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to copy codec parameters", Op: "SetCodecParameters", } } return nil } // SetCodecContextParameters copies parameters from a codec context to this stream func (s *Stream) SetCodecContextParameters(cc *Context) error { if s.ptr == nil || cc == nil || cc.ptr == nil { return ErrInvalidCodec } // Manually copy fields from codec context to codec parameters s.ptr.codecpar.codec_type = cc.ptr.codec_type s.ptr.codecpar.codec_id = cc.ptr.codec_id s.ptr.codecpar.bit_rate = cc.ptr.bit_rate s.ptr.codecpar.width = cc.ptr.width s.ptr.codecpar.height = cc.ptr.height // Copy format based on codec type if cc.ptr.codec_type == C.AVMEDIA_TYPE_VIDEO { s.ptr.codecpar.format = C.int(cc.ptr.pix_fmt) } else if cc.ptr.codec_type == C.AVMEDIA_TYPE_AUDIO { s.ptr.codecpar.format = C.int(cc.ptr.sample_fmt) } s.ptr.codecpar.sample_rate = cc.ptr.sample_rate s.ptr.codecpar.channels = cc.ptr.channels s.ptr.codecpar.channel_layout = cc.ptr.channel_layout return nil } // Codec returns the codec context (deprecated: use CodecParameters instead) func (s *Stream) Codec() *Context { // In FFmpeg 4.0+, codec field was removed from AVStream // Use CodecParameters() and allocate a new context if needed return nil } // TimeBase returns the time base func (s *Stream) TimeBase() Rational { if s.ptr == nil { return Rational{} } return Rational{ num: int(s.ptr.time_base.num), den: int(s.ptr.time_base.den), } } // SetTimeBase sets the time base func (s *Stream) SetTimeBase(r Rational) { if s.ptr != nil { s.ptr.time_base.num = C.int(r.num) s.ptr.time_base.den = C.int(r.den) } } // Rational represents a rational number type Rational struct { num int den int } // NewRational creates a new rational func NewRational(num, den int) Rational { return Rational{num: num, den: den} } // CodecParameters represents codec parameters type CodecParameters struct { ptr *C.AVCodecParameters } // CodecParametersFromC converts a C pointer to CodecParameters func CodecParametersFromC(ptr *C.AVCodecParameters) *CodecParameters { return &CodecParameters{ptr: ptr} } // CPtr returns the underlying C pointer func (cp *CodecParameters) CPtr() *C.AVCodecParameters { return cp.ptr } // CodecType returns the codec type func (cp *CodecParameters) CodecType() CodecType { if cp.ptr == nil { return CodecType(0) } return CodecType(cp.ptr.codec_type) } // CodecID returns the codec ID func (cp *CodecParameters) CodecID() int { if cp.ptr == nil { return 0 } return int(cp.ptr.codec_id) } // Width returns the width func (cp *CodecParameters) Width() int { if cp.ptr == nil { return 0 } return int(cp.ptr.width) } // Height returns the height func (cp *CodecParameters) Height() int { if cp.ptr == nil { return 0 } return int(cp.ptr.height) } // Format returns the format func (cp *CodecParameters) Format() int { if cp.ptr == nil { return -1 } return int(cp.ptr.format) } // SampleRate returns the sample rate func (cp *CodecParameters) SampleRate() int { if cp.ptr == nil { return 0 } return int(cp.ptr.sample_rate) } // Channels returns the number of channels func (cp *CodecParameters) Channels() int { if cp.ptr == nil { return 0 } return int(cp.ptr.channels) } // FrameSize returns the frame size func (cp *CodecParameters) FrameSize() int { if cp.ptr == nil { return 0 } return int(cp.ptr.frame_size) } // BitRate returns the bit rate func (cp *CodecParameters) BitRate() int64 { if cp.ptr == nil { return 0 } return int64(cp.ptr.bit_rate) } // OutputFormatContext represents an output format context type OutputFormatContext struct { FormatContext } // AllocOutputContext allocates an output format context func AllocOutputContext(url string, fmt *OutputFormat) (*OutputFormatContext, error) { ofc := &OutputFormatContext{ FormatContext: FormatContext{ ptr: C.avformat_alloc_context(), }, } if ofc.ptr == nil { return nil, ErrInvalidOutput } cURL := C.CString(url) defer C.free(unsafe.Pointer(cURL)) var ret C.int if fmt != nil && fmt.ptr != nil { ret = C.avformat_alloc_output_context2(&ofc.ptr, fmt.ptr, nil, cURL) } else { ret = C.avformat_alloc_output_context2(&ofc.ptr, nil, nil, cURL) } if ret < 0 { C.avformat_free_context(ofc.ptr) return nil, &FFmpegError{ Code: int(ret), Message: "failed to allocate output context", Op: "AllocOutputContext", } } return ofc, nil } // AddStream adds a new stream with optional codec func (ofc *OutputFormatContext) AddStream(codec *Codec) (*Stream, error) { if ofc.ptr == nil { return nil, ErrInvalidOutput } var stream *C.AVStream if codec != nil && codec.ptr != nil { stream = C.avformat_new_stream(ofc.ptr, codec.ptr) } else { stream = C.avformat_new_stream(ofc.ptr, nil) } if stream == nil { return nil, ErrInvalidCodec } return StreamFromC(stream), nil } // SetOformat sets the output format func (ofc *OutputFormatContext) SetOformat(fmt *OutputFormat) { if ofc.ptr != nil && fmt != nil && fmt.ptr != nil { ofc.ptr.oformat = fmt.ptr } } // OpenOutput opens the output file func (ofc *OutputFormatContext) OpenOutput(url string) error { if ofc.ptr == nil { return ErrInvalidOutput } cURL := C.CString(url) defer C.free(unsafe.Pointer(cURL)) ret := C.avio_open(&ofc.ptr.pb, cURL, C.AVIO_FLAG_WRITE) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to open output", Op: "OpenOutput", } } return nil } // CloseOutput closes the output file func (ofc *OutputFormatContext) CloseOutput() { if ofc.ptr != nil && ofc.ptr.pb != nil { C.avio_close(ofc.ptr.pb) } } // DumpFormat dumps format info func (ofc *OutputFormatContext) DumpFormat(idx int, url string, isOutput bool) { ofc.FormatContext.DumpFormat(idx, url, isOutput) } // WriteHeader writes the header func (ofc *OutputFormatContext) WriteHeader() error { if ofc.ptr == nil { return ErrInvalidOutput } ret := C.avformat_write_header(ofc.ptr, nil) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to write header", Op: "WriteHeader", } } return nil } // WriteTrailer writes the trailer func (ofc *OutputFormatContext) WriteTrailer() error { if ofc.ptr == nil { return ErrInvalidOutput } ret := C.av_write_trailer(ofc.ptr) if ret < 0 { return &FFmpegError{ Code: int(ret), Message: "failed to write trailer", Op: "WriteTrailer", } } return nil } // OutputFormat represents an output format type OutputFormat struct { ptr *C.AVOutputFormat } // OutputFormatFromC converts a C pointer to OutputFormat func OutputFormatFromC(ptr *C.AVOutputFormat) *OutputFormat { return &OutputFormat{ptr: ptr} } // CPtr returns the underlying C pointer func (of *OutputFormat) CPtr() *C.AVOutputFormat { return of.ptr } // GuessFormat guesses the output format func GuessFormat(shortName, filename string) *OutputFormat { cShortName := C.CString(shortName) cFilename := C.CString(filename) defer C.free(unsafe.Pointer(cShortName)) defer C.free(unsafe.Pointer(cFilename)) ptr := C.av_guess_format(cShortName, cFilename, nil) if ptr == nil { return nil } return OutputFormatFromC(ptr) }