mirror of
https://github.com/3d0c/gmf
synced 2025-12-24 10:40:59 +08:00
255 lines
6.0 KiB
Go
255 lines
6.0 KiB
Go
package gmf
|
|
|
|
/*
|
|
#cgo pkg-config: libavcodec libavutil
|
|
|
|
#include "libavutil/avutil.h"
|
|
#include "libavutil/error.h"
|
|
#include "libavutil/mathematics.h"
|
|
#include "libavutil/rational.h"
|
|
#include "libavutil/samplefmt.h"
|
|
#include "libavcodec/avcodec.h"
|
|
#include "libavutil/imgutils.h"
|
|
|
|
uint32_t return_int (int num) {
|
|
return (uint32_t)(num);
|
|
}
|
|
|
|
uint8_t * gmf_alloc_buffer(int32_t fmt, int width, int height) {
|
|
int numBytes = av_image_get_buffer_size(fmt, width, height, 0);
|
|
return (uint8_t *) av_malloc(numBytes*sizeof(uint8_t));
|
|
}
|
|
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
type AVRational C.struct_AVRational
|
|
|
|
type AVR struct {
|
|
Num int
|
|
Den int
|
|
}
|
|
|
|
const (
|
|
AVERROR_EOF = -541478725
|
|
// AV_ROUND_PASS_MINMAX = 8192
|
|
)
|
|
|
|
var (
|
|
AV_TIME_BASE int = C.AV_TIME_BASE
|
|
AV_TIME_BASE_Q AVRational = AVRational{1, C.int(AV_TIME_BASE)}
|
|
)
|
|
|
|
func (a AVR) AVRational() AVRational {
|
|
return AVRational{C.int(a.Num), C.int(a.Den)}
|
|
}
|
|
|
|
func (a AVR) String() string {
|
|
return fmt.Sprintf("%d/%d", a.Num, a.Den)
|
|
}
|
|
|
|
func (a AVR) Av2qd() float64 {
|
|
return float64(a.Num) / float64(a.Den)
|
|
}
|
|
|
|
func (a AVR) Invert() AVR {
|
|
return AVR{Num: a.Den, Den: a.Num}
|
|
}
|
|
|
|
func (a AVRational) AVR() AVR {
|
|
return AVR{Num: int(a.num), Den: int(a.den)}
|
|
}
|
|
|
|
func AvError(averr int) error {
|
|
errlen := 1024
|
|
b := make([]byte, errlen)
|
|
|
|
C.av_strerror(C.int(averr), (*C.char)(unsafe.Pointer(&b[0])), C.size_t(errlen))
|
|
|
|
return errors.New(string(b[:bytes.Index(b, []byte{0})]))
|
|
}
|
|
|
|
func AvErrno(ret int) syscall.Errno {
|
|
if ret < 0 {
|
|
ret = -ret
|
|
}
|
|
|
|
return syscall.Errno(ret)
|
|
}
|
|
|
|
func RescaleQ(a int64, encBase AVRational, stBase AVRational) int64 {
|
|
return int64(C.av_rescale_q(C.int64_t(a), C.struct_AVRational(encBase), C.struct_AVRational(stBase)))
|
|
}
|
|
|
|
func RescaleQRnd(a int64, encBase AVRational, stBase AVRational) int64 {
|
|
return int64(C.av_rescale_q_rnd(C.int64_t(a), C.struct_AVRational(encBase), C.struct_AVRational(stBase), C.AV_ROUND_NEAR_INF|C.AV_ROUND_PASS_MINMAX))
|
|
}
|
|
|
|
func CompareTimeStamp(aTimestamp int, aTimebase AVRational, bTimestamp int, bTimebase AVRational) int {
|
|
return int(C.av_compare_ts(C.int64_t(aTimestamp), C.struct_AVRational(aTimebase),
|
|
C.int64_t(bTimestamp), C.struct_AVRational(bTimebase)))
|
|
}
|
|
func RescaleDelta(inTb AVRational, inTs int64, fsTb AVRational, duration int, last *int64, outTb AVRational) int64 {
|
|
return int64(C.av_rescale_delta(C.struct_AVRational(inTb), C.int64_t(inTs), C.struct_AVRational(fsTb), C.int(duration), (*C.int64_t)(unsafe.Pointer(&last)), C.struct_AVRational(outTb)))
|
|
}
|
|
|
|
func Rescale(a, b, c int64) int64 {
|
|
return int64(C.av_rescale(C.int64_t(a), C.int64_t(b), C.int64_t(c)))
|
|
}
|
|
|
|
func RescaleTs(pkt *Packet, encBase AVRational, stBase AVRational) {
|
|
C.av_packet_rescale_ts(&pkt.avPacket, C.struct_AVRational(encBase), C.struct_AVRational(stBase))
|
|
}
|
|
|
|
func GetSampleFmtName(fmt int32) string {
|
|
return C.GoString(C.av_get_sample_fmt_name(fmt))
|
|
}
|
|
|
|
func AvInvQ(q AVRational) AVRational {
|
|
avr := q.AVR()
|
|
return AVRational{C.int(avr.Den), C.int(avr.Num)}
|
|
}
|
|
|
|
// Synthetic video generator. It produces 25 iteratable frames.
|
|
// Used for tests.
|
|
func GenSyntVideoNewFrame(w, h int, fmt int32) chan *Frame {
|
|
yield := make(chan *Frame)
|
|
|
|
go func() {
|
|
defer close(yield)
|
|
for i := 0; i < 25; i++ {
|
|
frame := NewFrame().SetWidth(w).SetHeight(h).SetFormat(fmt)
|
|
|
|
if err := frame.ImgAlloc(); err != nil {
|
|
return
|
|
}
|
|
|
|
for y := 0; y < h; y++ {
|
|
for x := 0; x < w; x++ {
|
|
frame.SetData(0, y*frame.LineSize(0)+x, x+y+i*3)
|
|
}
|
|
}
|
|
|
|
// Cb and Cr
|
|
for y := 0; y < h/2; y++ {
|
|
for x := 0; x < w/2; x++ {
|
|
frame.SetData(1, y*frame.LineSize(1)+x, 128+y+i*2)
|
|
frame.SetData(2, y*frame.LineSize(2)+x, 64+x+i*5)
|
|
}
|
|
}
|
|
|
|
yield <- frame
|
|
}
|
|
}()
|
|
return yield
|
|
}
|
|
|
|
// tmp
|
|
func GenSyntVideoN(N, w, h int, fmt int32) chan *Frame {
|
|
yield := make(chan *Frame)
|
|
|
|
go func() {
|
|
defer close(yield)
|
|
for i := 0; i < N; i++ {
|
|
frame := NewFrame().SetWidth(w).SetHeight(h).SetFormat(fmt)
|
|
|
|
if err := frame.ImgAlloc(); err != nil {
|
|
return
|
|
}
|
|
|
|
for y := 0; y < h; y++ {
|
|
for x := 0; x < w; x++ {
|
|
frame.SetData(0, y*frame.LineSize(0)+x, x+y+i*3)
|
|
}
|
|
}
|
|
|
|
// Cb and Cr
|
|
for y := 0; y < h/2; y++ {
|
|
for x := 0; x < w/2; x++ {
|
|
frame.SetData(1, y*frame.LineSize(1)+x, 128+y+i*2)
|
|
frame.SetData(2, y*frame.LineSize(2)+x, 64+x+i*5)
|
|
}
|
|
}
|
|
|
|
yield <- frame
|
|
}
|
|
}()
|
|
return yield
|
|
}
|
|
|
|
func TimeCodeToComparable(timeCodeToTest, timeCodeStart, timeCodeEnd string) (hhToTest int, hhStart int, hhEnd int, err error) {
|
|
hhStart, err = TimeCodeToHundredths(timeCodeStart)
|
|
if err != nil {
|
|
return
|
|
}
|
|
hhEnd, err = TimeCodeToHundredths(timeCodeEnd)
|
|
if err != nil {
|
|
return
|
|
}
|
|
hhToTest, err = TimeCodeToHundredths(timeCodeToTest)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if hhEnd < hhStart {
|
|
hhStart -= hhEnd
|
|
hhToTest -= hhEnd
|
|
if hhToTest <= 0 {
|
|
hhToTest += 24 * 60 * 60 * 100
|
|
}
|
|
hhEnd = 24 * 60 * 60 * 100
|
|
}
|
|
return
|
|
}
|
|
|
|
func IsTimeCodeBetween(timeCodeToTest, timeCodeStart, timeCodeEnd string) (bool, error) {
|
|
hhToTest, hhStart, hhEnd, err := TimeCodeToComparable(timeCodeToTest, timeCodeStart, timeCodeEnd)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return hhToTest >= hhStart && hhToTest <= hhEnd, nil
|
|
}
|
|
|
|
// TimeCodeToHHMMSS splits a timeCode = "20:15:31:00" to return 20, 15, 31
|
|
func TimeCodeToHHMMSS(timeCode string) (int, int, int, int, error) {
|
|
parts := strings.Split(timeCode, ":")
|
|
if len(parts) != 4 {
|
|
return -1, -1, -1, -1, errors.New("invalid timecode")
|
|
}
|
|
hh, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return -1, -1, -1, -1, errors.New("invalid timecode")
|
|
}
|
|
mm, err := strconv.Atoi(parts[1])
|
|
if err != nil {
|
|
return -1, -1, -1, -1, errors.New("invalid timecode")
|
|
}
|
|
ss, err := strconv.Atoi(parts[2])
|
|
if err != nil {
|
|
return -1, -1, -1, -1, errors.New("invalid timecode")
|
|
}
|
|
hs, err := strconv.Atoi(parts[3])
|
|
if err != nil {
|
|
return -1, -1, -1, -1, errors.New("invalid timecode")
|
|
}
|
|
return hh, mm, ss, hs, nil
|
|
}
|
|
|
|
func TimeCodeToHundredths(timeCode string) (int, error) {
|
|
hh, mm, ss, hs, err := TimeCodeToHHMMSS(timeCode)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return (hh * 60 * 60 * 100) + (mm * 60 * 100) + (ss * 100) + hs, nil
|
|
}
|