FFmpeg学习之overlay实时水印

FFmpeg可以使用overlay滤镜添加水印,这次使用overlay和movie滤镜实现实时水印。

movie滤镜可以做为独立于标准输入的源,参数filename可以接收视频、图片、设备和流

input -----------> deltapts0 --> overlay --> output
                                    ^
                                    |
movie --> scale--> deltapts1 -------+
初始化滤镜
- (void)initFilters {
    AVFilterGraph * filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        printf("Failed to create filter graph!\n");
        return;
    }
    
    AVRational time_base = formatCtx->streams[videoIndex]->time_base;
    
    //source filter
    char args[512];
    snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, time_base.num, time_base.den, pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);
    
    const AVFilter * bufferSrc = avfilter_get_by_name("buffer");
    int ret = avfilter_graph_create_filter(&bufferSrc_ctx, bufferSrc, "in", args, NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create buffer source filter\n");
        return;
    }

    //sink filter
    AVBufferSinkParams * bufferSink_params;
    const AVFilter * bufferSink = avfilter_get_by_name("buffersink");
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
    bufferSink_params = av_buffersink_params_alloc();
    bufferSink_params->pixel_fmts = pix_fmts;
    ret = avfilter_graph_create_filter(&bufferSink_ctx, bufferSink, "out", NULL, bufferSink_params, filter_graph);
    if (ret < 0) {
        printf("Fail to create buffer sink filter\n");
        return;
    }
    
    //overlay filter
    const AVFilter * overlayFilter = avfilter_get_by_name("overlay");
    ret = avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, "x", "100", NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create overlay filter\n");
        return;
    }
    
    const AVFilter * movieFilter = avfilter_get_by_name("movie");
    NSString * pngPath = [[NSBundle mainBundle] pathForResource:@"3" ofType:@"png"];
    ret = avfilter_graph_create_filter(&movieFilter_ctx, movieFilter, "filename ", [pngPath UTF8String], NULL, filter_graph);
    //这里在iOS平台使用PNG图片可能会出现 inflate returned error -3的错误,需要在Build Settings里把Compress PNG Files和Remove Text Metadata From PNG Files都设置为NO
    if (ret < 0) {
        printf("Fail to create movie filter\n");
        return;
    }
    
    ret = avfilter_link(bufferSrc_ctx, 0, overlayFilter_ctx, 0);
    if (ret < 0) {
        printf("Fail to link src to overlay");
        return;
    }

    ret = avfilter_link(movieFilter_ctx, 0, overlayFilter_ctx, 1);
    if (ret < 0) {
        printf("Fail to link movie to overlay");
        return;
    }
    
    ret = avfilter_link(overlayFilter_ctx, 0, bufferSink_ctx, 0);
    if (ret < 0) {
        printf("Fail to link overlay to sink");
        return;
    }
    
    ret = avfilter_graph_config(filter_graph, NULL);
    if (ret < 0) {
        printf("Fail in filter graph\n");
        return;
    }
}
初始化滤镜并连接配置后,配合av_buffersrc_add_frame_flags和av_buffersink_get_frame就能得到带有水印的视频,如果要实时动态的改变水印的位置要在解码的时候设置
- (void)decode {
  ....
  while(av_read_frame(format_ctx, packet) >= 0) {
    if (packet->stream_index == videoIndex) {
      ...
      ret = avfilter_graph_send_command(filter_graph, "overlay", "x", "10", NULL, 0, 0);
      //这里将水印的位置由上面的距左边的距离100改到了10,可以根据需求在解码过程中动态的改变,更多overlay支持的参数可以去FFmpeg官网查看,经测试movie滤镜并不支持这样动态改变输入源
      ret = avcodec_send_packet(codec_ctx, packet);
      AVFrame * frame = av_frame_alloc();
      ret = avcodec_receive_frame(codec_ctx, frame);
      ret = av_buffersrc_add_frame_flags(bufferSrc_ctx, frame, 0);
      AVFrame * overlay_frame = av_frame_alloc();
      ret = av_buffersink_get_frame(bufferSink_ctx, overlay_frame);
      ...
    }
  }      
}

你可能感兴趣的:(FFmpeg学习之overlay实时水印)