Images to video example

This commit is contained in:
alex
2019-01-23 15:22:51 +03:00
parent cdc9bbeb66
commit 704f3471c3
4 changed files with 208 additions and 10 deletions

View File

@@ -569,13 +569,13 @@ func (cc *CodecCtx) Encode(frames []*Frame, drain int) ([]*Packet, error) {
ret = int(C.avcodec_send_frame(cc.avCodecCtx, frame.avFrame))
}
if ret < 0 {
return nil, fmt.Errorf("error during encoding - %s", AvError(ret))
return nil, AvError(ret)
}
for {
pkt := NewPacket()
ret = int(C.avcodec_receive_packet(cc.avCodecCtx, &pkt.avPacket))
if ret != 0 {
if ret < 0 {
pkt.Free()
break
}

198
examples/images-to-video.go Normal file
View File

@@ -0,0 +1,198 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"path/filepath"
"github.com/3d0c/gmf"
)
var pts int64 = 0
func initOst(name string, oc *gmf.FmtCtx, ist *gmf.Stream) (*gmf.Stream, error) {
var (
cc *gmf.CodecCtx
ost *gmf.Stream
options []gmf.Option
)
codec, err := gmf.FindEncoder(name)
if err != nil {
return nil, err
}
if ost = oc.NewStream(codec); ost == nil {
return nil, fmt.Errorf("unable to create new stream in output context")
}
if cc = gmf.NewCodecCtx(codec); cc == nil {
return nil, fmt.Errorf("unable to create codec context")
}
defer gmf.Release(cc)
if oc.IsGlobalHeader() {
cc.SetFlag(gmf.CODEC_FLAG_GLOBAL_HEADER)
}
if codec.IsExperimental() {
cc.SetStrictCompliance(gmf.FF_COMPLIANCE_EXPERIMENTAL)
}
options = append(
[]gmf.Option{
{Key: "time_base", Val: gmf.AVR{Num: 1, Den: 25}},
},
)
ost.SetTimeBase(gmf.AVR{Num: 1, Den: 25})
ost.SetRFrameRate(gmf.AVR{Num: 25, Den: 1})
options = append(
[]gmf.Option{
{Key: "pixel_format", Val: gmf.AV_PIX_FMT_YUV420P},
{Key: "video_size", Val: ist.CodecCtx().GetVideoSize()},
},
options...,
)
cc.SetProfile(gmf.FF_PROFILE_H264_MAIN)
cc.SetOptions(options)
if err := cc.Open(nil); err != nil {
return nil, err
}
ost.SetCodecCtx(cc)
return ost, nil
}
func main() {
var (
src string
dst string
ost *gmf.Stream
pkt *gmf.Packet
frame, dstFrame *gmf.Frame
swsCtx *gmf.SwsCtx
ret int
sources []string = make([]string, 0)
)
flag.StringVar(&src, "src", "./tmp", "source images folder")
flag.StringVar(&dst, "dst", "result.mp4", "destination file")
flag.Parse()
fis, err := ioutil.ReadDir(src)
if err != nil {
log.Fatalf("Error reding '%s' - %s\n", src, err)
}
for _, fi := range fis {
ext := filepath.Ext(fi.Name())
if ext != ".jpg" && ext != ".jpeg" && ext != ".png" {
log.Printf("skipping %s, ext: '%s'\n", fi.Name(), ext)
continue
}
sources = append(sources, filepath.Join(src, fi.Name()))
}
if len(sources) == 0 {
log.Fatalf("Not enough source files\n")
}
octx, err := gmf.NewOutputCtx(dst)
if err != nil {
log.Fatalf("Error creating output context - %s\n", err)
}
defer octx.Free()
for _, source := range sources {
log.Printf("Loading %s\n", source)
ictx, err := gmf.NewInputCtx(source)
if err != nil {
log.Fatalf("Error creating input context - %s\n", err)
}
ist, err := ictx.GetBestStream(gmf.AVMEDIA_TYPE_VIDEO)
if err != nil {
log.Fatalf("Error getting source stream - %s\n", err)
}
if ost == nil {
if ost, err = initOst("libx264", octx, ist); err != nil {
log.Fatalf("Error init output stream - %s\n", err)
}
if err = octx.WriteHeader(); err != nil {
log.Fatalf("%s\n", err)
}
}
if swsCtx == nil {
swsCtx = gmf.NewSwsCtx(ist.CodecCtx(), ost.CodecCtx(), gmf.SWS_FAST_BILINEAR)
}
if pkt, err = ictx.GetNextPacket(); err != nil {
log.Fatalf("Error getting packet - %s", err)
}
frame, ret = ist.CodecCtx().Decode2(pkt)
if ret < 0 {
log.Fatalf("Unexpected error - %s\n", gmf.AvError(ret))
}
if dstFrame, err = swsCtx.Scale2(frame); err != nil {
log.Fatalf("Error scaling - %s\n", err)
}
encode(octx, ost, dstFrame, -1)
pkt.Free()
ictx.Free()
frame.Free()
}
encode(octx, ost, nil, 1)
octx.WriteTrailer()
}
func encode(octx *gmf.FmtCtx, ost *gmf.Stream, frame *gmf.Frame, drain int) {
if frame != nil {
frame.SetPts(pts)
}
pts += 1
packets, err := ost.CodecCtx().Encode([]*gmf.Frame{frame}, drain)
if err != nil {
log.Fatalf("Error encoding - %s\n", err)
}
if len(packets) == 0 {
return
}
for _, packet := range packets {
packet.SetPts(gmf.RescaleQ(packet.Pts(), ost.CodecCtx().TimeBase(), ost.TimeBase()))
packet.SetDts(gmf.RescaleQ(packet.Dts(), ost.CodecCtx().TimeBase(), ost.TimeBase()))
if err = octx.WritePacket(packet); err != nil {
log.Fatalf("Error writing packet - %s\n", err)
}
packet.Free()
if drain > 0 {
pts += 1
}
}
return
}

View File

@@ -1,10 +1,10 @@
package main
import (
"fmt"
"log"
"os"
"runtime/debug"
"strconv"
. "github.com/3d0c/gmf"
)
@@ -22,10 +22,10 @@ func assert(i interface{}, err error) interface{} {
return i
}
var i int = 0
var i, j int = 0, 0
func writeFile(b []byte) {
name := "./tmp/" + strconv.Itoa(i) + ".jpg"
name := fmt.Sprintf("./tmp/%d%d.png", j, i)
fp, err := os.Create(name)
if err != nil {
@@ -37,6 +37,10 @@ func writeFile(b []byte) {
fatal(err)
}
i++
if i == 9 {
i = 0
j++
}
}()
if n, err := fp.Write(b); err != nil {
@@ -63,7 +67,7 @@ func main() {
log.Println("No video stream found in", srcFileName)
}
codec, err := FindEncoder(AV_CODEC_ID_JPEG2000)
codec, err := FindEncoder(AV_CODEC_ID_PNG)
if err != nil {
fatal(err)
}

View File

@@ -109,10 +109,6 @@ func GetSampleFmtName(fmt int32) string {
return C.GoString(C.av_get_sample_fmt_name(fmt))
}
// func RescaleRnd(a, b, c int64) int64 {
// return int64(C.av_rescale_rnd(C.int64_t(a), C.int64_t(b), C.int64_t(c), 3))
// }
func AvInvQ(q AVRational) AVRational {
avr := q.AVR()
return AVRational{C.int(avr.Den), C.int(avr.Num)}