Rotating a video during encoding with ffmpeg and libav API results in half of video corrupted

喜欢而已 提交于 2020-05-17 05:50:12

问题


I'm using the C API for ffmpeg/libav to rotate a vertically filmed iphone video during the encoding step. There are other questions asking to do a similar thing but they are all using the CLI tool to do so.

So far I was able to figure out how to use the AVFilter to rotate the video, base off this example https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/filtering_video.c

The problem is that half the output file is corrupt.

Here is the code for my encoding logic. Its written with GOLANG using CGO to interface with the C API.

// Encode encode an AVFrame and return it
func Encode(enc Encoder, frame *C.AVFrame) (*EncodedFrame, error) {
    ctx := enc.Context()

    if ctx.buffersrcctx == nil {
        // initialize filter
        outputs := C.avfilter_inout_alloc()
        inputs  := C.avfilter_inout_alloc()
        m_pFilterGraph := C.avfilter_graph_alloc()
        buffersrc := C.avfilter_get_by_name(C.CString("buffer"))
        argsStr := fmt.Sprintf("video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", ctx.avctx.width, ctx.avctx.height, ctx.avctx.pix_fmt, ctx.avctx.time_base.num, ctx.avctx.time_base.den, ctx.avctx.sample_aspect_ratio.num, ctx.avctx.sample_aspect_ratio.den)
        Log.Info.Println("yakotest")
        Log.Info.Println(argsStr)
        args := C.CString(argsStr)
        ret := C.avfilter_graph_create_filter(&ctx.buffersrcctx, buffersrc, C.CString("my_buffersrc"), args, nil, m_pFilterGraph)
        if ret < 0 {
            Log.Info.Printf("\n problem creating filter %v\n", AVError(ret).Error())
        }

        buffersink := C.avfilter_get_by_name(C.CString("buffersink"))
        ret = C.avfilter_graph_create_filter(&ctx.buffersinkctx, buffersink, C.CString("my_buffersink"), nil, nil, m_pFilterGraph)
        if ret < 0 {
            Log.Info.Printf("\n problem creating filter %v\n", AVError(ret).Error())
        }

        /*
         * Set the endpoints for the filter graph. The filter_graph will
         * be linked to the graph described by filters_descr.
         */

        /*
         * The buffer source output must be connected to the input pad of
         * the first filter described by filters_descr; since the first
         * filter input label is not specified, it is set to "in" by
         * default.
         */
        outputs.name       = C.av_strdup(C.CString("in"))
        outputs.filter_ctx = ctx.buffersrcctx
        outputs.pad_idx    = 0
        outputs.next       = nil

        /*
         * The buffer sink input must be connected to the output pad of
         * the last filter described by filters_descr; since the last
         * filter output label is not specified, it is set to "out" by
         * default.
         */
        inputs.name       = C.av_strdup(C.CString("out"))
        inputs.filter_ctx = ctx.buffersinkctx
        inputs.pad_idx    = 0
        inputs.next       = nil

        ret = C.avfilter_graph_parse_ptr(m_pFilterGraph, C.CString("transpose=clock,scale=-2:1080"),
            &inputs, &outputs, nil)
        if ret < 0 {
            Log.Info.Printf("\n problem with avfilter_graph_parse %v\n", AVError(ret).Error())
        }

        ret = C.avfilter_graph_config(m_pFilterGraph, nil)
        if ret < 0 {
            Log.Info.Printf("\n problem with graph config %v\n", AVError(ret).Error())
        }
    }

    filteredFrame :=  C.av_frame_alloc()

    /* push the decoded frame into the filtergraph */
    ret := C.av_buffersrc_add_frame_flags(ctx.buffersrcctx, frame, C.AV_BUFFERSRC_FLAG_KEEP_REF)
    if ret < 0 {
        Log.Error.Printf("\nError while feeding the filter greaph, err = %v\n", AVError(ret).Error())
        return nil, errors.New(ErrorFFmpegCodecFailure)
    }

    /* pull filtered frames from the filtergraph */
    for {
        ret = C.av_buffersink_get_frame(ctx.buffersinkctx, filteredFrame)
        if ret == C.AVERROR_EAGAIN || ret == C.AVERROR_EOF {
            break
        }
        if ret < 0 {
            Log.Error.Printf("\nCouldnt find a frame, err = %v\n", AVError(ret).Error())
            return nil, errors.New(ErrorFFmpegCodecFailure)
        }

        filteredFrame.pts = frame.pts
        frame = filteredFrame
        defer C.av_frame_free(&filteredFrame)
    }

    if frame != nil {
        frame.pict_type = 0 // reset pict type for the encoder
        if C.avcodec_send_frame(ctx.avctx, frame) != 0 {
            Log.Error.Printf("%+v\n", StackErrorf("codec error, could not send frame"))
            return nil, errors.New(ErrorFFmpegCodecFailure)
        }
    }

    for {
        ret := C.avcodec_receive_packet(ctx.avctx, ctx.pkt)
        if ret == C.AVERROR_EAGAIN {
            break
        }
        if ret == C.AVERROR_EOF {
            return nil, fmt.Errorf("EOF")
        }
        if ret < 0 {
            Log.Error.Printf("%+v\n", StackErrorf("codec error, receiving packet"))
            return nil, errors.New(ErrorFFmpegCodecFailure)
        }

        data := C.GoBytes(unsafe.Pointer(ctx.pkt.data), ctx.pkt.size)
        return &EncodedFrame{data, int64(ctx.pkt.pts), int64(ctx.pkt.dts),
            (ctx.pkt.flags & C.AV_PKT_FLAG_KEY) != 0}, nil
    }

    return nil, nil
}

It seems like I need to do something with the scaling here but I'm struggling to find helpful information online.

来源:https://stackoverflow.com/questions/61719959/rotating-a-video-during-encoding-with-ffmpeg-and-libav-api-results-in-half-of-vi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!