From 2080b3f4d3c1230a935e99647a86dffef6965dac Mon Sep 17 00:00:00 2001 From: aggresss Date: Tue, 31 Oct 2023 09:16:44 +0800 Subject: [PATCH] 2023-10-31 09:16:44 CST W44D2 --- avcodec.go | 15 +- avutil_display.go | 6 + avutil_file.go | 1 + avutil_hwcontext_qsv.go | 15 ++ avutil_log.go | 2 +- avutil_samplefmt.go | 2 +- examples/qsvdec/main.go | 189 ++++++++++++++++++- examples/vaapi-transcode/main.go | 299 ++++++++++++++++++++++++++++++- 8 files changed, 520 insertions(+), 9 deletions(-) diff --git a/avcodec.go b/avcodec.go index 027468c..f14fcea 100644 --- a/avcodec.go +++ b/avcodec.go @@ -3472,6 +3472,11 @@ func (avctx *AVCodecContext) GetPropertiesAddr() *uint32 { return (*uint32)(&avctx.properties) } +const ( + FF_CODEC_PROPERTY_LOSSLESS = uint32(C.FF_CODEC_PROPERTY_LOSSLESS) + FF_CODEC_PROPERTY_CLOSED_CAPTIONS = uint32(C.FF_CODEC_PROPERTY_CLOSED_CAPTIONS) +) + // GetCodedSideData gets `AVCodecContext.coded_side_data` value. func (avctx *AVCodecContext) GetCodedSideData() *AVPacketSideData { return (*AVPacketSideData)(avctx.coded_side_data) @@ -3730,7 +3735,7 @@ func AvCodecGetLowres(avctx *AVCodecContext) int32 { // // AvCodecSetLowres func AvCodecSetLowres(avctx *AVCodecContext, i int32) { - C.av_codec_set_lowres((*C.struct_AVCodecContext)(avctx), C.int(i)) + C.av_codec_set_lowres((*C.struct_AVCodecContext)(avctx), (C.int)(i)) } // Deprecated: No use. @@ -3744,7 +3749,7 @@ func AvCodecGetSeekPreroll(avctx *AVCodecContext) int32 { // // AvCodecSetSeekPreroll func AvCodecSetSeekPreroll(avctx *AVCodecContext, i int32) { - C.av_codec_set_seek_preroll((*C.struct_AVCodecContext)(avctx), C.int(i)) + C.av_codec_set_seek_preroll((*C.struct_AVCodecContext)(avctx), (C.int)(i)) } // Deprecated: No use. @@ -4290,13 +4295,13 @@ func AvSubtitleFree(s *AVSubtitle) { // The default callback for AVCodecContext.get_buffer2(). func AvCodecDefaultGetBuffer2(avctx *AVCodecContext, frame *AVFrame, flags int32) int32 { return (int32)(C.avcodec_default_get_buffer2((*C.struct_AVCodecContext)(avctx), - (*C.struct_AVFrame)(frame), C.int(flags))) + (*C.struct_AVFrame)(frame), (C.int)(flags))) } // The default callback for AVCodecContext.get_encode_buffer(). func AvCodecDefaultGetEncodeBuffer(avctx *AVCodecContext, pkt *AVPacket, flags int32) int32 { return (int32)(C.avcodec_default_get_encode_buffer((*C.struct_AVCodecContext)(avctx), - (*C.struct_AVPacket)(pkt), C.int(flags))) + (*C.struct_AVPacket)(pkt), (C.int)(flags))) } // AvCodecAlignDimensions modifies width and height values so that they will result in a memory @@ -4321,7 +4326,7 @@ func AvCodecEnumToChromaPos(xpos, ypos *int32, pos AVChromaLocation) int32 { // AvCodecChromaPosToEnum converts swscale x/y chroma position to AVChromaLocation. func AvCodecChromaPosToEnum(xpos, ypos int32) AVChromaLocation { - return (AVChromaLocation)(C.avcodec_chroma_pos_to_enum(C.int(xpos), C.int(ypos))) + return (AVChromaLocation)(C.avcodec_chroma_pos_to_enum((C.int)(xpos), (C.int)(ypos))) } // Deprecated: Use AVCodecSendPacket() and AVCodecReceiveFrame(). diff --git a/avutil_display.go b/avutil_display.go index 747ef8b..6aa24bd 100644 --- a/avutil_display.go +++ b/avutil_display.go @@ -20,10 +20,16 @@ func AvDisplayRotationGet(matrix []int32) float64 { // AvDisplayRotationSet initializes a transformation matrix describing a pure counterclockwise // rotation by the specified angle (in degrees). func AvDisplayRotationSet(matrix []int32, angle float64) { + if len(matrix) < 9 { + panic("matrix len < 9") + } C.av_display_rotation_set((*C.int32_t)(&matrix[0]), (C.double)(angle)) } // AvDisplayMatrixFlip flips the input matrix horizontally and/or vertically. func AvDisplayMatrixFlip(matrix []int32, hflip, vflip int32) { + if len(matrix) < 9 { + panic("matrix len < 9") + } C.av_display_matrix_flip((*C.int32_t)(&matrix[0]), (C.int)(hflip), (C.int)(vflip)) } diff --git a/avutil_file.go b/avutil_file.go index 4cc0a9e..eab7206 100644 --- a/avutil_file.go +++ b/avutil_file.go @@ -5,6 +5,7 @@ package ffmpeg /* +#include #include */ import "C" diff --git a/avutil_hwcontext_qsv.go b/avutil_hwcontext_qsv.go index b2a7a5f..d36df32 100644 --- a/avutil_hwcontext_qsv.go +++ b/avutil_hwcontext_qsv.go @@ -16,3 +16,18 @@ type AVQSVDeviceContext C.struct_AVQSVDeviceContext // AVQSVFramesContext type AVQSVFramesContext C.struct_AVQSVFramesContext + +// GetFrameType gets `AVQSVFramesContext.frame_type` value. +func (ctx *AVQSVFramesContext) GetFrameType() int32 { + return (int32)(ctx.frame_type) +} + +// SetFrameType sets `AVQSVFramesContext.frame_type` value. +func (ctx *AVQSVFramesContext) SetFrameType(v int32) { + ctx.frame_type = (C.int)(v) +} + +// GetFrameTypeAddr gets `AVQSVFramesContext.frame_type` address. +func (ctx *AVQSVFramesContext) GetFrameTypeAddr() *int32 { + return (*int32)(&ctx.frame_type) +} diff --git a/avutil_log.go b/avutil_log.go index 4656243..9060a60 100644 --- a/avutil_log.go +++ b/avutil_log.go @@ -206,7 +206,7 @@ func AvLogGetLevel() int32 { // AvLogSetLevel sets the log level func AvLogSetLevel(level int32) { - C.av_log_set_level(C.int(level)) + C.av_log_set_level((C.int)(level)) } // typedef void (*av_log_callback_func)(void*, int, const char*, va_list); diff --git a/avutil_samplefmt.go b/avutil_samplefmt.go index 3ac53ad..60588be 100644 --- a/avutil_samplefmt.go +++ b/avutil_samplefmt.go @@ -49,7 +49,7 @@ func AvGetSampleFmt(name string) AVSampleFormat { // requested planar/packed format, the format returned is the same as the // input. func AvGetAltSampleFmt(sampleFmt AVSampleFormat, planar int32) AVSampleFormat { - return (AVSampleFormat)(C.av_get_alt_sample_fmt((C.enum_AVSampleFormat)(sampleFmt), C.int(planar))) + return (AVSampleFormat)(C.av_get_alt_sample_fmt((C.enum_AVSampleFormat)(sampleFmt), (C.int)(planar))) } // AvGetPackedSampleFmt gets the packed alternative form of the given sample format. diff --git a/examples/qsvdec/main.go b/examples/qsvdec/main.go index 7905807..47e99c0 100644 --- a/examples/qsvdec/main.go +++ b/examples/qsvdec/main.go @@ -1,5 +1,192 @@ +//go:build ffmpeg_hw_qsv + package main -func main() { +/* +#include +struct AVCodecContext; + +int get_format(struct AVCodecContext *avctx, int *pix_fmts) { + // TODO. + return -1; +} + +*/ +import "C" + +import ( + "fmt" + "os" + "syscall" + "unsafe" + + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +type decodeContext struct { + hwDeviceRef *ffmpeg.AVBufferRef +} + +func decodePacket(decode *decodeContext, decoderCtx *ffmpeg.AVCodecContext, + frame *ffmpeg.AVFrame, swFrame *ffmpeg.AVFrame, + pkt *ffmpeg.AVPacket, outputCtx *ffmpeg.AVIOContext) (ret int32) { + var ( + data [][]uint8 + ) + if ret = ffmpeg.AvCodecSendPacket(decoderCtx, pkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during decoding\n") + return ret + } + + for ret >= 0 { + ret = ffmpeg.AvCodecReceiveFrame(decoderCtx, frame) + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + break + } else if ret < 0 { + fmt.Fprintf(os.Stderr, "Error while decoding.\n") + return ret + } + + // A real program would do something useful with the decoded frame here. + // We just retrieve the raw data and write it to a file, which is rather + // useless but pedagogic. + if ret = ffmpeg.AvHWFrameTransferData(swFrame, frame, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Error transferring the data to system memory\n") + goto fail + } + + data = ffmpeg.SliceSlice(&swFrame.GetData()[0], len(swFrame.GetData()), + swFrame.GetWidth()*swFrame.GetHeight()) + for i := 0; i < len(swFrame.GetData()) && data[i] != nil; i++ { + for j := 0; j < int(swFrame.GetHeight()>>ffmpeg.CondExpr(i > 0, 1, 0)); j++ { + ffmpeg.AvIOWrite(outputCtx, &data[i][j*int(swFrame.GetLinesize()[i])], swFrame.GetWidth()) + } + } + fail: + ffmpeg.AvFrameUnref(swFrame) + ffmpeg.AvFrameUnref(frame) + + if ret < 0 { + return ret + } + } + + return 0 +} + +func main() { + var ( + intputCtx *ffmpeg.AVFormatContext + videoSt *ffmpeg.AVStream + decoderCtx *ffmpeg.AVCodecContext + decoder *ffmpeg.AVCodec + pkt ffmpeg.AVPacket + frame, swFrame *ffmpeg.AVFrame + decode = &decodeContext{} + outputCtx *ffmpeg.AVIOContext + ret int32 + ) + + if len(os.Args) < 3 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) + os.Exit(1) + } + + // open the input file + if ret = ffmpeg.AvFormatOpenInput(&intputCtx, os.Args[1], nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open input file '%s': ", os.Args[1]) + goto finish + } + + // find the first H.264 video stream + for i := 0; i < int(intputCtx.GetNbStreams()); i++ { + st := intputCtx.GetStreams()[i] + if st.GetCodecpar().GetCodecId() == ffmpeg.AV_CODEC_ID_H264 && videoSt == nil { + videoSt = st + } else { + st.SetDiscard(ffmpeg.AVDISCARD_ALL) + } + } + if videoSt == nil { + fmt.Fprintf(os.Stderr, "No H.264 video stream in the input file\n") + goto finish + } + + // open the hardware device + if ret = ffmpeg.AvHWDeviceCtxCreate(&decode.hwDeviceRef, ffmpeg.AV_HWDEVICE_TYPE_QSV, + "auto", nil, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open the hardware device\n") + goto finish + } + + // initialize the decoder + if decoder = ffmpeg.AvCodecFindDecoderByName("h264_qsv"); decoder == nil { + fmt.Fprintf(os.Stderr, "The QSV decoder is not present in libavcodec\n") + goto finish + } + + decoderCtx.SetCodecId(ffmpeg.AV_CODEC_ID_H264) + if videoSt.GetCodecpar().GetExtradataSize() != 0 { + decoderCtx.SetExtradata((*uint8)(ffmpeg.AvMallocz(videoSt.GetCodecpar().GetExtradataSize() + + ffmpeg.AV_INPUT_BUFFER_PADDING_SIZE))) + if decoderCtx.GetExtradata() == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto finish + } + copy(unsafe.Slice(decoderCtx.GetExtradata(), videoSt.GetCodecpar().GetExtradataSize()), + unsafe.Slice(videoSt.GetCodecpar().GetExtradata(), videoSt.GetCodecpar().GetExtradataSize())) + } + + decoderCtx.SetOpaque(decode.hwDeviceRef) + decoderCtx.SetGetFormat((ffmpeg.AVCodecContextGetFormatFunc)(C.get_format)) + + if ret = ffmpeg.AvCodecOpen2(decoderCtx, nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Error opening the decoder: ") + goto finish + } + + // open the output stream + if ret = ffmpeg.AvIOOpen(&outputCtx, os.Args[2], ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + fmt.Fprintf(os.Stderr, "Error opening the output context: ") + goto finish + } + + frame = ffmpeg.AvFrameAlloc() + swFrame = ffmpeg.AvFrameAlloc() + if frame == nil || swFrame == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto finish + } + + // actual decoding + for ret >= 0 { + if ret = ffmpeg.AvReadFrame(intputCtx, &pkt); ret < 0 { + break + } + + if pkt.GetStreamIndex() == videoSt.GetIndex() { + ret = decodePacket(decode, decoderCtx, frame, swFrame, &pkt, outputCtx) + } + + ffmpeg.AvPacketUnref(&pkt) + } + +finish: + if ret < 0 { + fmt.Fprintf(os.Stderr, "%s\n", ffmpeg.AvErr2str(ret)) + } + + ffmpeg.AvFormatCloseInput(&intputCtx) + + ffmpeg.AvFrameFree(&frame) + ffmpeg.AvFrameFree(&swFrame) + + ffmpeg.AvCodecFreeContext(&decoderCtx) + + ffmpeg.AvBufferUnref(&decode.hwDeviceRef) + + ffmpeg.AvIOClose(outputCtx) + + os.Exit(int(ret)) } diff --git a/examples/vaapi-transcode/main.go b/examples/vaapi-transcode/main.go index 7905807..3c036e4 100644 --- a/examples/vaapi-transcode/main.go +++ b/examples/vaapi-transcode/main.go @@ -1,5 +1,302 @@ package main -func main() { +/* +#include +#include +struct AVCodecContext; + +static int av_pix_fmt_none; +static int av_pix_fmt_vaapi; + +static inline void initial_pix_fmt(int x, int y) +{ + av_pix_fmt_none = x; + av_pix_fmt_vaapi = y; +} + +int get_vaapi_format(struct AVCodecContext *ctx, int *pix_fmts) +{ + const int *p; + + for (p = pix_fmts; *p != av_pix_fmt_none; p++) { + if (*p == av_pix_fmt_vaapi) + return *p; + } + + fprintf(stderr, "Unable to decode this file using VA-API.\n"); + return av_pix_fmt_none; +} + +*/ +import "C" + +import ( + "fmt" + "os" + "syscall" + + ffmpeg "github.com/qrtc/ffmpeg-dev-go" +) + +type streamContext struct { + ifmtCtx *ffmpeg.AVFormatContext + ofmtCtx *ffmpeg.AVFormatContext + + hwDeviceCtx *ffmpeg.AVBufferRef + + decoderCtx *ffmpeg.AVCodecContext + encoderCtx *ffmpeg.AVCodecContext + + videoStream int32 + ost *ffmpeg.AVStream + initialized bool +} + +func openInputFile(filename string, sc *streamContext) (ret int32) { + var ( + decoder *ffmpeg.AVCodec + video *ffmpeg.AVStream + ) + + if ret = ffmpeg.AvFormatOpenInput(&sc.ifmtCtx, filename, nil, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open input file '%s', Error code: %s\n", + filename, ffmpeg.AvErr2str(ret)) + return ret + } + + if ret = ffmpeg.AvFormatFindStreamInfo(sc.ifmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot find input stream information. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + + if ret = ffmpeg.AvFindBestStream(sc.ifmtCtx, ffmpeg.AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot find a video stream in the input file."+ + " Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + sc.videoStream = ret + + if sc.decoderCtx = ffmpeg.AvCodecAllocContext3(decoder); sc.decoderCtx == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + + video = sc.ifmtCtx.GetStreams()[sc.videoStream] + if ret = ffmpeg.AvCodecParametersToContext(sc.decoderCtx, video.GetCodecpar()); ret < 0 { + fmt.Fprintf(os.Stderr, "avcodec_parameters_to_context error. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + + sc.decoderCtx.SetHwDeviceCtx(ffmpeg.AvBufferRef(sc.hwDeviceCtx)) + if sc.decoderCtx.GetHwDeviceCtx() == nil { + fmt.Fprintf(os.Stderr, "A hardware device reference create failed.\n") + return ffmpeg.AVERROR(syscall.ENOMEM) + } + C.initial_pix_fmt((C.int)(ffmpeg.AV_PIX_FMT_NONE), (C.int)(ffmpeg.AV_PIX_FMT_VAAPI)) + sc.decoderCtx.SetGetFormat((ffmpeg.AVCodecContextGetFormatFunc)(C.get_vaapi_format)) + + if ret = ffmpeg.AvCodecOpen2(sc.decoderCtx, decoder, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to open codec for decoding. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + } + + return 0 +} + +func encodeWrite(encPkt *ffmpeg.AVPacket, frame *ffmpeg.AVFrame, sc *streamContext) (ret int32) { + + ffmpeg.AvPacketUnref(encPkt) + + if ret = ffmpeg.AvCodecSendFrame(sc.encoderCtx, frame); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during encoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto end + } + for { + if ret = ffmpeg.AvCodecReceivePacket(sc.encoderCtx, encPkt); ret != 0 { + break + } + + encPkt.SetStreamIndex(0) + ffmpeg.AvPacketRescaleTs(encPkt, sc.ifmtCtx.GetStreams()[sc.videoStream].GetTimeBase(), + sc.ofmtCtx.GetStreams()[0].GetTimeBase()) + if ret = ffmpeg.AvInterleavedWriteFrame(sc.ofmtCtx, encPkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during writing data to output file. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return -1 + } + } +end: + if ret == ffmpeg.AVERROR_EOF { + return 0 + } + return ffmpeg.CondExpr(ret == ffmpeg.AVERROR(syscall.EAGAIN), 0, int32(-1)) +} + +func initialEncCodecCtx(encCodec *ffmpeg.AVCodec, sc *streamContext) (ret int32) { + + // we need to ref hw_frames_ctx of decoder to initialize encoder's codec. + // Only after we get a decoded frame, can we obtain its hw_frames_ctx + sc.encoderCtx.SetHwFramesCtx(ffmpeg.AvBufferRef(sc.decoderCtx.GetHwFramesCtx())) + if sc.encoderCtx.GetHwFramesCtx() == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + // set AVCodecContext Parameters for encoder, here we keep them stay + // the same as decoder. + // xxx: now the sample can't handle resolution change case. + sc.encoderCtx.SetTimeBase(ffmpeg.AvInvQ(sc.decoderCtx.GetFramerate())) + sc.encoderCtx.SetPixFmt(ffmpeg.AV_PIX_FMT_VAAPI) + sc.encoderCtx.SetWidth(sc.decoderCtx.GetWidth()) + sc.encoderCtx.SetHeight(sc.decoderCtx.GetHeight()) + + if ret = ffmpeg.AvCodecOpen2(sc.encoderCtx, encCodec, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to open encode codec. Error code: %s\n", + ffmpeg.AvErr2str(ret)) + return ret + } + sc.ost.SetTimeBase(sc.encoderCtx.GetTimeBase()) + if ret = ffmpeg.AvCodecParametersFromContext(sc.ost.GetCodecpar(), sc.encoderCtx); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to copy the stream parameters. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + // write the stream header + if ret = ffmpeg.AvFormatWriteHeader(sc.ofmtCtx, nil); ret < 0 { + fmt.Fprintf(os.Stderr, "Error while writing stream header. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + return 0 +} + +func decEnc(pkt *ffmpeg.AVPacket, encCodec *ffmpeg.AVCodec, sc *streamContext) (ret int32) { + var ( + frame *ffmpeg.AVFrame + ) + + if ret = ffmpeg.AvCodecSendPacket(sc.decoderCtx, pkt); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during decoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + return ret + } + + for ret >= 0 { + if frame = ffmpeg.AvFrameAlloc(); frame == nil { + return ffmpeg.AVERROR(syscall.ENOMEM) + } + + ret = ffmpeg.AvCodecReceiveFrame(sc.decoderCtx, frame) + if ret == ffmpeg.AVERROR(syscall.EAGAIN) || ret == ffmpeg.AVERROR_EOF { + ffmpeg.AvFrameFree(&frame) + } else if ret < 0 { + fmt.Fprintf(os.Stderr, "Error while decoding. Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto fail + } + + if !sc.initialized { + if ret = initialEncCodecCtx(encCodec, sc); ret != 0 { + fmt.Fprintf(os.Stderr, "Initial EncodeCtx failed.\n") + goto fail + } + sc.initialized = true + } + + if ret = encodeWrite(pkt, frame, sc); ret < 0 { + fmt.Fprintf(os.Stderr, "Error during encoding and writing.\n") + } + fail: + ffmpeg.AvFrameFree(&frame) + if ret < 0 { + return ret + } + } + return 0 +} + +func main() { + var ( + ret int32 + decPkt *ffmpeg.AVPacket + encCodec *ffmpeg.AVCodec + sc = &streamContext{} + ) + + if len(os.Args) != 4 { + fmt.Fprintf(os.Stderr, "Usage: %s \n"+ + "The output format is guessed according to the file extension.\n"+ + "\n", os.Args[0]) + os.Exit(1) + } + + if ret = ffmpeg.AvHWDeviceCtxCreate(&sc.hwDeviceCtx, ffmpeg.AV_HWDEVICE_TYPE_VAAPI, + ffmpeg.NIL, nil, 0); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to create a VAAPI device. Error code: %s\n", ffmpeg.AvErr2str(ret)) + os.Exit(1) + } + + if decPkt = ffmpeg.AvPacketAlloc(); decPkt == nil { + fmt.Fprintf(os.Stderr, "Failed to allocate decode packet\n") + goto end + } + + if ret = openInputFile(os.Args[1], sc); ret < 0 { + goto end + } + + if encCodec = ffmpeg.AvCodecFindEncoderByName(os.Args[2]); encCodec == nil { + fmt.Fprintf(os.Stderr, "Could not find encoder '%s'\n", os.Args[2]) + ret = -1 + goto end + } + + if ret = ffmpeg.AvFormatAllocOutputContext2(&sc.ofmtCtx, nil, ffmpeg.NIL, os.Args[3]); ret < 0 { + fmt.Fprintf(os.Stderr, "Failed to deduce output format from file extension. Error code: "+ + "%s\n", ffmpeg.AvErr2str(ret)) + goto end + } + + if sc.encoderCtx = ffmpeg.AvCodecAllocContext3(encCodec); sc.encoderCtx == nil { + ret = ffmpeg.AVERROR(syscall.ENOMEM) + goto end + } + + if ret = ffmpeg.AvIOOpen(sc.ofmtCtx.GetPbAddr(), os.Args[3], ffmpeg.AVIO_FLAG_WRITE); ret < 0 { + fmt.Fprintf(os.Stderr, "Cannot open output file. "+ + "Error code: %s\n", ffmpeg.AvErr2str(ret)) + goto end + } + + // read all packets and only transcoding video + for ret >= 0 { + if ret = ffmpeg.AvReadFrame(sc.ifmtCtx, decPkt); ret < 0 { + break + } + + if sc.videoStream == decPkt.GetStreamIndex() { + ret = decEnc(decPkt, encCodec, sc) + } + + ffmpeg.AvPacketUnref(decPkt) + } + + // flush decoder + ffmpeg.AvPacketUnref(decPkt) + decEnc(decPkt, encCodec, sc) + + // flush encoder + encodeWrite(decPkt, nil, sc) + + // write the trailer for output stream + ffmpeg.AvWriteTrailer(sc.ofmtCtx) + +end: + ffmpeg.AvFormatCloseInput(&sc.ifmtCtx) + ffmpeg.AvFormatCloseInput(&sc.ofmtCtx) + ffmpeg.AvCodecFreeContext(&sc.decoderCtx) + ffmpeg.AvCodecFreeContext(&sc.encoderCtx) + ffmpeg.AvBufferUnref(&sc.hwDeviceCtx) + ffmpeg.AvPacketFree(&decPkt) + os.Exit(int(ret)) }