mirror of
https://github.com/giorgisio/goav.git
synced 2026-10-19 16:14:41 +00:00
212 lines
6.7 KiB
Go
212 lines
6.7 KiB
Go
// Use of this source code is governed by a MIT license that can be found in the LICENSE file.
|
|
// Giorgis (habtom@giorgis.io)
|
|
|
|
package avutil
|
|
|
|
/*
|
|
#cgo pkg-config: libavutil
|
|
#include <libavutil/frame.h>
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"log"
|
|
"unsafe"
|
|
)
|
|
|
|
type (
|
|
AvBuffer C.struct_AVBuffer
|
|
AvBufferRef C.struct_AVBufferRef
|
|
AvBufferPool C.struct_AVBufferPool
|
|
Frame C.struct_AVFrame
|
|
AvFrameSideData C.struct_AVFrameSideData
|
|
AvFrameSideDataType C.enum_AVFrameSideDataType
|
|
)
|
|
|
|
func AvprivFrameGetMetadatap(f *Frame) *Dictionary {
|
|
return (*Dictionary)(unsafe.Pointer(f.metadata))
|
|
}
|
|
|
|
func AvFrameSetQpTable(f *Frame, b *AvBufferRef, s, q int) int {
|
|
return int(C.av_frame_set_qp_table((*C.struct_AVFrame)(unsafe.Pointer(f)), (*C.struct_AVBufferRef)(unsafe.Pointer(b)), C.int(s), C.int(q)))
|
|
}
|
|
|
|
func AvFrameGetQpTable(f *Frame, s, t *int) int8 {
|
|
return int8(*C.av_frame_get_qp_table((*C.struct_AVFrame)(unsafe.Pointer(f)), (*C.int)(unsafe.Pointer(s)), (*C.int)(unsafe.Pointer(t))))
|
|
}
|
|
|
|
//Allocate an Frame and set its fields to default values.
|
|
func AvFrameAlloc() *Frame {
|
|
return (*Frame)(unsafe.Pointer(C.av_frame_alloc()))
|
|
}
|
|
|
|
//Free the frame and any dynamically allocated objects in it, e.g.
|
|
func AvFrameFree(f *Frame) {
|
|
C.av_frame_free((**C.struct_AVFrame)(unsafe.Pointer(&f)))
|
|
}
|
|
|
|
//Allocate new buffer(s) for audio or video data.
|
|
func AvFrameGetBuffer(f *Frame, a int) int {
|
|
return int(C.av_frame_get_buffer((*C.struct_AVFrame)(unsafe.Pointer(f)), C.int(a)))
|
|
}
|
|
|
|
//Setup a new reference to the data described by an given frame.
|
|
func AvFrameRef(d, s *Frame) int {
|
|
return int(C.av_frame_ref((*C.struct_AVFrame)(unsafe.Pointer(d)), (*C.struct_AVFrame)(unsafe.Pointer(s))))
|
|
}
|
|
|
|
//Create a new frame that references the same data as src.
|
|
func AvFrameClone(f *Frame) *Frame {
|
|
return (*Frame)(C.av_frame_clone((*C.struct_AVFrame)(unsafe.Pointer(f))))
|
|
}
|
|
|
|
//Unreference all the buffers referenced by frame and reset the frame fields.
|
|
func AvFrameUnref(f *Frame) {
|
|
cf := (*C.struct_AVFrame)(unsafe.Pointer(f))
|
|
C.av_frame_unref(cf)
|
|
}
|
|
|
|
//Move everythnig contained in src to dst and reset src.
|
|
func AvFrameMoveRef(d, s *Frame) {
|
|
C.av_frame_move_ref((*C.struct_AVFrame)(unsafe.Pointer(d)), (*C.struct_AVFrame)(unsafe.Pointer(s)))
|
|
}
|
|
|
|
//Check if the frame data is writable.
|
|
func AvFrameIsWritable(f *Frame) int {
|
|
return int(C.av_frame_is_writable((*C.struct_AVFrame)(unsafe.Pointer(f))))
|
|
}
|
|
|
|
//Ensure that the frame data is writable, avoiding data copy if possible.
|
|
func AvFrameMakeWritable(f *Frame) int {
|
|
return int(C.av_frame_make_writable((*C.struct_AVFrame)(unsafe.Pointer(f))))
|
|
}
|
|
|
|
//Copy only "metadata" fields from src to dst.
|
|
func AvFrameCopyProps(d, s *Frame) int {
|
|
return int(C.av_frame_copy_props((*C.struct_AVFrame)(unsafe.Pointer(d)), (*C.struct_AVFrame)(unsafe.Pointer(s))))
|
|
}
|
|
|
|
//Get the buffer reference a given data plane is stored in.
|
|
func AvFrameGetPlaneBuffer(f *Frame, p int) *AvBufferRef {
|
|
return (*AvBufferRef)(C.av_frame_get_plane_buffer((*C.struct_AVFrame)(unsafe.Pointer(f)), C.int(p)))
|
|
}
|
|
|
|
//Add a new side data to a frame.
|
|
func AvFrameNewSideData(f *Frame, d AvFrameSideDataType, s int) *AvFrameSideData {
|
|
return (*AvFrameSideData)(C.av_frame_new_side_data((*C.struct_AVFrame)(unsafe.Pointer(f)), (C.enum_AVFrameSideDataType)(d), C.int(s)))
|
|
}
|
|
|
|
func AvFrameGetSideData(f *Frame, t AvFrameSideDataType) *AvFrameSideData {
|
|
return (*AvFrameSideData)(C.av_frame_get_side_data((*C.struct_AVFrame)(unsafe.Pointer(f)), (C.enum_AVFrameSideDataType)(t)))
|
|
}
|
|
|
|
func Data(f *Frame) (data [8]*uint8) {
|
|
for i := range data {
|
|
data[i] = (*uint8)(f.data[i])
|
|
}
|
|
return
|
|
}
|
|
|
|
func Linesize(f *Frame) (linesize [8]int32) {
|
|
for i := range linesize {
|
|
linesize[i] = int32(f.linesize[i])
|
|
}
|
|
return
|
|
}
|
|
|
|
//GetPicture creates a YCbCr image from the frame
|
|
func GetPicture(f *Frame) (img *image.YCbCr, err error) {
|
|
// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
|
|
// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
|
|
// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
|
|
// For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2.
|
|
// For 4:1:1, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/4.
|
|
// For 4:1:0, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/8.
|
|
|
|
w := int(f.linesize[0])
|
|
h := int(f.height)
|
|
r := image.Rectangle{image.Point{0, 0}, image.Point{w, h}}
|
|
// TODO: Use the sub sample ratio from the input image 'f.format'
|
|
img = image.NewYCbCr(r, image.YCbCrSubsampleRatio420)
|
|
// convert the frame data data to a Go byte array
|
|
img.Y = C.GoBytes(unsafe.Pointer(f.data[0]), C.int(w*h))
|
|
|
|
wCb := int(f.linesize[1])
|
|
if unsafe.Pointer(f.data[1]) != nil {
|
|
img.Cb = C.GoBytes(unsafe.Pointer(f.data[1]), C.int(wCb*h/2))
|
|
}
|
|
|
|
wCr := int(f.linesize[2])
|
|
if unsafe.Pointer(f.data[2]) != nil {
|
|
img.Cr = C.GoBytes(unsafe.Pointer(f.data[2]), C.int(wCr*h/2))
|
|
}
|
|
return
|
|
}
|
|
|
|
// SetPicture sets the image pointer of |f| to the image pointers of |img|
|
|
func SetPicture(f *Frame, img *image.YCbCr) {
|
|
d := Data(f)
|
|
// l := Linesize(f)
|
|
// FIXME: Save the original pointers somewhere, this is a memory leak
|
|
d[0] = (*uint8)(unsafe.Pointer(&img.Y[0]))
|
|
// d[1] = (*uint8)(unsafe.Pointer(&img.Cb[0]))
|
|
}
|
|
|
|
func GetPictureRGB(f *Frame) (img *image.RGBA, err error) {
|
|
w := int(f.linesize[0])
|
|
h := int(f.height)
|
|
r := image.Rectangle{image.Point{0, 0}, image.Point{w, h}}
|
|
// TODO: Use the sub sample ratio from the input image 'f.format'
|
|
img = image.NewRGBA(r)
|
|
// convert the frame data data to a Go byte array
|
|
img.Pix = C.GoBytes(unsafe.Pointer(f.data[0]), C.int(w*h))
|
|
img.Stride = w
|
|
log.Println("w", w, "h", h)
|
|
return
|
|
}
|
|
|
|
func AvSetFrame(f *Frame, w int, h int, pixFmt int) (err error) {
|
|
f.width = C.int(w)
|
|
f.height = C.int(h)
|
|
f.format = C.int(pixFmt)
|
|
if ret := C.av_frame_get_buffer((*C.struct_AVFrame)(unsafe.Pointer(f)), 32 /*alignment*/); ret < 0 {
|
|
err = fmt.Errorf("Error allocating avframe buffer. Err: %v", ret)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func AvFrameGetInfo(f *Frame) (width int, height int, linesize [8]int32, data [8]*uint8) {
|
|
width = int(f.linesize[0])
|
|
height = int(f.height)
|
|
for i := range linesize {
|
|
linesize[i] = int32(f.linesize[i])
|
|
}
|
|
for i := range data {
|
|
data[i] = (*uint8)(f.data[i])
|
|
}
|
|
// log.Println("Linesize is ", f.linesize, "Data is", data)
|
|
return
|
|
}
|
|
|
|
func GetBestEffortTimestamp(f *Frame) int64 {
|
|
return int64(f.best_effort_timestamp)
|
|
}
|
|
|
|
// //static int get_video_buffer (Frame *frame, int align)
|
|
// func GetVideoBuffer(f *Frame, a int) int {
|
|
// return int(C.get_video_buffer(f, C.int(a)))
|
|
// }
|
|
|
|
// //static int get_audio_buffer (Frame *frame, int align)
|
|
// func GetAudioBuffer(f *Frame, a int) int {
|
|
// return C.get_audio_buffer(f, C.int(a))
|
|
// }
|
|
|
|
// //static void get_frame_defaults (Frame *frame)
|
|
// func GetFrameDefaults(f *Frame) {
|
|
// C.get_frame_defaults(*C.struct_AVFrame(f))
|
|
// }
|