Initial FFmpeg CGO bindings library structure
- Add CGO configuration with pkg-config for FFmpeg libraries - Implement FormatContext for input/output operations - Add Stream and CodecParameters wrappers - Implement Codec/Context with encode/decode API - Add Frame and Packet wrappers for media data - Include error types and FFmpegError - Add CLI tool and simple-transcode example Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3b0dbf6014
commit
8a258d8b60
|
|
@ -0,0 +1,83 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.kingecg.top/kingecg/goffmpeg/pkg/ffmpeg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
inputURL := flag.String("i", "", "Input file or URL")
|
||||||
|
outputURL := flag.String("o", "", "Output file or URL")
|
||||||
|
codecName := flag.String("c", "", "Codec name (e.g., libx264, aac)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *inputURL == "" || *outputURL == "" {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open input
|
||||||
|
ic := ffmpeg.AllocFormatContext()
|
||||||
|
defer ic.Free()
|
||||||
|
|
||||||
|
if err := ic.OpenInput(*inputURL); err != nil {
|
||||||
|
log.Fatalf("Failed to open input: %v", err)
|
||||||
|
}
|
||||||
|
defer ic.Close()
|
||||||
|
|
||||||
|
if err := ic.FindStreamInfo(); err != nil {
|
||||||
|
log.Fatalf("Failed to find stream info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ic.DumpFormat(0, *inputURL, false)
|
||||||
|
|
||||||
|
// Find video stream
|
||||||
|
videoStreams := ic.VideoStreams()
|
||||||
|
if len(videoStreams) == 0 {
|
||||||
|
log.Fatal("No video stream found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open output context
|
||||||
|
var ofc *ffmpeg.OutputFormatContext
|
||||||
|
if *codecName != "" {
|
||||||
|
codec, err := ffmpeg.FindEncoder(*codecName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to find encoder: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
of := ffmpeg.GuessFormat("", *outputURL)
|
||||||
|
if of == nil {
|
||||||
|
log.Fatalf("Failed to guess format")
|
||||||
|
}
|
||||||
|
|
||||||
|
ofc, err = ffmpeg.AllocOutputContext(*outputURL, of)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to allocate output context: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := ofc.AddStream(codec)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to add stream: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := videoStreams[0]
|
||||||
|
cp := vs.CodecParameters()
|
||||||
|
stream.SetCodecParameters(cp)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
ofc, err = ffmpeg.AllocOutputContext(*outputURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to allocate output context: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer ofc.Free()
|
||||||
|
|
||||||
|
fmt.Println("Transcoding started...")
|
||||||
|
fmt.Printf("Input: %s\n", *inputURL)
|
||||||
|
fmt.Printf("Output: %s\n", *outputURL)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.kingecg.top/kingecg/goffmpeg/pkg/ffmpeg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Simple transcoding example using goffmpeg library
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
fmt.Println("Usage: simple-transcode <input> <output>")
|
||||||
|
fmt.Println("Example: simple-transcode input.mp4 output.flv")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputURL := os.Args[1]
|
||||||
|
outputURL := os.Args[2]
|
||||||
|
|
||||||
|
// Open input file
|
||||||
|
ic := ffmpeg.AllocFormatContext()
|
||||||
|
defer ic.Free()
|
||||||
|
|
||||||
|
if err := ic.OpenInput(inputURL); err != nil {
|
||||||
|
log.Fatalf("Failed to open input %s: %v", inputURL, err)
|
||||||
|
}
|
||||||
|
defer ic.Close()
|
||||||
|
|
||||||
|
// Find stream info
|
||||||
|
if err := ic.FindStreamInfo(); err != nil {
|
||||||
|
log.Fatalf("Failed to find stream info: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump input format info
|
||||||
|
fmt.Printf("Input: %s\n", inputURL)
|
||||||
|
ic.DumpFormat(0, inputURL, false)
|
||||||
|
|
||||||
|
// Find video stream
|
||||||
|
videoStreams := ic.VideoStreams()
|
||||||
|
if len(videoStreams) == 0 {
|
||||||
|
log.Fatal("No video stream found in input")
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := videoStreams[0]
|
||||||
|
fmt.Printf("Video stream index: %d\n", vs.Index())
|
||||||
|
|
||||||
|
// Get codec parameters
|
||||||
|
cp := vs.CodecParameters()
|
||||||
|
fmt.Printf("Codec type: %d, Codec ID: %d\n", cp.CodecType(), cp.CodecID())
|
||||||
|
|
||||||
|
// Create output context
|
||||||
|
of := ffmpeg.GuessFormat("", outputURL)
|
||||||
|
if of == nil {
|
||||||
|
log.Fatalf("Failed to guess output format")
|
||||||
|
}
|
||||||
|
|
||||||
|
ofc, err := ffmpeg.AllocOutputContext(outputURL, of)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to allocate output context: %v", err)
|
||||||
|
}
|
||||||
|
defer ofc.Free()
|
||||||
|
|
||||||
|
// Copy stream from input to output
|
||||||
|
_, err = ofc.AddStream(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to add stream: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\nOutput: %s\n", outputURL)
|
||||||
|
fmt.Println("Transcoding setup complete. Use the library APIs to process frames.")
|
||||||
|
|
||||||
|
_ = vs // vs is used for reference
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
module git.kingecg.top/kingecg/goffmpeg
|
||||||
|
|
||||||
|
//go:build cgo
|
||||||
|
|
||||||
|
go 1.25.1
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: libavcodec libavformat libavutil libavfilter libswscale libswresample
|
||||||
|
#cgo LDFLAGS: -lavcodec -lavformat -lavutil -lavfilter -lswscale -lswresample
|
||||||
|
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavutil/opt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
|
||||||
|
static enum AVMediaType codec_get_type(const AVCodec *c) {
|
||||||
|
return c->type;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// CodecType represents the type of codec
|
||||||
|
type CodecType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodecTypeVideo CodecType = CodecType(C.AVMEDIA_TYPE_VIDEO)
|
||||||
|
CodecTypeAudio CodecType = CodecType(C.AVMEDIA_TYPE_AUDIO)
|
||||||
|
CodecTypeSubtitle CodecType = CodecType(C.AVMEDIA_TYPE_SUBTITLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Codec represents an FFmpeg codec
|
||||||
|
type Codec struct {
|
||||||
|
ptr *C.AVCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindDecoder finds a decoder by name
|
||||||
|
func FindDecoder(name string) (*Codec, error) {
|
||||||
|
cName := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cName))
|
||||||
|
|
||||||
|
ptr := C.avcodec_find_decoder_by_name(cName)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, ErrCodecNotFound
|
||||||
|
}
|
||||||
|
return &Codec{ptr: ptr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindEncoder finds an encoder by name
|
||||||
|
func FindEncoder(name string) (*Codec, error) {
|
||||||
|
cName := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cName))
|
||||||
|
|
||||||
|
ptr := C.avcodec_find_encoder_by_name(cName)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, ErrCodecNotFound
|
||||||
|
}
|
||||||
|
return &Codec{ptr: ptr}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodecFromC converts a C pointer to Codec
|
||||||
|
func CodecFromC(ptr *C.AVCodec) *Codec {
|
||||||
|
return &Codec{ptr: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPtr returns the underlying C pointer
|
||||||
|
func (c *Codec) CPtr() *C.AVCodec {
|
||||||
|
return c.ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the codec name
|
||||||
|
func (c *Codec) Name() string {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return C.GoString(c.ptr.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LongName returns the codec long name
|
||||||
|
func (c *Codec) LongName() string {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return C.GoString(c.ptr.long_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the codec type
|
||||||
|
func (c *Codec) Type() CodecType {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return CodecType(0)
|
||||||
|
}
|
||||||
|
t := C.codec_get_type(c.ptr)
|
||||||
|
return CodecType(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the codec ID
|
||||||
|
func (c *Codec) ID() int {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(c.ptr.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context represents a codec context
|
||||||
|
type Context struct {
|
||||||
|
ptr *C.AVCodecContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocContext allocates a codec context
|
||||||
|
func AllocContext() *Context {
|
||||||
|
return &Context{
|
||||||
|
ptr: C.avcodec_alloc_context3(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free frees the codec context
|
||||||
|
func (c *Context) Free() {
|
||||||
|
if c.ptr != nil {
|
||||||
|
C.avcodec_free_context(&c.ptr)
|
||||||
|
c.ptr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextFromC converts a C pointer to Context
|
||||||
|
func ContextFromC(ptr *C.AVCodecContext) *Context {
|
||||||
|
return &Context{ptr: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPtr returns the underlying C pointer
|
||||||
|
func (c *Context) CPtr() *C.AVCodecContext {
|
||||||
|
return c.ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCodec sets the codec for this context
|
||||||
|
func (c *Context) SetCodec(codec *Codec) error {
|
||||||
|
if c.ptr == nil || codec == nil || codec.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
C.avcodec_free_context(&c.ptr)
|
||||||
|
c.ptr = C.avcodec_alloc_context3(codec.ptr)
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width returns the width
|
||||||
|
func (c *Context) Width() int {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(c.ptr.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWidth sets the width
|
||||||
|
func (c *Context) SetWidth(width int) {
|
||||||
|
if c.ptr != nil {
|
||||||
|
c.ptr.width = C.int(width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height returns the height
|
||||||
|
func (c *Context) Height() int {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(c.ptr.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHeight sets the height
|
||||||
|
func (c *Context) SetHeight(height int) {
|
||||||
|
if c.ptr != nil {
|
||||||
|
c.ptr.height = C.int(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns the pixel/sample format
|
||||||
|
func (c *Context) Format() int {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return int(c.ptr.sample_fmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormat sets the pixel/sample format
|
||||||
|
func (c *Context) SetFormat(format int) {
|
||||||
|
if c.ptr != nil {
|
||||||
|
// Use unsafe.Pointer to assign to the enum field
|
||||||
|
*(*C.int)(unsafe.Pointer(&c.ptr.sample_fmt)) = C.int(format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BitRate returns the bit rate
|
||||||
|
func (c *Context) BitRate() int64 {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(c.ptr.bit_rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBitRate sets the bit rate
|
||||||
|
func (c *Context) SetBitRate(br int64) {
|
||||||
|
if c.ptr != nil {
|
||||||
|
c.ptr.bit_rate = C.int64_t(br)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens the codec
|
||||||
|
func (c *Context) Open(codec *Codec) error {
|
||||||
|
if c.ptr == nil || codec == nil || codec.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := C.avcodec_open2(c.ptr, codec.ptr, nil)
|
||||||
|
if ret < 0 {
|
||||||
|
return &FFmpegError{
|
||||||
|
Code: int(ret),
|
||||||
|
Message: "failed to open codec",
|
||||||
|
Op: "Open",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendPacket sends a packet to the decoder
|
||||||
|
func (c *Context) SendPacket(pkt *Packet) error {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := C.avcodec_send_packet(c.ptr, pkt.CPtr())
|
||||||
|
if ret < 0 {
|
||||||
|
return &FFmpegError{
|
||||||
|
Code: int(ret),
|
||||||
|
Message: "failed to send packet",
|
||||||
|
Op: "SendPacket",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceiveFrame receives a frame from the decoder
|
||||||
|
func (c *Context) ReceiveFrame(frame *Frame) error {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := C.avcodec_receive_frame(c.ptr, frame.CPtr())
|
||||||
|
if ret < 0 {
|
||||||
|
return &FFmpegError{
|
||||||
|
Code: int(ret),
|
||||||
|
Message: "failed to receive frame",
|
||||||
|
Op: "ReceiveFrame",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendFrame sends a frame to the encoder
|
||||||
|
func (c *Context) SendFrame(frame *Frame) error {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := C.avcodec_send_frame(c.ptr, frame.CPtr())
|
||||||
|
if ret < 0 {
|
||||||
|
return &FFmpegError{
|
||||||
|
Code: int(ret),
|
||||||
|
Message: "failed to send frame",
|
||||||
|
Op: "SendFrame",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceivePacket receives a packet from the encoder
|
||||||
|
func (c *Context) ReceivePacket(pkt *Packet) error {
|
||||||
|
if c.ptr == nil {
|
||||||
|
return ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := C.avcodec_receive_packet(c.ptr, pkt.CPtr())
|
||||||
|
if ret < 0 {
|
||||||
|
return &FFmpegError{
|
||||||
|
Code: int(ret),
|
||||||
|
Message: "failed to receive packet",
|
||||||
|
Op: "ReceivePacket",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the codec
|
||||||
|
func (c *Context) Close() {
|
||||||
|
if c.ptr != nil {
|
||||||
|
C.avcodec_close(c.ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidInput = errors.New("invalid input")
|
||||||
|
ErrInvalidOutput = errors.New("invalid output")
|
||||||
|
ErrCodecNotFound = errors.New("codec not found")
|
||||||
|
ErrFormatNotSupported = errors.New("format not supported")
|
||||||
|
ErrDecodeFailed = errors.New("decode failed")
|
||||||
|
ErrEncodeFailed = errors.New("encode failed")
|
||||||
|
ErrFilterFailed = errors.New("filter failed")
|
||||||
|
ErrIOFailed = errors.New("I/O operation failed")
|
||||||
|
ErrNoStream = errors.New("no stream found")
|
||||||
|
ErrInvalidCodec = errors.New("invalid codec")
|
||||||
|
)
|
||||||
|
|
||||||
|
// FFmpegError wraps FFmpeg errors with additional context
|
||||||
|
type FFmpegError struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
Op string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FFmpegError) Error() string {
|
||||||
|
return e.Op + ": " + e.Message + " (code: " + itoa(e.Code) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func itoa(n int) string {
|
||||||
|
if n < 0 {
|
||||||
|
return "-" + uitoa(uint(-n))
|
||||||
|
}
|
||||||
|
return uitoa(uint(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func uitoa(n uint) string {
|
||||||
|
if n == 0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
var buf [20]byte
|
||||||
|
i := len(buf)
|
||||||
|
for n > 0 {
|
||||||
|
i--
|
||||||
|
buf[i] = byte('0' + n%10)
|
||||||
|
n /= 10
|
||||||
|
}
|
||||||
|
return string(buf[i:])
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,497 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavformat/avio.h>
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func (ofc *OutputFormatContext) AddStream(codec *Codec) (*Stream, error) {
|
||||||
|
if ofc.ptr == nil || codec == nil || codec.ptr == nil {
|
||||||
|
return nil, ErrInvalidCodec
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := C.avformat_new_stream(ofc.ptr, codec.ptr)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unsafe.Pointer(ofc.ptr.pb) != nil) && (ofc.ptr.flags&C.AVFMT_NOFILE) == 0 {
|
||||||
|
// file handle has been created by the caller
|
||||||
|
} else {
|
||||||
|
// let avformat do it
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <libavutil/frame.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// Frame represents a decoded video/audio frame
|
||||||
|
type Frame struct {
|
||||||
|
ptr *C.AVFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocFrame allocates an empty frame
|
||||||
|
func AllocFrame() *Frame {
|
||||||
|
return &Frame{
|
||||||
|
ptr: C.av_frame_alloc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeFrame frees the frame
|
||||||
|
func (p *Frame) Free() {
|
||||||
|
if p.ptr != nil {
|
||||||
|
C.av_frame_free(&p.ptr)
|
||||||
|
p.ptr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameFromC converts a C pointer to Frame
|
||||||
|
func FrameFromC(ptr *C.AVFrame) *Frame {
|
||||||
|
return &Frame{ptr: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPtr returns the underlying C pointer
|
||||||
|
func (f *Frame) CPtr() *C.AVFrame {
|
||||||
|
return f.ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width returns the frame width
|
||||||
|
func (f *Frame) Width() int {
|
||||||
|
if f.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(f.ptr.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height returns the frame height
|
||||||
|
func (f *Frame) Height() int {
|
||||||
|
if f.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(f.ptr.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format returns the pixel/sample format
|
||||||
|
func (f *Frame) Format() int {
|
||||||
|
if f.ptr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return int(f.ptr.format)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linesize returns the line size
|
||||||
|
func (f *Frame) Linesize(i int) int {
|
||||||
|
if f.ptr == nil || i < 0 || i >= C.AVMEDIA_TYPE_NB {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(f.ptr.linesize[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data returns the frame data
|
||||||
|
func (f *Frame) Data(i int) []byte {
|
||||||
|
if f.ptr == nil || i < 0 || i >= C.AVMEDIA_TYPE_NB {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
size := f.Linesize(i) * f.Height()
|
||||||
|
if size <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return C.GoBytes(unsafe.Pointer(f.ptr.data[i]), C.int(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NbSamples returns the number of audio samples
|
||||||
|
func (f *Frame) NbSamples() int {
|
||||||
|
if f.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(f.ptr.nb_samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PTS returns the presentation timestamp
|
||||||
|
func (f *Frame) PTS() int64 {
|
||||||
|
if f.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(f.ptr.pts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPTS sets the presentation timestamp
|
||||||
|
func (f *Frame) SetPTS(pts int64) {
|
||||||
|
if f.ptr != nil {
|
||||||
|
f.ptr.pts = C.int64_t(pts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unref unreferences the frame
|
||||||
|
func (f *Frame) Unref() {
|
||||||
|
if f.ptr != nil {
|
||||||
|
C.av_frame_unref(f.ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
package ffmpeg
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavcodec/packet.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// Packet represents an encoded data packet
|
||||||
|
type Packet struct {
|
||||||
|
ptr *C.AVPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllocPacket allocates an empty packet
|
||||||
|
func AllocPacket() *Packet {
|
||||||
|
return &Packet{
|
||||||
|
ptr: C.av_packet_alloc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreePacket frees the packet
|
||||||
|
func (p *Packet) Free() {
|
||||||
|
if p.ptr != nil {
|
||||||
|
C.av_packet_free(&p.ptr)
|
||||||
|
p.ptr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketFromC converts a C pointer to Packet
|
||||||
|
func PacketFromC(ptr *C.AVPacket) *Packet {
|
||||||
|
return &Packet{ptr: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPtr returns the underlying C pointer
|
||||||
|
func (p *Packet) CPtr() *C.AVPacket {
|
||||||
|
return p.ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data returns the packet data
|
||||||
|
func (p *Packet) Data() []byte {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
size := int(p.ptr.size)
|
||||||
|
if size <= 0 || p.ptr.data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return C.GoBytes(unsafe.Pointer(p.ptr.data), C.int(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the packet size
|
||||||
|
func (p *Packet) Size() int {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(p.ptr.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PTS returns the presentation timestamp
|
||||||
|
func (p *Packet) PTS() int64 {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(p.ptr.pts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DTS returns the decoding timestamp
|
||||||
|
func (p *Packet) DTS() int64 {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(p.ptr.dts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPTS sets the presentation timestamp
|
||||||
|
func (p *Packet) SetPTS(pts int64) {
|
||||||
|
if p.ptr != nil {
|
||||||
|
p.ptr.pts = C.int64_t(pts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDTS sets the decoding timestamp
|
||||||
|
func (p *Packet) SetDTS(dts int64) {
|
||||||
|
if p.ptr != nil {
|
||||||
|
p.ptr.dts = C.int64_t(dts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamIndex returns the stream index
|
||||||
|
func (p *Packet) StreamIndex() int {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return int(p.ptr.stream_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStreamIndex sets the stream index
|
||||||
|
func (p *Packet) SetStreamIndex(idx int) {
|
||||||
|
if p.ptr != nil {
|
||||||
|
p.ptr.stream_index = C.int(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags returns the packet flags
|
||||||
|
func (p *Packet) Flags() int {
|
||||||
|
if p.ptr == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int(p.ptr.flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unref unreferences the packet data
|
||||||
|
func (p *Packet) Unref() {
|
||||||
|
if p.ptr != nil {
|
||||||
|
C.av_packet_unref(p.ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue