mirror of
https://github.com/3d0c/gmf
synced 2025-12-24 10:40:59 +08:00
Merge pull request #88 from 3d0c/feature/video-from-images
Images to video example
This commit is contained in:
@@ -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
198
examples/images-to-video.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
4
utils.go
4
utils.go
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user