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