前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SkeyeExPlayer(Windows)开发系列之采用ffmpeg进行截图

SkeyeExPlayer(Windows)开发系列之采用ffmpeg进行截图

原创
作者头像
Openskeye
发布2023-04-14 11:05:14
2510
发布2023-04-14 11:05:14
举报

下面我们讲解下SkeyeExPlayer的截图功能,截图原理就是将YUV/RGB原始数据压缩成jpg或者png等格式(当然bmp格式是不需要压缩的),然后存储成文件的过程;我们以jpg格式为例进行讲解;一般情况下可以使用libjpeg库进行jpeg格式压缩,在不使用libjpeg的情况下,可以使用ffmpeg(内部也集成了libjpeg库)提供的接口进行压缩并写文件,这个方式也普遍适用于写MP4或者其他文件,下面我们讲解下ffmpeg进行截图的流程。

1.数据源转换

代码语言:txt
复制
    // init ffmpeg
    av_register_all();

    fileext = file + strlen(file) - 3;

    codecid = AV_CODEC_ID_MJPEG;
    swsofmt = AV_PIX_FMT_YUVJ420P;

    // alloc frame
    frame.format = swsofmt;
    frame.width  = w > 0 ? w : video->width;
    frame.height = h > 0 ? h : video->height;
    if (av_frame_get_buffer(&frame, 32) < 0) {
        av_log(NULL, AV_LOG_ERROR, "failed to allocate frame !\n", file);
        goto done;
    }

    // scale frame
    sws_ctx = sws_getContext(video->width, video->height, (AVPixelFormat)Format/*video->format*/,
        frame.width, frame.height, swsofmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    if (!sws_ctx) {
        av_log(NULL, AV_LOG_ERROR, "could not initialize the conversion context jpg\n");
        goto done;
    }
    sws_scale(sws_ctx, video->data, video->linesize, 0, video->height, frame.data, frame.linesize);

这段代码很简单,首先初始化ffmpeg,然后进行格式转换,注意,为了格式统一,这里统一将格式转换成YUV420,以便进行编码,指定输入格式为YUV420,输出格式为JPEG;如果要编码为PNG,这里指定格式为PNG即可:

代码语言:txt
复制
 if (_stricmp(fileext, "png") == 0) 
 {
      codecid = AV_CODEC_ID_APNG;
      swsofmt = AV_PIX_FMT_RGB24;
 }

2.数据源编码

代码语言:txt
复制
    // jpeg encoding
    fmt_ctxt = avformat_alloc_context();
    out_fmt  = av_guess_format("mjpeg", NULL, NULL);
    fmt_ctxt->oformat = out_fmt;
    if (!out_fmt) {
        av_log(NULL, AV_LOG_ERROR, "failed to guess format !\n");
        goto done;
    }

    if (avio_open(&fmt_ctxt->pb, file, AVIO_FLAG_READ_WRITE) < 0) {
        av_log(NULL, AV_LOG_ERROR, "failed to open output file: %s !\n", file);
        goto done;
    }

    stream = avformat_new_stream(fmt_ctxt, 0);
    if (!stream) {
        av_log(NULL, AV_LOG_ERROR, "failed to create a new stream !\n");
        goto done;
    }
    codec_ctxt                = stream->codec;
    codec_ctxt->codec_id      = out_fmt->video_codec;
    codec_ctxt->codec_type    = AVMEDIA_TYPE_VIDEO;
    codec_ctxt->pix_fmt       = swsofmt;
    codec_ctxt->width         = frame.width;
    codec_ctxt->height        = frame.height;
    codec_ctxt->time_base.num = 1;
    codec_ctxt->time_base.den = 25;

    codec = avcodec_find_encoder(codec_ctxt->codec_id);
    if (!codec) {
        av_log(NULL, AV_LOG_ERROR, "failed to find encoder !\n");
        goto done;
    }

    if (avcodec_open2(codec_ctxt, codec, NULL) < 0) {
        av_log(NULL, AV_LOG_ERROR, "failed to open encoder !\n");
        goto done;
    }
    while (retry-- && !got) 
    {
        if (avcodec_encode_video2(codec_ctxt, &packet, &frame, &got) < 0) {
            av_log(NULL, AV_LOG_ERROR, "failed to do frame encoding !\n");
            goto done;
        }

        if (got) {
            ret = avformat_write_header(fmt_ctxt, NULL);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "error occurred when opening output file !\n");
                goto done;
            }
            av_write_frame(fmt_ctxt, &packet);
            av_write_trailer(fmt_ctxt);
        }
    }

avcodec_find_encoder找到一个编码器,指定一个格式为JPEG进行编码,avcodec_encode_video2函数进行编码。

3.jpeg编码数据写文件

从2中代码段可以看出,通过avcodec_open2打开一个文件,通过avformat_new_stream新建一个输出流,avformat_write_header

写入文件头,av_write_frame写入一帧数据,av_write_trailer写入文件尾,存入一帧数据成jpeg结束。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档