From 001779d0326de9f52d44338ca3e45f15065a3bc2 Mon Sep 17 00:00:00 2001 From: Corebreaker Date: Mon, 17 Dec 2018 20:10:57 +0300 Subject: [PATCH] Working tutorial; More hooks to libav API; removes debbuging print Missing constants, additional functions, remove errors --- .gitignore | 2 + _config.yml | 1 - avcodec/avcodec.go | 33 +++ avcodec/context.go | 25 +- avcodec/flags.go | 39 +++ avcodec/packet.go | 8 + avcodec/packet_struct.go | 12 + avcodec/pixel.go | 171 +++++++++++++ avcodec/rational.go | 45 ++++ avformat/avformat.go | 34 ++- avformat/codec_context_struct.go | 150 ++++++++++++ avformat/context.go | 29 ++- avformat/context_struct.go | 21 +- avformat/flags.go | 23 ++ avformat/media_types.go | 17 ++ avformat/packet.go | 21 ++ avformat/rational.go | 13 + avformat/stream.go | 16 +- avformat/stream_struct.go | 30 ++- avutil/avutil.go | 1 + avutil/error.go | 28 +++ avutil/frame.go | 8 +- common/types.go | 19 -- example/tutorial01.go | 404 ++++++++++++++----------------- example/versions.go | 3 +- go.mod | 6 + go.sum | 4 + 27 files changed, 861 insertions(+), 302 deletions(-) create mode 100644 .gitignore delete mode 100644 _config.yml create mode 100644 avcodec/flags.go create mode 100644 avcodec/pixel.go create mode 100644 avcodec/rational.go create mode 100644 avformat/codec_context_struct.go create mode 100644 avformat/flags.go create mode 100644 avformat/media_types.go create mode 100644 avformat/packet.go create mode 100644 avformat/rational.go create mode 100644 avutil/error.go delete mode 100644 common/types.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66f8fb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.vscode/ diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c741881..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-slate \ No newline at end of file diff --git a/avcodec/avcodec.go b/avcodec/avcodec.go index f023b5c..19695d8 100644 --- a/avcodec/avcodec.go +++ b/avcodec/avcodec.go @@ -33,6 +33,7 @@ type ( BitStreamFilterContext C.struct_AVBitStreamFilterContext Rational C.struct_AVRational Class C.struct_AVClass + AvCodecParameters C.struct_AVCodecParameters AvHWAccel C.struct_AVHWAccel AvPacketSideData C.struct_AVPacketSideData AvPanScan C.struct_AVPanScan @@ -56,6 +57,30 @@ type ( AvSampleFormat C.enum_AVSampleFormat ) +func (cp *AvCodecParameters) AvCodecGetId() CodecId { + return *((*CodecId)(unsafe.Pointer(&cp.codec_id))) +} + +func (cp *AvCodecParameters) AvCodecGetType() MediaType { + return *((*MediaType)(unsafe.Pointer(&cp.codec_type))) +} + +func (cp *AvCodecParameters) AvCodecGetWidth() int { + return *((*int)(unsafe.Pointer(&cp.width))) +} + +func (cp *AvCodecParameters) AvCodecGetHeight() int { + return *((*int)(unsafe.Pointer(&cp.height))) +} + +func (cp *AvCodecParameters) AvCodecGetChannels() int { + return *((*int)(unsafe.Pointer(&cp.channels))) +} + +func (cp *AvCodecParameters) AvCodecGetSampleRate() int { + return *((*int)(unsafe.Pointer(&cp.sample_rate))) +} + func (c *Codec) AvCodecGetMaxLowres() int { return int(C.av_codec_get_max_lowres((*C.struct_AVCodec)(c))) } @@ -136,6 +161,10 @@ func AvsubtitleFree(s *AvSubtitle) { C.avsubtitle_free((*C.struct_AVSubtitle)(s)) } +func AvPacketAlloc() *Packet { + return (*Packet)(C.av_packet_alloc()) +} + //Pack a dictionary for use in side_data. func AvPacketPackDictionary(d *Dictionary, s *int) *uint8 { return (*uint8)(C.av_packet_pack_dictionary((*C.struct_AVDictionary)(d), (*C.int)(unsafe.Pointer(s)))) @@ -151,6 +180,10 @@ func AvcodecFindDecoder(id CodecId) *Codec { return (*Codec)(C.avcodec_find_decoder((C.enum_AVCodecID)(id))) } +func AvCodecIterate(p *unsafe.Pointer) *Codec { + return (*Codec)(C.av_codec_iterate(p)) +} + //Find a registered decoder with the specified name. func AvcodecFindDecoderByName(n string) *Codec { return (*Codec)(C.avcodec_find_decoder_by_name(C.CString(n))) diff --git a/avcodec/context.go b/avcodec/context.go index 94f2b4e..97695d7 100644 --- a/avcodec/context.go +++ b/avcodec/context.go @@ -8,22 +8,15 @@ package avcodec import "C" import ( "unsafe" - - "github.com/selfmodify/goav/common" ) func (ctxt *Context) AvCodecGetPktTimebase() Rational { - return (Rational)(C.av_codec_get_pkt_timebase((*C.struct_AVCodecContext)(ctxt))) + return Rational(C.av_codec_get_pkt_timebase((*C.struct_AVCodecContext)(ctxt))) } // AvCodecGetPktTimebase2 returns the timebase rational number as numerator and denominator -func (ctxt *Context) AvCodecGetPktTimebase2() (timebase common.AVRational) { - r := ctxt.AvCodecGetPktTimebase() - timebase = common.AVRational{ - Num: int(r.num), - Den: int(r.den), - } - return +func (ctxt *Context) AvCodecGetPktTimebase2() Rational { + return ctxt.AvCodecGetPktTimebase() } func (ctxt *Context) AvCodecSetPktTimebase(r Rational) { @@ -179,7 +172,7 @@ func (ctxt *Context) SetTimebase(num1 int, den1 int) { ctxt.time_base.den = C.int(den1) } -func (ctxt *Context) SetEncodeParams2(width int, height int, pxlFmt common.PixelFormat, hasBframes bool, gopSize int) { +func (ctxt *Context) SetEncodeParams2(width int, height int, pxlFmt PixelFormat, hasBframes bool, gopSize int) { ctxt.width = C.int(width) ctxt.height = C.int(height) // ctxt.bit_rate = 1000000 @@ -197,6 +190,14 @@ func (ctxt *Context) SetEncodeParams2(width int, height int, pxlFmt common.Pixel // C.av_opt_set(ctxt.priv_data, "preset", "ultrafast", 0) } -func (ctxt *Context) SetEncodeParams(width int, height int, pxlFmt common.PixelFormat) { +func (ctxt *Context) SetEncodeParams(width int, height int, pxlFmt PixelFormat) { ctxt.SetEncodeParams2(width, height, pxlFmt, false /*no b frames*/, 10) } + +func (ctxt *Context) AvcodecSendPacket(packet *Packet) int { + return (int)(C.avcodec_send_packet((*C.struct_AVCodecContext)(ctxt), (*C.struct_AVPacket)(packet))) +} + +func (ctxt *Context) AvcodecReceiveFrame(frame *Frame) int { + return (int)(C.avcodec_receive_frame((*C.struct_AVCodecContext)(ctxt), (*C.struct_AVFrame)(frame))) +} diff --git a/avcodec/flags.go b/avcodec/flags.go new file mode 100644 index 0000000..d40326d --- /dev/null +++ b/avcodec/flags.go @@ -0,0 +1,39 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +package avcodec + +//#cgo pkg-config: libavcodec +//#include +import "C" + +const ( + AV_CODEC_FLAG_UNALIGNED = int(C.AV_CODEC_FLAG_UNALIGNED) + AV_CODEC_FLAG_QSCALE = int(C.AV_CODEC_FLAG_QSCALE) + AV_CODEC_FLAG_4MV = int(C.AV_CODEC_FLAG_4MV) + AV_CODEC_FLAG_OUTPUT_CORRUPT = int(C.AV_CODEC_FLAG_OUTPUT_CORRUPT) + AV_CODEC_FLAG_QPEL = int(C.AV_CODEC_FLAG_QPEL) + AV_CODEC_FLAG_PASS1 = int(C.AV_CODEC_FLAG_PASS1) + AV_CODEC_FLAG_PASS2 = int(C.AV_CODEC_FLAG_PASS2) + AV_CODEC_FLAG_LOOP_FILTER = int(C.AV_CODEC_FLAG_LOOP_FILTER) + AV_CODEC_FLAG_GRAY = int(C.AV_CODEC_FLAG_GRAY) + AV_CODEC_FLAG_PSNR = int(C.AV_CODEC_FLAG_PSNR) + AV_CODEC_FLAG_TRUNCATED = int(C.AV_CODEC_FLAG_TRUNCATED) + AV_CODEC_FLAG_INTERLACED_DCT = int(C.AV_CODEC_FLAG_INTERLACED_DCT) + AV_CODEC_FLAG_LOW_DELAY = int(C.AV_CODEC_FLAG_LOW_DELAY) + AV_CODEC_FLAG_GLOBAL_HEADER = int(C.AV_CODEC_FLAG_GLOBAL_HEADER) + AV_CODEC_FLAG_BITEXACT = int(C.AV_CODEC_FLAG_BITEXACT) + AV_CODEC_FLAG_AC_PRED = int(C.AV_CODEC_FLAG_AC_PRED) + AV_CODEC_FLAG_INTERLACED_ME = int(C.AV_CODEC_FLAG_INTERLACED_ME) + AV_CODEC_FLAG_CLOSED_GOP = int(C.AV_CODEC_FLAG_CLOSED_GOP) + AV_CODEC_FLAG2_FAST = int(C.AV_CODEC_FLAG2_FAST) + AV_CODEC_FLAG2_NO_OUTPUT = int(C.AV_CODEC_FLAG2_NO_OUTPUT) + AV_CODEC_FLAG2_LOCAL_HEADER = int(C.AV_CODEC_FLAG2_LOCAL_HEADER) + AV_CODEC_FLAG2_DROP_FRAME_TIMECODE = int(C.AV_CODEC_FLAG2_DROP_FRAME_TIMECODE) + AV_CODEC_FLAG2_CHUNKS = int(C.AV_CODEC_FLAG2_CHUNKS) + AV_CODEC_FLAG2_IGNORE_CROP = int(C.AV_CODEC_FLAG2_IGNORE_CROP) + AV_CODEC_FLAG2_SHOW_ALL = int(C.AV_CODEC_FLAG2_SHOW_ALL) + AV_CODEC_FLAG2_EXPORT_MVS = int(C.AV_CODEC_FLAG2_EXPORT_MVS) + AV_CODEC_FLAG2_SKIP_MANUAL = int(C.AV_CODEC_FLAG2_SKIP_MANUAL) + AV_CODEC_FLAG2_RO_FLUSH_NOOP = int(C.AV_CODEC_FLAG2_RO_FLUSH_NOOP) +) diff --git a/avcodec/packet.go b/avcodec/packet.go index 37384d4..a59d087 100644 --- a/avcodec/packet.go +++ b/avcodec/packet.go @@ -10,9 +10,17 @@ import ( "unsafe" ) +const ( + AV_PKT_FLAG_KEY = int(C.AV_PKT_FLAG_KEY) + AV_PKT_FLAG_CORRUPT = int(C.AV_PKT_FLAG_CORRUPT) + AV_PKT_FLAG_DISCARD = int(C.AV_PKT_FLAG_DISCARD) +) + //Initialize optional fields of a packet with default values. func (p *Packet) AvInitPacket() { C.av_init_packet((*C.struct_AVPacket)(p)) + p.size = 0 + p.data = nil } //Allocate the payload of a packet and initialize its fields with default values. diff --git a/avcodec/packet_struct.go b/avcodec/packet_struct.go index 0c6dc51..255ff3d 100644 --- a/avcodec/packet_struct.go +++ b/avcodec/packet_struct.go @@ -16,6 +16,9 @@ func (p *Packet) Duration() int { func (p *Packet) Flags() int { return int(p.flags) } +func (p *Packet) SetFlags(flags int) { + p.flags = C.int(flags) +} func (p *Packet) SideDataElems() int { return int(p.side_data_elems) } @@ -25,18 +28,27 @@ func (p *Packet) Size() int { func (p *Packet) StreamIndex() int { return int(p.stream_index) } +func (p *Packet) SetStreamIndex(idx int) { + p.stream_index = C.int(idx) +} func (p *Packet) ConvergenceDuration() int64 { return int64(p.convergence_duration) } func (p *Packet) Dts() int64 { return int64(p.dts) } +func (p *Packet) SetDts(dts int64) { + p.dts = C.int64_t(dts) +} func (p *Packet) Pos() int64 { return int64(p.pos) } func (p *Packet) Pts() int64 { return int64(p.pts) } +func (p *Packet) SetPts(pts int64) { + p.dts = C.int64_t(pts) +} func (p *Packet) Data() *uint8 { return (*uint8)(p.data) } diff --git a/avcodec/pixel.go b/avcodec/pixel.go new file mode 100644 index 0000000..b79117f --- /dev/null +++ b/avcodec/pixel.go @@ -0,0 +1,171 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +//Package avcodec contains the codecs (decoders and encoders) provided by the libavcodec library +//Provides some generic global options, which can be set on all the encoders and decoders. +package avcodec + +//#cgo pkg-config: libavformat libavcodec libavutil +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +import "C" + +const ( + AV_PIX_FMT_YUV = 0 + AV_PIX_FMT_YUV420P9 = C.AV_PIX_FMT_YUV420P9 + AV_PIX_FMT_YUV422P9 = C.AV_PIX_FMT_YUV422P9 + AV_PIX_FMT_YUV444P9 = C.AV_PIX_FMT_YUV444P9 + AV_PIX_FMT_YUV420P10 = C.AV_PIX_FMT_YUV420P10 + AV_PIX_FMT_YUV422P10 = C.AV_PIX_FMT_YUV422P10 + AV_PIX_FMT_YUV440P10 = C.AV_PIX_FMT_YUV440P10 + AV_PIX_FMT_YUV444P10 = C.AV_PIX_FMT_YUV444P10 + AV_PIX_FMT_YUV420P12 = C.AV_PIX_FMT_YUV420P12 + AV_PIX_FMT_YUV422P12 = C.AV_PIX_FMT_YUV422P12 + AV_PIX_FMT_YUV440P12 = C.AV_PIX_FMT_YUV440P12 + AV_PIX_FMT_YUV444P12 = C.AV_PIX_FMT_YUV444P12 + AV_PIX_FMT_YUV420P14 = C.AV_PIX_FMT_YUV420P14 + AV_PIX_FMT_YUV422P14 = C.AV_PIX_FMT_YUV422P14 + AV_PIX_FMT_YUV444P14 = C.AV_PIX_FMT_YUV444P14 + AV_PIX_FMT_YUV420P16 = C.AV_PIX_FMT_YUV420P16 + AV_PIX_FMT_YUV422P16 = C.AV_PIX_FMT_YUV422P16 + AV_PIX_FMT_YUV444P16 = C.AV_PIX_FMT_YUV444P16 + AV_PIX_FMT_YUVA420P9 = C.AV_PIX_FMT_YUVA420P9 + AV_PIX_FMT_YUVA422P9 = C.AV_PIX_FMT_YUVA422P9 + AV_PIX_FMT_YUVA444P9 = C.AV_PIX_FMT_YUVA444P9 + AV_PIX_FMT_YUVA420P10 = C.AV_PIX_FMT_YUVA420P10 + AV_PIX_FMT_YUVA422P10 = C.AV_PIX_FMT_YUVA422P10 + AV_PIX_FMT_YUVA444P10 = C.AV_PIX_FMT_YUVA444P10 + AV_PIX_FMT_YUVA420P16 = C.AV_PIX_FMT_YUVA420P16 + AV_PIX_FMT_YUVA422P16 = C.AV_PIX_FMT_YUVA422P16 + AV_PIX_FMT_YUVA444P16 = C.AV_PIX_FMT_YUVA444P16 + AV_PIX_FMT_RGB24 = C.AV_PIX_FMT_RGB24 + AV_PIX_FMT_RGBA = C.AV_PIX_FMT_RGBA + + SWS_FAST_BILINEAR = C.SWS_FAST_BILINEAR + SWS_BILINEAR = C.SWS_BILINEAR + SWS_BICUBIC = C.SWS_BICUBIC + SWS_X = C.SWS_X + SWS_POINT = C.SWS_POINT + SWS_AREA = C.SWS_AREA + SWS_BICUBLIN = C.SWS_BICUBLIN + SWS_GAUSS = C.SWS_GAUSS + SWS_SINC = C.SWS_SINC + SWS_LANCZOS = C.SWS_LANCZOS + SWS_SPLINE = C.SWS_SPLINE + SWS_SRC_V_CHR_DROP_MASK = C.SWS_SRC_V_CHR_DROP_MASK + SWS_SRC_V_CHR_DROP_SHIFT = C.SWS_SRC_V_CHR_DROP_SHIFT + SWS_PARAM_DEFAULT = C.SWS_PARAM_DEFAULT + SWS_PRINT_INFO = C.SWS_PRINT_INFO + SWS_FULL_CHR_H_INT = C.SWS_FULL_CHR_H_INT + SWS_FULL_CHR_H_INP = C.SWS_FULL_CHR_H_INP + SWS_DIRECT_BGR = C.SWS_DIRECT_BGR + SWS_ACCURATE_RND = C.SWS_ACCURATE_RND + SWS_BITEXACT = C.SWS_BITEXACT + SWS_ERROR_DIFFUSION = C.SWS_ERROR_DIFFUSION + SWS_MAX_REDUCE_CUTOFF = C.SWS_MAX_REDUCE_CUTOFF + SWS_CS_ITU709 = C.SWS_CS_ITU709 + SWS_CS_FCC = C.SWS_CS_FCC + SWS_CS_ITU601 = C.SWS_CS_ITU601 + SWS_CS_ITU624 = C.SWS_CS_ITU624 + SWS_CS_SMPTE170M = C.SWS_CS_SMPTE170M + SWS_CS_SMPTE240M = C.SWS_CS_SMPTE240M + SWS_CS_DEFAULT = C.SWS_CS_DEFAULT + SWS_CS_BT2020 = C.SWS_CS_BT2020 +) + +func (pf PixelFormat) String() string { + switch int(pf) { + case AV_PIX_FMT_YUV420P9: + return "YUV420P9" + + case AV_PIX_FMT_YUV422P9: + return "YUV422P9" + + case AV_PIX_FMT_YUV444P9: + return "YUV444P9" + + case AV_PIX_FMT_YUV420P10: + return "YUV420P10" + + case AV_PIX_FMT_YUV422P10: + return "YUV422P10" + + case AV_PIX_FMT_YUV440P10: + return "YUV440P10" + + case AV_PIX_FMT_YUV444P10: + return "YUV444P10" + + case AV_PIX_FMT_YUV420P12: + return "YUV420P12" + + case AV_PIX_FMT_YUV422P12: + return "YUV422P12" + + case AV_PIX_FMT_YUV440P12: + return "YUV440P12" + + case AV_PIX_FMT_YUV444P12: + return "YUV444P12" + + case AV_PIX_FMT_YUV420P14: + return "YUV420P14" + + case AV_PIX_FMT_YUV422P14: + return "YUV422P14" + + case AV_PIX_FMT_YUV444P14: + return "YUV444P14" + + case AV_PIX_FMT_YUV420P16: + return "YUV420P16" + + case AV_PIX_FMT_YUV422P16: + return "YUV422P16" + + case AV_PIX_FMT_YUV444P16: + return "YUV444P16" + + case AV_PIX_FMT_YUVA420P9: + return "YUVA420P9" + + case AV_PIX_FMT_YUVA422P9: + return "YUVA422P9" + + case AV_PIX_FMT_YUVA444P9: + return "YUVA444P9" + + case AV_PIX_FMT_YUVA420P10: + return "YUVA420P10" + + case AV_PIX_FMT_YUVA422P10: + return "YUVA422P10" + + case AV_PIX_FMT_YUVA444P10: + return "YUVA444P10" + + case AV_PIX_FMT_YUVA420P16: + return "YUVA420P16" + + case AV_PIX_FMT_YUVA422P16: + return "YUVA422P16" + + case AV_PIX_FMT_YUVA444P16: + return "YUVA444P16" + + case AV_PIX_FMT_RGB24: + return "RGB24" + + case AV_PIX_FMT_RGBA: + return "RGBA" + } + + return "{UNKNOWN}" +} diff --git a/avcodec/rational.go b/avcodec/rational.go new file mode 100644 index 0000000..ee17df0 --- /dev/null +++ b/avcodec/rational.go @@ -0,0 +1,45 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +//Package avcodec contains the codecs (decoders and encoders) provided by the libavcodec library +//Provides some generic global options, which can be set on all the encoders and decoders. +package avcodec + +//#cgo pkg-config: libavformat libavcodec libavutil +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +import "C" +import "fmt" + +func (r Rational) Num() int { + return int(r.num) +} + +func (r Rational) Den() int { + return int(r.den) +} + +func (r Rational) String() string { + return fmt.Sprintln("%d/%d", int(r.num), int(r.den)) +} + +func (r *Rational) Assign(o Rational) { + r.Set(o.Num(), o.Den()) +} + +func (r *Rational) Set(num, den int) { + r.num, r.den = C.int(num), C.int(den) +} + +func NewRational(num, den int) Rational { + return Rational{ + num: C.int(num), + den: C.int(den), + } +} diff --git a/avformat/avformat.go b/avformat/avformat.go index 28dc316..6ed928b 100644 --- a/avformat/avformat.go +++ b/avformat/avformat.go @@ -20,6 +20,9 @@ package avformat import "C" import ( "unsafe" + + "github.com/giorgisio/goav/avcodec" + "github.com/giorgisio/goav/avutil" ) type ( @@ -35,10 +38,8 @@ type ( AvProgram C.struct_AVProgram AvChapter C.struct_AVChapter AvPacketList C.struct_AVPacketList - Packet C.struct_AVPacket CodecParserContext C.struct_AVCodecParserContext AvIOContext C.struct_AVIOContext - Rational C.struct_AVRational AvCodec C.struct_AVCodec AvCodecTag C.struct_AVCodecTag Class C.struct_AVClass @@ -57,13 +58,17 @@ type ( type File C.FILE //Allocate and read the payload of a packet and initialize its fields with default values. -func (ctxt *AvIOContext) AvGetPacket(pkt *Packet, s int) int { - return int(C.av_get_packet((*C.struct_AVIOContext)(ctxt), (*C.struct_AVPacket)(pkt), C.int(s))) +func (ctxt *AvIOContext) AvGetPacket(pkt *avcodec.Packet, s int) int { + return int(C.av_get_packet((*C.struct_AVIOContext)(ctxt), toCPacket(pkt), C.int(s))) } //Read data and append it to the current content of the Packet. -func (ctxt *AvIOContext) AvAppendPacket(pkt *Packet, s int) int { - return int(C.av_append_packet((*C.struct_AVIOContext)(ctxt), (*C.struct_AVPacket)(pkt), C.int(s))) +func (ctxt *AvIOContext) AvAppendPacket(pkt *avcodec.Packet, s int) int { + return int(C.av_append_packet((*C.struct_AVIOContext)(ctxt), toCPacket(pkt), C.int(s))) +} + +func (ctxt *AvIOContext) Close() error { + return avutil.ErrorFromCode(int(C.avio_close((*C.AVIOContext)(unsafe.Pointer(ctxt))))) } func (f *InputFormat) AvRegisterInputFormat() { @@ -190,13 +195,13 @@ func AvHexDumpLog(a, l int, b *uint8, s int) { } //Send a nice dump of a packet to the specified file stream. -func AvPktDump2(f *File, pkt *Packet, dp int, st *Stream) { - C.av_pkt_dump2((*C.FILE)(f), (*C.struct_AVPacket)(pkt), C.int(dp), (*C.struct_AVStream)(st)) +func AvPktDump2(f *File, pkt *avcodec.Packet, dp int, st *Stream) { + C.av_pkt_dump2((*C.FILE)(f), toCPacket(pkt), C.int(dp), (*C.struct_AVStream)(st)) } //Send a nice dump of a packet to the log. -func AvPktDumpLog2(a int, l int, pkt *Packet, dp int, st *Stream) { - C.av_pkt_dump_log2(unsafe.Pointer(&a), C.int(l), (*C.struct_AVPacket)(pkt), C.int(dp), (*C.struct_AVStream)(st)) +func AvPktDumpLog2(a int, l int, pkt *avcodec.Packet, dp int, st *Stream) { + C.av_pkt_dump_log2(unsafe.Pointer(&a), C.int(l), toCPacket(pkt), C.int(dp), (*C.struct_AVStream)(st)) } //enum CodecId av_codec_get_id (const struct AvCodecTag *const *tags, unsigned int tag) @@ -273,3 +278,12 @@ func AvformatGetMovVideoTags() *AvCodecTag { func AvformatGetMovAudioTags() *AvCodecTag { return (*AvCodecTag)(C.avformat_get_mov_audio_tags()) } + +func AvIOOpen(url string, flags int) (res *AvIOContext, err error) { + urlStr := C.CString(url) + defer C.free(unsafe.Pointer(urlStr)) + + err = avutil.ErrorFromCode(int(C.avio_open((**C.AVIOContext)(unsafe.Pointer(&res)), urlStr, C.int(flags)))) + + return +} diff --git a/avformat/codec_context_struct.go b/avformat/codec_context_struct.go new file mode 100644 index 0000000..0fe5485 --- /dev/null +++ b/avformat/codec_context_struct.go @@ -0,0 +1,150 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +//Package avformat provides some generic global options, which can be set on all the muxers and demuxers. +//In addition each muxer or demuxer may support so-called private options, which are specific for that component. +//Supported formats (muxers and demuxers) provided by the libavformat library +package avformat + +//#cgo pkg-config: libavformat libavcodec libavutil libavdevice libavfilter libswresample libswscale +//#include +//#include +//#include +//#include +//#include +//#include +//#include +import "C" +import ( + "reflect" + "unsafe" + + "github.com/giorgisio/goav/avcodec" +) + +func (cctxt *CodecContext) Type() MediaType { + return MediaType(cctxt.codec_type) +} + +func (cctxt *CodecContext) SetBitRate(br int64) { + cctxt.bit_rate = C.int64_t(br) +} + +func (cctxt *CodecContext) GetCodecId() CodecId { + return CodecId(cctxt.codec_id) +} + +func (cctxt *CodecContext) SetCodecId(codecId CodecId) { + cctxt.codec_id = C.enum_AVCodecID(codecId) +} + +func (cctxt *CodecContext) GetCodecType() MediaType { + return MediaType(cctxt.codec_type) +} + +func (cctxt *CodecContext) SetCodecType(ctype MediaType) { + cctxt.codec_type = C.enum_AVMediaType(ctype) +} + +func (cctxt *CodecContext) GetTimeBase() avcodec.Rational { + return newRational(cctxt.time_base) +} + +func (cctxt *CodecContext) SetTimeBase(timeBase avcodec.Rational) { + cctxt.time_base.num = C.int(timeBase.Num()) + cctxt.time_base.den = C.int(timeBase.Den()) +} + +func (cctx *CodecContext) GetWidth() int { + return int(cctx.width) +} + +func (cctx *CodecContext) SetWidth(w int) { + cctx.width = C.int(w) +} + +func (cctx *CodecContext) GetHeight() int { + return int(cctx.height) +} + +func (cctx *CodecContext) SetHeight(h int) { + cctx.height = C.int(h) +} + +func (cctx *CodecContext) GetPixelFormat() avcodec.PixelFormat { + return avcodec.PixelFormat(C.int(cctx.pix_fmt)) +} + +func (cctx *CodecContext) SetPixelFormat(fmt avcodec.PixelFormat) { + cctx.pix_fmt = C.enum_AVPixelFormat(C.int(fmt)) +} + +func (cctx *CodecContext) GetFlags() int { + return int(cctx.flags) +} + +func (cctx *CodecContext) SetFlags(flags int) { + cctx.flags = C.int(flags) +} + +func (cctx *CodecContext) GetMeRange() int { + return int(cctx.me_range) +} + +func (cctx *CodecContext) SetMeRange(r int) { + cctx.me_range = C.int(r) +} + +func (cctx *CodecContext) GetMaxQDiff() int { + return int(cctx.max_qdiff) +} + +func (cctx *CodecContext) SetMaxQDiff(v int) { + cctx.max_qdiff = C.int(v) +} + +func (cctx *CodecContext) GetQMin() int { + return int(cctx.qmin) +} + +func (cctx *CodecContext) SetQMin(v int) { + cctx.qmin = C.int(v) +} + +func (cctx *CodecContext) GetQMax() int { + return int(cctx.qmax) +} + +func (cctx *CodecContext) SetQMax(v int) { + cctx.qmax = C.int(v) +} + +func (cctx *CodecContext) GetQCompress() float32 { + return float32(cctx.qcompress) +} + +func (cctx *CodecContext) SetQCompress(v float32) { + cctx.qcompress = C.float(v) +} + +func (cctx *CodecContext) GetExtraData() []byte { + header := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(cctx.extradata)), + Len: int(cctx.extradata_size), + Cap: int(cctx.extradata_size), + } + + return *((*[]byte)(unsafe.Pointer(&header))) +} + +func (cctx *CodecContext) SetExtraData(data []byte) { + header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + + cctx.extradata = (*C.uint8_t)(unsafe.Pointer(header.Data)) + cctx.extradata_size = C.int(header.Len) +} + +func (cctx *CodecContext) Release() { + C.avcodec_close((*C.struct_AVCodecContext)(unsafe.Pointer(cctx))) + C.av_freep(unsafe.Pointer(cctx)) +} diff --git a/avformat/context.go b/avformat/context.go index 3f044a0..c25b536 100644 --- a/avformat/context.go +++ b/avformat/context.go @@ -10,8 +10,7 @@ import ( "time" "unsafe" - "github.com/selfmodify/goav/avcodec" - "github.com/selfmodify/goav/common" + "github.com/giorgisio/goav/avcodec" ) const ( @@ -106,7 +105,7 @@ func AvFindBestStream(ic *Context, t MediaType, ws, rs int, c **AvCodec, f int) //Return the next frame of a stream. func (s *Context) AvReadFrame(pkt *avcodec.Packet) int { - return int(C.av_read_frame((*C.struct_AVFormatContext)(unsafe.Pointer(s)), (*C.struct_AVPacket)(unsafe.Pointer(pkt)))) + return int(C.av_read_frame((*C.struct_AVFormatContext)(unsafe.Pointer(s)), toCPacket(pkt))) } //Seek to the keyframe at timestamp. @@ -116,8 +115,8 @@ func (s *Context) AvSeekFrame(st int, t int64, f int) int { // AvSeekFrameTime seeks to a specified time location. // |timebase| is codec specific and can be obtained by calling AvCodecGetPktTimebase2 -func (s *Context) AvSeekFrameTime(st int, at time.Duration, timebase common.AVRational) int { - t2 := C.double(C.double(at.Seconds())*C.double(timebase.Den)) / (C.double(timebase.Num)) +func (s *Context) AvSeekFrameTime(st int, at time.Duration, timebase avcodec.Rational) int { + t2 := C.double(C.double(at.Seconds())*C.double(timebase.Den())) / (C.double(timebase.Num())) // log.Printf("Seeking to time :%v TimebaseTime:%v ActualTimebase:%v", at, t2, timebase) return int(C.av_seek_frame((*C.struct_AVFormatContext)(s), C.int(st), C.int64_t(t2), AvseekFlagBackward)) } @@ -139,7 +138,7 @@ func (s *Context) AvReadPause() int { //Close an opened input Context. func (s *Context) AvformatCloseInput() { - C.avformat_close_input((**C.struct_AVFormatContext)(unsafe.Pointer(s))) + C.avformat_close_input((**C.struct_AVFormatContext)(unsafe.Pointer(&s))) } //Allocate the stream private data and write the stream header to an output media file. @@ -148,13 +147,13 @@ func (s *Context) AvformatWriteHeader(o **Dictionary) int { } //Write a packet to an output media file. -func (s *Context) AvWriteFrame(pkt *Packet) int { - return int(C.av_write_frame((*C.struct_AVFormatContext)(s), (*C.struct_AVPacket)(pkt))) +func (s *Context) AvWriteFrame(pkt *avcodec.Packet) int { + return int(C.av_write_frame((*C.struct_AVFormatContext)(s), toCPacket(pkt))) } //Write a packet to an output media file ensuring correct interleaving. -func (s *Context) AvInterleavedWriteFrame(pkt *Packet) int { - return int(C.av_interleaved_write_frame((*C.struct_AVFormatContext)(s), (*C.struct_AVPacket)(pkt))) +func (s *Context) AvInterleavedWriteFrame(pkt *avcodec.Packet) int { + return int(C.av_interleaved_write_frame((*C.struct_AVFormatContext)(s), toCPacket(pkt))) } //Write a uncoded frame to an output media file. @@ -192,13 +191,13 @@ func (s *Context) AvDumpFormat(i int, url string, io int) { } //Guess the sample aspect ratio of a frame, based on both the stream and the frame aspect ratio. -func (s *Context) AvGuessSampleAspectRatio(st *Stream, fr *Frame) Rational { - return (Rational)(C.av_guess_sample_aspect_ratio((*C.struct_AVFormatContext)(s), (*C.struct_AVStream)(st), (*C.struct_AVFrame)(fr))) +func (s *Context) AvGuessSampleAspectRatio(st *Stream, fr *Frame) avcodec.Rational { + return newRational(C.av_guess_sample_aspect_ratio((*C.struct_AVFormatContext)(s), (*C.struct_AVStream)(st), (*C.struct_AVFrame)(fr))) } //Guess the frame rate, based on both the container and codec information. -func (s *Context) AvGuessFrameRate(st *Stream, fr *Frame) Rational { - return (Rational)(C.av_guess_frame_rate((*C.struct_AVFormatContext)(s), (*C.struct_AVStream)(st), (*C.struct_AVFrame)(fr))) +func (s *Context) AvGuessFrameRate(st *Stream, fr *Frame) avcodec.Rational { + return newRational(C.av_guess_frame_rate((*C.struct_AVFormatContext)(s), (*C.struct_AVStream)(st), (*C.struct_AVFrame)(fr))) } //Check if the stream st contained in s is matched by the stream specifier spec. @@ -212,7 +211,7 @@ func (s *Context) AvformatQueueAttachedPictures() int { func (s *Context) AvformatNewStream2(c *AvCodec) *Stream { stream := (*Stream)(C.avformat_new_stream((*C.struct_AVFormatContext)(s), (*C.struct_AVCodec)(c))) - stream.codec.pix_fmt = int32(common.AV_PIX_FMT_YUV) + stream.codec.pix_fmt = int32(avcodec.AV_PIX_FMT_YUV) stream.codec.width = 640 stream.codec.height = 480 stream.time_base.num = 1 diff --git a/avformat/context_struct.go b/avformat/context_struct.go index 78c6d4e..6ad373a 100644 --- a/avformat/context_struct.go +++ b/avformat/context_struct.go @@ -7,6 +7,7 @@ package avformat //#include import "C" import ( + "reflect" "unsafe" ) @@ -42,12 +43,24 @@ func (ctxt *Context) InterruptCallback() AvIOInterruptCB { return AvIOInterruptCB(ctxt.interrupt_callback) } -func (ctxt *Context) Programs() **AvProgram { - return (**AvProgram)(unsafe.Pointer(ctxt.programs)) +func (ctxt *Context) Programs() []*AvProgram { + header := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(ctxt.programs)), + Len: int(ctxt.NbPrograms()), + Cap: int(ctxt.NbPrograms()), + } + + return *((*[]*AvProgram)(unsafe.Pointer(&header))) } -func (ctxt *Context) Streams() *Stream { - return (*Stream)(unsafe.Pointer(*ctxt.streams)) +func (ctxt *Context) Streams() []*Stream { + header := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(ctxt.streams)), + Len: int(ctxt.NbStreams()), + Cap: int(ctxt.NbStreams()), + } + + return *((*[]*Stream)(unsafe.Pointer(&header))) } func (ctxt *Context) Filename() string { diff --git a/avformat/flags.go b/avformat/flags.go new file mode 100644 index 0000000..2bf377a --- /dev/null +++ b/avformat/flags.go @@ -0,0 +1,23 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +//Package avformat provides some generic global options, which can be set on all the muxers and demuxers. +//In addition each muxer or demuxer may support so-called private options, which are specific for that component. +//Supported formats (muxers and demuxers) provided by the libavformat library +package avformat + +//#cgo pkg-config: libavformat libavcodec libavutil libavdevice libavfilter libswresample libswscale +//#include +//#include +//#include +//#include +//#include +//#include +//#include +import "C" + +const ( + AVIO_FLAG_READ = int(C.AVIO_FLAG_READ) + AVIO_FLAG_WRITE = int(C.AVIO_FLAG_WRITE) + AVIO_FLAG_READ_WRITE = int(C.AVIO_FLAG_READ_WRITE) +) diff --git a/avformat/media_types.go b/avformat/media_types.go new file mode 100644 index 0000000..444c70b --- /dev/null +++ b/avformat/media_types.go @@ -0,0 +1,17 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. + +package avformat + +//#cgo pkg-config: libavutil +//#include +import "C" + +const ( + AVMEDIA_TYPE_UNKNOWN = C.AVMEDIA_TYPE_UNKNOWN + AVMEDIA_TYPE_VIDEO = C.AVMEDIA_TYPE_VIDEO + AVMEDIA_TYPE_AUDIO = C.AVMEDIA_TYPE_AUDIO + AVMEDIA_TYPE_DATA = C.AVMEDIA_TYPE_DATA + AVMEDIA_TYPE_SUBTITLE = C.AVMEDIA_TYPE_SUBTITLE + AVMEDIA_TYPE_ATTACHMENT = C.AVMEDIA_TYPE_ATTACHMENT + AVMEDIA_TYPE_NB = C.AVMEDIA_TYPE_NB +) diff --git a/avformat/packet.go b/avformat/packet.go new file mode 100644 index 0000000..4983a78 --- /dev/null +++ b/avformat/packet.go @@ -0,0 +1,21 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +package avformat + +//#cgo pkg-config: libavformat +//#include +import "C" +import ( + "unsafe" + + "github.com/giorgisio/goav/avcodec" +) + +func toCPacket(pkt *avcodec.Packet) *C.struct_AVPacket { + return (*C.struct_AVPacket)(unsafe.Pointer(pkt)) +} + +func fromCPacket(pkt *C.struct_AVPacket) *avcodec.Packet { + return (*avcodec.Packet)(unsafe.Pointer(pkt)) +} diff --git a/avformat/rational.go b/avformat/rational.go new file mode 100644 index 0000000..904fdd2 --- /dev/null +++ b/avformat/rational.go @@ -0,0 +1,13 @@ +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// Giorgis (habtom@giorgis.io) + +package avformat + +//#cgo pkg-config: libavutil +//#include +import "C" +import "github.com/giorgisio/goav/avcodec" + +func newRational(r C.struct_AVRational) avcodec.Rational { + return avcodec.NewRational(int(r.num), int(r.den)) +} diff --git a/avformat/stream.go b/avformat/stream.go index 69ea508..e5946f7 100644 --- a/avformat/stream.go +++ b/avformat/stream.go @@ -6,15 +6,23 @@ package avformat //#cgo pkg-config: libavformat //#include import "C" +import ( + "github.com/giorgisio/goav/avcodec" +) //Rational av_stream_get_r_frame_rate (const Stream *s) -func (s *Stream) AvStreamGetRFrameRate() Rational { - return (Rational)(C.av_stream_get_r_frame_rate((*C.struct_AVStream)(s))) +func (s *Stream) AvStreamGetRFrameRate() avcodec.Rational { + return newRational(C.av_stream_get_r_frame_rate((*C.struct_AVStream)(s))) } //void av_stream_set_r_frame_rate (Stream *s, Rational r) -func (s *Stream) AvStreamSetRFrameRate(r Rational) { - C.av_stream_set_r_frame_rate((*C.struct_AVStream)(s), (C.struct_AVRational)(r)) +func (s *Stream) AvStreamSetRFrameRate(r avcodec.Rational) { + rat := C.struct_AVRational{ + num: C.int(r.Num()), + den: C.int(r.Den()), + } + + C.av_stream_set_r_frame_rate((*C.struct_AVStream)(s), rat) } //struct CodecParserContext * av_stream_get_parser (const Stream *s) diff --git a/avformat/stream_struct.go b/avformat/stream_struct.go index d518330..d7947eb 100644 --- a/avformat/stream_struct.go +++ b/avformat/stream_struct.go @@ -8,8 +8,14 @@ package avformat import "C" import ( "unsafe" + + "github.com/giorgisio/goav/avcodec" ) +func (avs *Stream) CodecParameters() *avcodec.AvCodecParameters { + return (*avcodec.AvCodecParameters)(unsafe.Pointer(avs.codecpar)) +} + func (avs *Stream) Codec() *CodecContext { return (*CodecContext)(unsafe.Pointer(avs.codec)) } @@ -22,8 +28,8 @@ func (avs *Stream) IndexEntries() *AvIndexEntry { return (*AvIndexEntry)(unsafe.Pointer(avs.index_entries)) } -func (avs *Stream) AttachedPic() Packet { - return Packet(avs.attached_pic) +func (avs *Stream) AttachedPic() avcodec.Packet { + return *fromCPacket(&avs.attached_pic) } func (avs *Stream) SideData() *AvPacketSideData { @@ -34,24 +40,24 @@ func (avs *Stream) ProbeData() AvProbeData { return AvProbeData(avs.probe_data) } -func (avs *Stream) AvgFrameRate() Rational { - return Rational(avs.avg_frame_rate) +func (avs *Stream) AvgFrameRate() avcodec.Rational { + return newRational(avs.avg_frame_rate) } // func (avs *Stream) DisplayAspectRatio() *Rational { // return (*Rational)(unsafe.Pointer(avs.display_aspect_ratio)) // } -func (avs *Stream) RFrameRate() Rational { - return Rational(avs.r_frame_rate) +func (avs *Stream) RFrameRate() avcodec.Rational { + return newRational(avs.r_frame_rate) } -func (avs *Stream) SampleAspectRatio() Rational { - return Rational(avs.sample_aspect_ratio) +func (avs *Stream) SampleAspectRatio() avcodec.Rational { + return newRational(avs.sample_aspect_ratio) } -func (avs *Stream) TimeBase() Rational { - return Rational(avs.time_base) +func (avs *Stream) TimeBase() avcodec.Rational { + return newRational(avs.time_base) } // func (avs *Stream) RecommendedEncoderConfiguration() string { @@ -225,3 +231,7 @@ func (avs *Stream) PtsReorderErrorCount() uint8 { func (avs *Stream) IndexEntriesAllocatedSize() uint { return uint(avs.index_entries_allocated_size) } + +func (avs *Stream) Free() { + C.av_freep(unsafe.Pointer(avs)) +} diff --git a/avutil/avutil.go b/avutil/avutil.go index ca08a92..e915b46 100644 --- a/avutil/avutil.go +++ b/avutil/avutil.go @@ -22,6 +22,7 @@ type ( Rational C.struct_AVRational MediaType C.enum_AVMediaType AvPictureType C.enum_AVPictureType + PixelFormat C.enum_AVPixelFormat File C.FILE ) diff --git a/avutil/error.go b/avutil/error.go new file mode 100644 index 0000000..76f0cff --- /dev/null +++ b/avutil/error.go @@ -0,0 +1,28 @@ +// 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 is a utility library to aid portable multimedia programming. +// It contains safe portable string functions, random number generators, data structures, +// additional mathematics functions, cryptography and multimedia related functionality. +// Some generic features and utilities provided by the libavutil library +package avutil + +//#cgo pkg-config: libavutil +//#include +//#include +//static const char *error2string(int code) { return av_err2str(code); } +import "C" +import "errors" + +const ( + AvErrorEOF = -('E' | ('O' << 8) | ('F' << 16) | (' ' << 24)) + AvErrorEAGAIN = -35 +) + +func ErrorFromCode(code int) error { + if code >= 0 { + return nil + } + + return errors.New(C.GoString(C.error2string(C.int(code)))) +} diff --git a/avutil/frame.go b/avutil/frame.go index f83fc6c..30ff245 100644 --- a/avutil/frame.go +++ b/avutil/frame.go @@ -25,9 +25,9 @@ type ( AvFrameSideDataType C.enum_AVFrameSideDataType ) -// func AvprivFrameGetMetadatap(f *Frame) **Dictionary { -// return (**Dictionary)(unsafe.Pointer(C.avpriv_frame_get_metadatap((*C.struct_AVFrame)(unsafe.Pointer(f))))) -// } +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))) @@ -44,7 +44,7 @@ func AvFrameAlloc() *Frame { //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))) + C.av_frame_free((**C.struct_AVFrame)(unsafe.Pointer(&f))) } //Allocate new buffer(s) for audio or video data. diff --git a/common/types.go b/common/types.go deleted file mode 100644 index 037a42a..0000000 --- a/common/types.go +++ /dev/null @@ -1,19 +0,0 @@ -// Use of this source code is governed by a MIT license that can be found in the LICENSE file. - -package common - -// Common types used across all av packages. - -// AVRational exposes the numerator and denominator part of the undrelying 'Rational' structure. -type AVRational struct { - Num int - Den int -} - -type PixelFormat int - -const ( - AV_PIX_FMT_YUV PixelFormat = 0 - AV_PIX_FMT_RGB24 = 3 - AV_PIX_FMT_RGBA = 28 -) diff --git a/example/tutorial01.go b/example/tutorial01.go index 182f4d9..7e3d041 100644 --- a/example/tutorial01.go +++ b/example/tutorial01.go @@ -1,247 +1,207 @@ -//Example 01 package main +// tutorial01.c +// Code based on a tutorial at http://dranger.com/ffmpeg/tutorial01.html + +// A small sample program that shows how to use libavformat and libavcodec to +// read video from a file. +// +// Use +// +// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lswscale -lz +// +// to build (assuming libavformat and libavcodec are correctly installed +// your system). +// +// Run using +// +// tutorial01 myvideofile.mpg +// +// to write the first five frames from "myvideofile.mpg" to disk in PPM +// format. import ( "fmt" - "github.com/giorgisio/goav/avcodec" - "github.com/giorgisio/goav/avformat" - "github.com/giorgisio/goav/avutil" - "github.com/giorgisio/goav/swscale" "log" "os" "unsafe" + + "github.com/giorgisio/goav/swscale" + + "github.com/giorgisio/goav/avcodec" + "github.com/giorgisio/goav/avformat" + "github.com/giorgisio/goav/avutil" ) +// SaveFrame writes a single frame to disk as a PPM file +func SaveFrame(frame *avutil.Frame, width, height, frameNumber int) { + // Open file + fileName := fmt.Sprintf("frame%d.ppm", frameNumber) + file, err := os.Create(fileName) + if err != nil { + log.Println("Error Reading") + } + defer file.Close() + + // Write header + header := fmt.Sprintf("P6\n%d %d\n255\n", width, height) + file.Write([]byte(header)) + + // Write pixel data + for y := 0; y < height; y++ { + data0 := avutil.Data(frame)[0] + buf := make([]byte, width*3) + startPos := uintptr(unsafe.Pointer(data0)) + uintptr(y)*uintptr(avutil.Linesize(frame)[0]) + for i := 0; i < width*3; i++ { + element := *(*uint8)(unsafe.Pointer(startPos + uintptr(i))) + buf[i] = element + } + file.Write(buf) + } +} + func main() { - - filename := "sample.mp4" - - var ( - ctxtFormat *avformat.Context - ctxtSource *avcodec.Context - ctxtDest *avcodec.Context - videoCodec *avcodec.Codec - videoFrame *avutil.Frame - videoFrameRGB *avutil.Frame - packet *avcodec.Packet - ctxtSws *swscale.Context - videoStream int - frameFinished int - numBytes int - url string - ) - //media_type *avutil.MediaType - - // Register all formats and codecs - avformat.AvRegisterAll() + if len(os.Args) < 2 { + fmt.Println("Please provide a movie file") + os.Exit(1) + } // Open video file - if avformat.AvformatOpenInput(&ctxtFormat, filename, nil, nil) != 0 { - log.Println("Error: Couldn't open file.") - return + pFormatContext := avformat.AvformatAllocContext() + if avformat.AvformatOpenInput(&pFormatContext, os.Args[1], nil, nil) != 0 { + fmt.Printf("Unable to open file %s\n", os.Args[1]) + os.Exit(1) } // Retrieve stream information - if ctxtFormat.AvformatFindStreamInfo(nil) < 0 { - log.Println("Error: Couldn't find stream information.") - return + if pFormatContext.AvformatFindStreamInfo(nil) < 0 { + fmt.Println("Couldn't find stream information") + os.Exit(1) } // Dump information about file onto standard error - ctxtFormat.AvDumpFormat(0, url, 0) + pFormatContext.AvDumpFormat(0, os.Args[1], 0) // Find the first video stream - videoStream = -1 + for i := 0; i < int(pFormatContext.NbStreams()); i++ { + switch pFormatContext.Streams()[i].CodecParameters().AvCodecGetType() { + case avformat.AVMEDIA_TYPE_VIDEO: - //ctxtFormat->nb_streams - n := ctxtFormat.NbStreams() - - //ctxtFormat->streams[] - s := ctxtFormat.Streams() - //s2 := avformat.StreamsOne(ctxtFormat, 1) - - log.Print("Number of Streams:", n) - - for i := 0; i < int(n); i++ { - // ctxtFormat->streams[i]->codec->codec_type - log.Println("Stream Number:", i) - - //FIX: AvMEDIA_TYPE_VIDEO - if (*avformat.CodecContext)(s.Codec()) != nil { - videoStream = i - break - } - } - - if videoStream == -1 { - log.Println("Couldn't find a video stream") - return - } - - codec := s.Codec() - - // Get a pointer to the codec context for the video stream - //ctxtSource = ctxtFormat.streams[videoStream].codec - ctxtSource = (*avcodec.Context)(unsafe.Pointer(&codec)) - log.Println("Bit Rate:", ctxtSource.BitRate()) - log.Println("Channels:", ctxtSource.Channels()) - log.Println("Coded_height:", ctxtSource.CodedHeight()) - log.Println("Coded_width:", ctxtSource.CodedWidth()) - log.Println("Coder_type:", ctxtSource.CoderType()) - log.Println("Height:", ctxtSource.Height()) - log.Println("Profile:", ctxtSource.Profile()) - log.Println("Width:", ctxtSource.Width()) - log.Println("Codec ID:", ctxtSource.CodecId()) - - //C.enum_AVCodecID - codec_id := ctxtSource.CodecId() - - // Find the decoder for the video stream - videoCodec = avcodec.AvcodecFindDecoder(codec_id) - if videoCodec == nil { - log.Println("Error: Unsupported codec!") - return // Codec not found - } - - // Copy context - ctxtDest = videoCodec.AvcodecAllocContext3() - - if ctxtDest.AvcodecCopyContext(ctxtSource) != 0 { - log.Println("Error: Couldn't copy codec context") - return // Error copying codec context - } - - // Open codec - if ctxtDest.AvcodecOpen2(videoCodec, nil) < 0 { - return // Could not open codec - } - - // Allocate video frame - videoFrame = avutil.AvFrameAlloc() - - // Allocate an Frame structure - if videoFrameRGB = avutil.AvFrameAlloc(); videoFrameRGB == nil { - return - } - - //##TODO - var a swscale.PixelFormat - var b int - //avcodec.PixelFormat - //avcodec.PIX_FMT_RGB24 - //avcodec.SWS_BILINEAR - - w := ctxtDest.Width() - h := ctxtDest.Height() - pix_fmt := ctxtDest.PixFmt() - - // Determine required buffer size and allocate buffer - numBytes = avcodec.AvpictureGetSize((avcodec.PixelFormat)(a), w, h) - - buffer := avutil.AvMalloc(uintptr(numBytes)) - - // Assign appropriate parts of buffer to image planes in videoFrameRGB - // Note that videoFrameRGB is an Frame, but Frame is a superset - // of Picture - avp := (*avcodec.Picture)(unsafe.Pointer(videoFrameRGB)) - avp.AvpictureFill((*uint8)(buffer), (avcodec.PixelFormat)(a), w, h) - - // initialize SWS context for software scaling - ctxtSws = swscale.SwsGetcontext(w, - h, - (swscale.PixelFormat)(pix_fmt), - w, - h, - a, - b, - nil, - nil, - nil, - ) - - // Read frames and save first five frames to disk - i := 0 - - for ctxtFormat.AvReadFrame(packet) >= 0 { - // Is this a packet from the video stream? - s := packet.StreamIndex() - if s == videoStream { - // Decode video frame - ctxtDest.AvcodecDecodeVideo2((*avcodec.Frame)(unsafe.Pointer(videoFrame)), &frameFinished, packet) - - // Did we get a video frame? - if frameFinished > 0 { - // Convert the image from its native format to RGB - d := avutil.Data(videoFrame) - l := avutil.Linesize(videoFrame) - dr := avutil.Data(videoFrameRGB) - lr := avutil.Linesize(videoFrameRGB) - swscale.SwsScale(ctxtSws, - d, - l, - 0, - h, - dr, - lr, - ) - - // Save the frame to disk - if i <= 5 { - saveFrame(videoFrameRGB, w, h, i) - } - i++ + // Get a pointer to the codec context for the video stream + pCodecCtxOrig := pFormatContext.Streams()[i].Codec() + // Find the decoder for the video stream + pCodec := avcodec.AvcodecFindDecoder(avcodec.CodecId(pCodecCtxOrig.GetCodecId())) + if pCodec == nil { + fmt.Println("Unsupported codec!") + os.Exit(1) } + // Copy context + pCodecCtx := pCodec.AvcodecAllocContext3() + if pCodecCtx.AvcodecCopyContext((*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig))) != 0 { + fmt.Println("Couldn't copy codec context") + os.Exit(1) + } + + // Open codec + if pCodecCtx.AvcodecOpen2(pCodec, nil) < 0 { + fmt.Println("Could not open codec") + os.Exit(1) + } + + // Allocate video frame + pFrame := avutil.AvFrameAlloc() + + // Allocate an AVFrame structure + pFrameRGB := avutil.AvFrameAlloc() + if pFrameRGB == nil { + fmt.Println("Unable to allocate RGB Frame") + os.Exit(1) + } + + // Determine required buffer size and allocate buffer + numBytes := uintptr(avcodec.AvpictureGetSize(avcodec.AV_PIX_FMT_RGB24, pCodecCtx.Width(), + pCodecCtx.Height())) + buffer := avutil.AvMalloc(numBytes) + + // Assign appropriate parts of buffer to image planes in pFrameRGB + // Note that pFrameRGB is an AVFrame, but AVFrame is a superset + // of AVPicture + avp := (*avcodec.Picture)(unsafe.Pointer(pFrameRGB)) + avp.AvpictureFill((*uint8)(buffer), avcodec.AV_PIX_FMT_RGB24, pCodecCtx.Width(), pCodecCtx.Height()) + + // initialize SWS context for software scaling + swsCtx := swscale.SwsGetcontext( + pCodecCtx.Width(), + pCodecCtx.Height(), + (swscale.PixelFormat)(pCodecCtx.PixFmt()), + pCodecCtx.Width(), + pCodecCtx.Height(), + avcodec.AV_PIX_FMT_RGB24, + avcodec.SWS_BILINEAR, + nil, + nil, + nil, + ) + + // Read frames and save first five frames to disk + frameNumber := 1 + packet := avcodec.AvPacketAlloc() + for pFormatContext.AvReadFrame(packet) >= 0 { + // Is this a packet from the video stream? + if packet.StreamIndex() == i { + // Decode video frame + response := pCodecCtx.AvcodecSendPacket(packet) + if response < 0 { + fmt.Printf("Error while sending a packet to the decoder: %s\n", avutil.ErrorFromCode(response)) + } + for response >= 0 { + response = pCodecCtx.AvcodecReceiveFrame((*avcodec.Frame)(unsafe.Pointer(pFrame))) + if response == avutil.AvErrorEAGAIN || response == avutil.AvErrorEOF { + break + } else if response < 0 { + fmt.Printf("Error while receiving a frame from the decoder: %s\n", avutil.ErrorFromCode(response)) + return + } + + if frameNumber <= 5 { + // Convert the image from its native format to RGB + swscale.SwsScale2(swsCtx, avutil.Data(pFrame), + avutil.Linesize(pFrame), 0, pCodecCtx.Height(), + avutil.Data(pFrameRGB), avutil.Linesize(pFrameRGB)) + + // Save the frame to disk + fmt.Printf("Writing frame %d\n", frameNumber) + SaveFrame(pFrameRGB, pCodecCtx.Width(), pCodecCtx.Height(), frameNumber) + } else { + return + } + frameNumber++ + } + } + + // Free the packet that was allocated by av_read_frame + packet.AvFreePacket() + } + + // Free the RGB image + avutil.AvFree(buffer) + avutil.AvFrameFree(pFrameRGB) + + // Free the YUV frame + avutil.AvFrameFree(pFrame) + + // Close the codecs + pCodecCtx.AvcodecClose() + (*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig)).AvcodecClose() + + // Close the video file + pFormatContext.AvformatCloseInput() + + // Stop after saving frames of first video straem + break + + default: + fmt.Println("Didn't find a video stream") + os.Exit(1) } - - // Free the packet that was allocated by av_read_frame - packet.AvFreePacket() } - - // Free the RGB image - avutil.AvFree(buffer) - avutil.AvFrameFree(videoFrameRGB) - - // Free the YUV frame - avutil.AvFrameFree(videoFrame) - - // Close the codecs - ctxtDest.AvcodecClose() - ctxtSource.AvcodecClose() - - // Close the video file - ctxtFormat.AvformatCloseInput() - -} - -func saveFrame(videoFrame *avutil.Frame, width int, height int, iFrame int) { - - var szFilename string - var y int - var file *os.File - var err error - - szFilename = "" - - // Open file - szFilename = fmt.Sprintf("frame%d.ppm", iFrame) - - if file, err = os.Open(szFilename); err != nil { - log.Println("Error Reading") - } - - // Write header - fh := fmt.Sprintf("P6\n%d %d\n255\n", width, height) - log.Println(fh) - - // Write pixel data - for y = 0; y < height; y++ { - // d := avutil.Data(videoFrame) - // l := avutil.Linesize(videoFrame) - //##TODO - f := make([]byte, 100) - file.Write(f) - } - - file.Close() - } diff --git a/example/versions.go b/example/versions.go index 44366af..2f4d9d0 100644 --- a/example/versions.go +++ b/example/versions.go @@ -1,6 +1,8 @@ package main import ( + "log" + "github.com/giorgisio/goav/avcodec" "github.com/giorgisio/goav/avdevice" "github.com/giorgisio/goav/avfilter" @@ -8,7 +10,6 @@ import ( "github.com/giorgisio/goav/avutil" "github.com/giorgisio/goav/swresample" "github.com/giorgisio/goav/swscale" - "log" ) func main() { diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7f3b18f --- /dev/null +++ b/go.mod @@ -0,0 +1,6 @@ +module github.com/giorgisio/goav + +require ( + github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd // indirect + github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7936782 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd h1:1e+0Z+T4t1mKL5xxvxXh5FkjuiToQGKreCobLu7lR3Y= +github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8= +github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1 h1:4iPLwzjiWGBQnYdtKbg/JNlGlEEvklrrMdjypdA1LKQ= +github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=