fixes, frames interator, transcoder

This commit is contained in:
alex
2014-04-11 19:52:37 +04:00
parent ac9f3b90b5
commit 5a65705a13
9 changed files with 130 additions and 121 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2013 alex.s.contact@yandex.ru
Copyright (c) 2013 Alex Parker(3d0c)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -17,8 +17,9 @@ import (
)
var (
CODEC_TYPE_AUDIO int = C.AVMEDIA_TYPE_AUDIO
CODEC_TYPE_VIDEO int = C.AVMEDIA_TYPE_VIDEO
AVMEDIA_TYPE_AUDIO int32 = C.AVMEDIA_TYPE_AUDIO
AVMEDIA_TYPE_VIDEO int32 = C.AVMEDIA_TYPE_VIDEO
AV_PIX_FMT_YUV420P int32 = C.AV_PIX_FMT_YUV420P
FF_PROFILE_MPEG4_SIMPLE int = C.FF_PROFILE_MPEG4_SIMPLE
AV_NOPTS_VALUE int = C.AV_NOPTS_VALUE

View File

@@ -22,14 +22,15 @@ import (
)
var (
AV_CODEC_ID_MPEG1VIDEO int = C.AV_CODEC_ID_MPEG1VIDEO
AV_CODEC_ID_MPEG2VIDEO int = C.AV_CODEC_ID_MPEG2VIDEO
AV_CODEC_ID_H264 int = C.AV_CODEC_ID_H264
AV_CODEC_ID_MPEG4 int = C.AV_CODEC_ID_MPEG4
CODEC_FLAG_GLOBAL_HEADER int = C.CODEC_FLAG_GLOBAL_HEADER
FF_MB_DECISION_SIMPLE int = C.FF_MB_DECISION_SIMPLE
FF_MB_DECISION_BITS int = C.FF_MB_DECISION_BITS
FF_MB_DECISION_RD int = C.FF_MB_DECISION_RD
AV_CODEC_ID_MPEG1VIDEO int = C.AV_CODEC_ID_MPEG1VIDEO
AV_CODEC_ID_MPEG2VIDEO int = C.AV_CODEC_ID_MPEG2VIDEO
AV_CODEC_ID_H264 int = C.AV_CODEC_ID_H264
AV_CODEC_ID_MPEG4 int = C.AV_CODEC_ID_MPEG4
CODEC_FLAG_GLOBAL_HEADER int = C.CODEC_FLAG_GLOBAL_HEADER
FF_MB_DECISION_SIMPLE int = C.FF_MB_DECISION_SIMPLE
FF_MB_DECISION_BITS int = C.FF_MB_DECISION_BITS
FF_MB_DECISION_RD int = C.FF_MB_DECISION_RD
AV_SAMPLE_FMT_S16 int32 = C.AV_SAMPLE_FMT_S16
)
type CodecCtx struct {
@@ -107,6 +108,8 @@ func (this *CodecCtx) CopyRequired(ist *Stream) *CodecCtx {
codec.sample_rate = icodec.sample_rate
codec.channels = icodec.channels
codec.channel_layout = icodec.channel_layout
return this
}
@@ -128,9 +131,8 @@ func (this *CodecCtx) Id() int {
return int(this.avCodecCtx.codec_id)
}
// @todo change it to int32
func (this *CodecCtx) Type() int {
return int(this.avCodecCtx.codec_type)
func (this *CodecCtx) Type() int32 {
return int32(this.avCodecCtx.codec_type)
}
func (this *CodecCtx) Width() int {
@@ -145,6 +147,14 @@ func (this *CodecCtx) PixFmt() int32 {
return int32(this.avCodecCtx.pix_fmt)
}
func (this *CodecCtx) FrameSize() int {
return int(this.avCodecCtx.frame_size)
}
func (this *CodecCtx) SampleFmt() int32 {
return this.avCodecCtx.sample_fmt
}
func (this *CodecCtx) GetProfile() int {
return int(this.avCodecCtx.profile)
}
@@ -158,6 +168,10 @@ func (this *CodecCtx) TimeBase() AVRational {
return AVRational(this.avCodecCtx.time_base)
}
func (this *CodecCtx) ChannelLayout() int {
return int(this.avCodecCtx.channel_layout)
}
func (this *CodecCtx) SetBitRate(val int) *CodecCtx {
this.avCodecCtx.bit_rate = C.int(val)
return this
@@ -203,3 +217,13 @@ func (this *CodecCtx) SetMbDecision(val int) *CodecCtx {
this.avCodecCtx.mb_decision = C.int(val)
return this
}
func (this *CodecCtx) SetSampleFmt(val int32) *CodecCtx {
this.avCodecCtx.sample_fmt = val
return this
}
func (this *CodecCtx) SetSampleRate(val int) *CodecCtx {
this.avCodecCtx.sample_rate = C.int(val)
return this
}

View File

@@ -7,9 +7,11 @@ import (
. "github.com/3d0c/gmf"
"log"
"os"
"runtime/debug"
)
func fatal(err error) {
debug.PrintStack()
log.Fatal(err)
os.Exit(0)
}
@@ -39,10 +41,14 @@ func addStream(codecName string, oc *FmtCtx, ist *Stream) (int, int) {
cc.CopyRequired(ist)
if cc.Type() == int(AVMEDIA_TYPE_VIDEO) && oc.IsGlobalHeader() {
if oc.IsGlobalHeader() {
cc.SetFlag(CODEC_FLAG_GLOBAL_HEADER)
}
if cc.Type() == AVMEDIA_TYPE_AUDIO {
cc.SetSampleFmt(AV_SAMPLE_FMT_S16).SetBitRate(64000)
}
if err := cc.Open(nil); err != nil {
fatal(err)
}
@@ -56,6 +62,8 @@ func main() {
var srcFileName, dstFileName string
var stMap map[int]int = make(map[int]int, 0)
log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
if len(os.Args) != 3 {
fmt.Println("Simple transcoder, it guesses source format and codecs and tries to convert it to v:mpeg4/a:mp2.")
fmt.Println("usage: [input filename] [output.mp4]")
@@ -91,46 +99,38 @@ func main() {
fatal(err)
}
i := 0
for packet := range inputCtx.Packets() {
ist := assert(inputCtx.GetStream(packet.StreamIndex())).(*Stream)
if ist.CodecCtx().Type() == int(AVMEDIA_TYPE_AUDIO) {
log.Println("skipping audio packet")
continue
}
ost := assert(outputCtx.GetStream(stMap[ist.Index()])).(*Stream)
frame, got, err := packet.Decode(ist.CodecCtx())
if got != 0 {
if ist.CodecCtx().Type() == int(AVMEDIA_TYPE_VIDEO) {
frame.SetPts(i)
for frame := range packet.Frames(ist.CodecCtx()) {
frame.SetPts(ost.Pts)
if ost.IsAudio() {
frame.
SetNbSamples(ost.CodecCtx().FrameSize()).
SetFormat(ost.CodecCtx().SampleFmt()).
SetChannelLayout(ost.CodecCtx().ChannelLayout())
}
ost := assert(outputCtx.GetStream(stMap[ist.Index()])).(*Stream)
if p, ready, _ := frame.Encode(ost.CodecCtx()); ready {
if ost.CodecCtx().Type() == int(AVMEDIA_TYPE_VIDEO) {
if p.Pts() != AV_NOPTS_VALUE {
p.SetPts(RescaleQ(p.Pts(), ost.CodecCtx().TimeBase(), ist.TimeBase()))
}
if p.Dts() != AV_NOPTS_VALUE {
p.SetDts(RescaleQ(p.Dts(), ost.CodecCtx().TimeBase(), ist.TimeBase()))
}
if p.Pts() != AV_NOPTS_VALUE {
p.SetPts(RescaleQ(p.Pts(), ost.CodecCtx().TimeBase(), ist.TimeBase()))
}
if p.Dts() != AV_NOPTS_VALUE {
p.SetDts(RescaleQ(p.Dts(), ost.CodecCtx().TimeBase(), ist.TimeBase()))
}
p.SetStreamIndex(ost.Index())
if err := outputCtx.WritePacket(p); err != nil {
fatal(err)
}
}
ost.Pts++
}
if got == 0 || err != nil {
fatal(err)
}
frame.Unref()
i++
}
}

View File

@@ -19,10 +19,7 @@ import (
"unsafe"
)
var (
AVMEDIA_TYPE_VIDEO int32 = C.AVMEDIA_TYPE_VIDEO
AVMEDIA_TYPE_AUDIO int32 = C.AVMEDIA_TYPE_AUDIO
)
var ()
type FmtCtx struct {
avCtx *_Ctype_AVFormatContext
@@ -161,7 +158,6 @@ func (this *FmtCtx) WriteHeader() error {
func (this *FmtCtx) WritePacket(p *Packet) error {
if averr := C.av_interleaved_write_frame(this.avCtx, &p.avPacket); averr < 0 {
// if averr := C.av_write_frame(this.avCtx, &p.avPacket); averr < 0 {
return errors.New(fmt.Sprintf("Unable to write packet to '%s': %s", this.ofmt.Filename, AvError(int(averr))))
}
@@ -242,7 +238,7 @@ func (this *FmtCtx) NewStream(c *Codec) *Stream {
if st := C.avformat_new_stream(this.avCtx, avCodec); st == nil {
return nil
} else {
return &Stream{avStream: st}
return &Stream{avStream: st, Pts: 0}
}
}

View File

@@ -131,7 +131,7 @@ func _TestPacketsIterator(t *testing.T) {
t.Fatal("Unexpected error:", err)
}
if stream.GetCodecCtx().Type() == CODEC_TYPE_AUDIO {
if stream.GetCodecCtx().Type() == AVMEDIA_TYPE_AUDIO {
// skip for tests
continue
}
@@ -286,7 +286,7 @@ func _TestEncode(t *testing.T) {
t.Fatal("Unexpected error:", err)
}
if stream.GetCodecCtx().Type() == CODEC_TYPE_AUDIO {
if stream.GetCodecCtx().Type() == AVMEDIA_TYPE_AUDIO {
// skip for tests
continue
}

View File

@@ -31,7 +31,7 @@ import (
type Frame struct {
avFrame *_Ctype_AVFrame
mediaType int
mediaType int32
}
func NewFrame() *Frame {
@@ -45,13 +45,13 @@ func (this *Frame) Encode(cc *CodecCtx) (*Packet, bool, error) {
p := NewPacket()
switch this.mediaType {
case CODEC_TYPE_AUDIO:
case AVMEDIA_TYPE_AUDIO:
ret = int(C.avcodec_encode_audio2(cc.avCodecCtx, &p.avPacket, this.avFrame, (*C.int)(unsafe.Pointer(&gotOutput))))
if ret < 0 {
return nil, false, errors.New(fmt.Sprintf("Unable to encode video packet, averror: %s", AvError(int(ret))))
}
case CODEC_TYPE_VIDEO:
case AVMEDIA_TYPE_VIDEO:
cc.avCodecCtx.field_order = C.AV_FIELD_PROGRESSIVE
ret = int(C.avcodec_encode_video2(cc.avCodecCtx, &p.avPacket, this.avFrame, (*C.int)(unsafe.Pointer(&gotOutput))))
@@ -68,10 +68,7 @@ func (this *Frame) Encode(cc *CodecCtx) (*Packet, bool, error) {
return p, ready, nil
}
func (this *Frame) Scale(width int, height int) *Frame {
return nil
}
// @remove
func (this *Frame) AvPtr() unsafe.Pointer {
return unsafe.Pointer(this.avFrame)
}
@@ -84,6 +81,7 @@ func (this *Frame) Unref() {
C.av_frame_unref(this.avFrame)
}
// @remove
func (this *Frame) SetPts(val int) {
this.avFrame.pts = (_Ctype_int64_t)(val)
}
@@ -137,7 +135,7 @@ func (this *Frame) KeyFrame() int {
}
func (this *Frame) SetFormat(val int32) *Frame {
this.avFrame.format = C.int(val) //C.int(val)
this.avFrame.format = C.int(val)
return this
}
@@ -151,6 +149,7 @@ func (this *Frame) SetHeight(val int) *Frame {
return this
}
// @todo PIX_FMT
func (this *Frame) ImgAlloc() error {
if ret := int(C.av_image_alloc(
(**C.uint8_t)(unsafe.Pointer(&this.avFrame.data)),
@@ -179,3 +178,13 @@ func (this *Frame) Clone() *Frame {
func (this *Frame) Free() {
C.av_frame_free(&this.avFrame)
}
func (this *Frame) SetNbSamples(val int) *Frame {
this.avFrame.nb_samples = C.int(val)
return this
}
func (this *Frame) SetChannelLayout(val int) *Frame {
this.avFrame.channel_layout = (_Ctype_uint64_t)(val)
return this
}

View File

@@ -28,7 +28,7 @@ import (
// > decoder).
// this stuff with map of singletons is used.
//
var frames map[int]*Frame = make(map[int]*Frame, 0)
var frames map[int32]*Frame = make(map[int32]*Frame, 0)
type Packet struct {
avPacket _Ctype_AVPacket
@@ -45,91 +45,49 @@ func NewPacket() *Packet {
return p
}
func (this *Packet) Decode(cc *CodecCtx) (*Frame, int, error) {
// @todo should be private
func (this *Packet) Decode(cc *CodecCtx) (*Frame, bool, int, error) {
var gotOutput int
var ret int = 0
if frames[cc.Type()] == nil {
frames[cc.Type()] = &Frame{avFrame: C.av_frame_alloc(), mediaType: cc.Type()}
}
switch cc.Type() {
case CODEC_TYPE_AUDIO:
ret := C.avcodec_decode_audio4(cc.avCodecCtx, frames[CODEC_TYPE_AUDIO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket)
case AVMEDIA_TYPE_AUDIO:
ret = int(C.avcodec_decode_audio4(cc.avCodecCtx, frames[AVMEDIA_TYPE_AUDIO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket))
if ret < 0 {
return nil, 0, errors.New(fmt.Sprintf("Unable to decode audio packet, averror: %s", AvError(int(ret))))
return nil, false, int(ret), errors.New(fmt.Sprintf("Unable to decode audio packet, averror: %s", AvError(int(ret))))
}
break
case CODEC_TYPE_VIDEO:
// pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
// this.avPacket.dts = C.av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base)
fmt.Println(cc.avCodecCtx)
ret := C.avcodec_decode_video2(cc.avCodecCtx, frames[CODEC_TYPE_VIDEO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket)
case AVMEDIA_TYPE_VIDEO:
ret = int(C.avcodec_decode_video2(cc.avCodecCtx, frames[AVMEDIA_TYPE_VIDEO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket))
if ret < 0 {
return nil, 0, errors.New(fmt.Sprintf("Unable to decode video packet, averror: %s", AvError(int(ret))))
return nil, false, int(ret), errors.New(fmt.Sprintf("Unable to decode video packet, averror: %s", AvError(int(ret))))
}
break
default:
return nil, 0, errors.New(fmt.Sprintf("Unknown codec type: %v", cc.Type()))
return nil, false, int(ret), errors.New(fmt.Sprintf("Unknown codec type: %v", cc.Type()))
}
return frames[cc.Type()], gotOutput, nil
return frames[cc.Type()], (gotOutput > 0), int(ret), nil
}
func (this *Packet) DecodeV2(cc *CodecCtx) *Frame {
var gotOutput int
if frames[cc.Type()] == nil {
frames[cc.Type()] = &Frame{avFrame: C.av_frame_alloc(), mediaType: cc.Type()}
}
ret := C.avcodec_decode_video2(cc.avCodecCtx, frames[CODEC_TYPE_VIDEO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket)
if ret < 0 {
return nil
fmt.Printf("Unable to decode video packet, averror: %s", AvError(int(ret)))
}
if gotOutput != 0 {
frames[cc.Type()].avFrame.pts = C.av_frame_get_best_effort_timestamp(frames[cc.Type()].avFrame)
return frames[cc.Type()]
}
return nil
}
func (this *Packet) DecodeV(cc *CodecCtx) chan *Frame {
var gotOutput int
if frames[cc.Type()] == nil {
frames[cc.Type()] = &Frame{avFrame: C.av_frame_alloc(), mediaType: cc.Type()}
}
func (this *Packet) Frames(cc *CodecCtx) chan *Frame {
yield := make(chan *Frame)
go func() {
defer close(yield)
for {
ret := C.avcodec_decode_video2(cc.avCodecCtx, frames[CODEC_TYPE_VIDEO].avFrame, (*C.int)(unsafe.Pointer(&gotOutput)), &this.avPacket)
if ret < 0 {
break
fmt.Printf("Unable to decode video packet, averror: %s", AvError(int(ret)))
}
if ret == 0 && gotOutput == 0 {
break
}
if ret == 0 {
continue
}
if gotOutput != 0 {
// frame->pts = av_frame_get_best_effort_timestamp(frame);
frames[cc.Type()].avFrame.pts = C.av_frame_get_best_effort_timestamp(frames[cc.Type()].avFrame)
yield <- frames[cc.Type()]
frame, ready, ret, _ := this.Decode(cc)
if ready {
yield <- frame
}
C.shift_data(&this.avPacket, C.int(ret))
@@ -138,8 +96,6 @@ func (this *Packet) DecodeV(cc *CodecCtx) chan *Frame {
break
}
}
close(yield)
}()
return yield
@@ -182,5 +138,11 @@ func (this *Packet) Data() []byte {
}
func (this *Packet) Dump() {
fmt.Println(this.avPacket)
fmt.Println("pkt:{\n", "pts:", this.avPacket.pts, "\ndts:", this.avPacket.dts, "\ndata:", string(C.GoBytes(unsafe.Pointer(this.avPacket.data), 128)), "size:", this.avPacket.size, "\n}")
}
func (this *Packet) SetStreamIndex(val int) *Packet {
this.avPacket.stream_index = C.int(val)
return this
}

View File

@@ -16,6 +16,7 @@ import (
type Stream struct {
avStream *_Ctype_AVStream
cc *CodecCtx
Pts int
}
func (this *Stream) CodecCtx() *CodecCtx {
@@ -33,6 +34,10 @@ func (this *Stream) CodecCtx() *CodecCtx {
avCodecCtx: this.avStream.codec,
}
if err := this.cc.Open(nil); err != nil {
panic(fmt.Sprintf("Can't open code for stream '%d', error: %v", this.Index(), err))
}
return this.cc
}
@@ -69,3 +74,15 @@ func (this *Stream) NbFrames() int {
func (this *Stream) TimeBase() AVRational {
return AVRational(this.avStream.time_base)
}
func (this *Stream) Type() int32 {
return this.CodecCtx().Type()
}
func (this *Stream) IsAudio() bool {
return (this.Type() == AVMEDIA_TYPE_AUDIO)
}
func (this *Stream) IsVideo() bool {
return (this.Type() == AVMEDIA_TYPE_VIDEO)
}