16 - FFmpeg 视频过滤器 方式2

代码等效密令【测试用】

/**ffmpeg -i 9.5.flv -vf "split[main][tmp];[tmp]crop=iw:ih/2:0:0,vflip[flip];[main][flip]overlay=0:H/2" -b:v 500k -vcodec libx264 9.5 out.flv
 * -vf:视频滤镜选项,后面跟着的字符串指定了要应用的多个滤镜。
 * 视频滤镜部分 (-vf "...")
 *     split[main][tmp]:这个滤镜将输入视频流分成两个相同的流,分别标记为 [main] 和 [tmp]。
 *     [tmp]crop=iw:ih/2:0:0:在 [tmp] 流上应用 crop 滤镜,将其裁剪为原始宽度 (iw) 和高度的一半 (ih/2),左上角的起始点为 (0,0)。这样将只保留视频的上半部分。
 *     vflip:在裁剪后的流上应用 vflip 滤镜,表示垂直翻转该流。
 *     [flip]:标记翻转后的流为 [flip]。
 *     [main][flip]overlay=0:H/2:将 [main] 流(未裁剪的原始视频)和 [flip] 流(翻转后的一半视频)叠加。
 *            这里 overlay 的参数 0:H/2 表示将 [flip] 流放置在原始视频的下半部分,0 是 x 轴的偏移量,H/2 是 y 轴的偏移量,表示将翻转后的流叠加在原始流的下方。
 * 编码及输出部分
 *     -b:v 500k:设置输出视频的比特率为 500 kbps。这影响了输出视频的质量和文件大小。
 *     -vcodec libx264:指定使用 libx264 编解码器来编码视频流,这是一种广泛使用的 H.264 视频编解码库。
 *     9.5 out.flv:指定输出文件名为 out.flv。
 **/

int InitFilters(const int width, const int height, const int format,
                AVFilterGraph **FilterGraph, AVFilterContext **ResultSinkCtx /*输出*/, AVFilterContext **MainSrcCtx /*输入*/)
{
    AVFilterInOut *inputs = NULL;
    AVFilterInOut *outputs = NULL;
    *FilterGraph = avfilter_graph_alloc();
    if ((*FilterGraph) == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avfilter graph alloc *FilterGraph error -- line:%d\n", __FUNCTION__, __LINE__);
        return -1;
    }

    char args[1024] = {0};

#ifdef OUTS
    snprintf(args, sizeof(args),
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d[v0];" // Parsed_buffer_0
             "[v0]split[main][tmp];"                                                      // Parsed_split_1
             "[tmp]crop=iw:ih/2:0:0,vflip[flip];"                                         // Parsed_crop_2 -> Parsed_vflip_3
             "[main]buffersink;"                                                          // Parsed_overlay_4
             "[flip]buffersink",                                                          // Parsed_buffersink_5
             width, height, format, 1, 25, 1, 1);
#else
    snprintf(args, sizeof(args),
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d[v0];" // Parsed_buffer_0
             "[v0]split[main][tmp];"                                                      // Parsed_split_1
             "[tmp]crop=iw:ih/2:0:0,vflip[flip];"                                         // Parsed_crop_2 -> Parsed_vflip_3
             "[main][flip]overlay=0:H/2[result];"                                         // Parsed_overlay_4
             "[result]buffersink",                                                        // Parsed_buffersink_5
             width, height, format, 1, 25, 1, 1);
#endif
    av_log(NULL, AV_LOG_INFO, "[%s] args:%s -- line:%d\n", __FUNCTION__, args, __LINE__);
    // 将由字符串描述的图形添加到图形中。 -- 内部组织 Filter
    int ret = avfilter_graph_parse2(*FilterGraph, args, &inputs, &outputs);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avfilter graph parse error:%s -- line:%d\n", __FUNCTION__, av_err2str(ret), __LINE__);
        return ret;
    }
    // 检查有效性并配置图中的所有链接和格式。 -- 提交过滤器
    ret = avfilter_graph_config(*FilterGraph, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avfilter graph config error:%s -- line:%d\n", __FUNCTION__, av_err2str(ret), __LINE__);
        return ret;
    }

    // 从AVFilterGraph解析字符串中获取AVFilterContext
    *MainSrcCtx = avfilter_graph_get_filter(*FilterGraph, "Parsed_buffer_0");
    if ((*MainSrcCtx) == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avfilter graph get filter Buffer_0 failed -- line:%d\n", __FUNCTION__, __LINE__);
        return -1;
    }
    *ResultSinkCtx = avfilter_graph_get_filter(*FilterGraph, "Parsed_buffersink_5");
    if ((*ResultSinkCtx) == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avfilter graph get filter Parsed_buffersink_5 failed -- line:%d\n", __FUNCTION__, __LINE__);
        return -1;
    }

    av_log(NULL, AV_LOG_WARNING, "[%s] SinkWidth:%d, SinkHeight:%d, SinkFormat:%d, -- line:%d\n", __FUNCTION__, av_buffersink_get_w(*ResultSinkCtx), av_buffersink_get_h(*ResultSinkCtx), av_buffersink_get_format(*ResultSinkCtx), __LINE__);

    char *GraphString = avfilter_graph_dump(*FilterGraph, NULL);
    if (GraphString != NULL)
    {
        FILE *GraphFile = fopen("Graph.txt", "w"); // 打印 filterfraph 的 具体情况
        if (GraphFile != NULL)
        {
            fwrite(GraphString, 1, strlen(GraphString), GraphFile);
            fclose(GraphFile);
        }
    }
    av_free(GraphString);
    return 0;
}

int Filter_2(const char *inFileName, const char *outFileName)
{
    FILE *IN_FILE = fopen(inFileName, "rb+");
    FILE *OUT_FILE = fopen(outFileName, "wb");
    if (IN_FILE == NULL || OUT_FILE == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] open file error! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }

    uint16_t InWidth = 1920;
    uint16_t InHeight = 1080;

    // FilterGraph - 对filters系统的整体管理
    AVFilterGraph *FilterGraph;
    AVFilterContext *ResultSinkCtx = NULL;
    AVFilterContext *MainSrcCtx = NULL;
    int ret = InitFilters(InWidth, InHeight, AV_PIX_FMT_YUV420P, &FilterGraph, &ResultSinkCtx, &MainSrcCtx);
    if (ret < 0 || (FilterGraph == NULL) || (ResultSinkCtx == NULL) || (MainSrcCtx == NULL))
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] InitFilters error! -- line:%d\n", __FUNCTION__, __LINE__);
        return -1;
    }

    AVFrame *InFrame = av_frame_alloc();
    uint8_t *InFrameBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, InWidth, InHeight, 1));
    av_image_fill_arrays(InFrame->data, InFrame->linesize, InFrameBuffer, AV_PIX_FMT_YUV420P, InWidth, InHeight, 1);

    AVFrame *OutFrame = av_frame_alloc();
    uint8_t *OutFrameBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, InWidth, InHeight, 1));
    av_image_fill_arrays(OutFrame->data, OutFrame->linesize, OutFrameBuffer, AV_PIX_FMT_YUV420P, InWidth, InHeight, 1);

    InFrame->width = InWidth;
    InFrame->height = InHeight;
    InFrame->format = AV_PIX_FMT_YUV420P;

    uint32_t FrameCount = 0;
    size_t ReadSize = 0;
    size_t PictureSize = InWidth * InHeight * 3 / 2;

    do
    {
        ReadSize = fread(InFrameBuffer, 1, PictureSize, IN_FILE); /*一次读取一张图*/
        // input Y,U.V
        InFrame->data[0] = InFrameBuffer;
        InFrame->data[1] = InFrameBuffer + InWidth * InHeight;
        InFrame->data[2] = InFrameBuffer + InWidth * InHeight * 5 / 4;

        ret = av_buffersrc_add_frame(MainSrcCtx, InFrame);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "[%s] Error while add frame -- line:%d\n", __FUNCTION__, __LINE__);
            goto _end;
        }

        // pull filtered pictrues from the filtergraph
        ret = av_buffersink_get_frame(ResultSinkCtx, OutFrame);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "[%s] Error while get frame -- line:%d\n", __FUNCTION__, __LINE__);
            goto _end;
        }

        // output Y,U,V
        if (OutFrame->format == AV_PIX_FMT_YUV420P) // YYYY..U..V..
        {
            for (int i = 0; i < OutFrame->height; i++)
                fwrite(OutFrame->data[0] /*Y*/ + OutFrame->linesize[0] * i, 1, OutFrame->width, OUT_FILE);
            for (int i = 0; i < OutFrame->height / 2; i++)
                fwrite(OutFrame->data[1] /*U*/ + OutFrame->linesize[1] * i, 1, OutFrame->width / 2, OUT_FILE);
            for (int i = 0; i < OutFrame->height / 2; i++)
                fwrite(OutFrame->data[2] /*V*/ + OutFrame->linesize[2] * i, 1, OutFrame->width / 2, OUT_FILE);
        }
        if (++FrameCount % 25 == 0)
            av_log(NULL, AV_LOG_INFO, "[%s] %d frames have been processed -- line:%d\n", __FUNCTION__, FrameCount, __LINE__);
        av_frame_unref(OutFrame);
    } while (ReadSize >= PictureSize);

_end:
    if (IN_FILE != NULL)
    {
        fclose(IN_FILE);
    }
    if (OUT_FILE != NULL)
    {
        fclose(OUT_FILE);
    }
    if (InFrame != NULL)
    {
        av_frame_free(&InFrame);
    }
    if (OutFrame != NULL)
    {
        av_frame_free(&OutFrame);
    }
    if (FilterGraph != NULL)
    {
        avfilter_graph_free(&FilterGraph); // 内部去释放 AVFilterContext
    }
}

你可能感兴趣的:(FFmpeg,ffmpeg,音视频)