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

2023-04-14 11:05:14 浏览数 (1)

下面我们讲解下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 jpgn");
        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结束。

0 人点赞