178 lines
5.4 KiB
C++
178 lines
5.4 KiB
C++
#include "screenshot.h"
|
|
#include "easylogging++.h"
|
|
AVFrame *allocate_sws_frame(AVCodecContext *enc_ctx)
|
|
{
|
|
int ret = 0;
|
|
AVFrame *sws_frame = av_frame_alloc();
|
|
if(sws_frame)
|
|
{
|
|
sws_frame->format = enc_ctx->pix_fmt;
|
|
sws_frame->width = enc_ctx->width;
|
|
sws_frame->height = enc_ctx->height;
|
|
sws_frame->pict_type = AV_PICTURE_TYPE_NONE;
|
|
ret = av_frame_get_buffer(sws_frame, 32); // 分配buffer
|
|
if(ret <0)
|
|
{
|
|
av_frame_free(&sws_frame);
|
|
return NULL;
|
|
}
|
|
}
|
|
return sws_frame;
|
|
}
|
|
|
|
ScreenShot::ScreenShot()
|
|
{
|
|
|
|
}
|
|
|
|
int ScreenShot::SaveJpeg(AVFrame *src_frame, const char *file_name, int jpeg_quality)
|
|
{
|
|
AVFormatContext* ofmt_ctx = NULL;
|
|
AVOutputFormat* fmt = NULL;
|
|
AVStream* video_st = NULL;
|
|
AVCodecContext* enc_ctx = NULL;
|
|
AVCodec* codec = NULL;
|
|
AVFrame* picture = NULL;
|
|
AVPacket *pkt = NULL;
|
|
int got_picture = 0;
|
|
int ret = 0;
|
|
struct SwsContext *img_convert_ctx = NULL;
|
|
|
|
ofmt_ctx = avformat_alloc_context();
|
|
//Guess format
|
|
fmt = av_guess_format("mjpeg", NULL, NULL);
|
|
ofmt_ctx->oformat = fmt;
|
|
//Output URL
|
|
if (avio_open(&ofmt_ctx->pb, file_name, AVIO_FLAG_READ_WRITE) < 0){
|
|
LOG(ERROR) <<"Couldn't open output file.";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
|
|
video_st = avformat_new_stream(ofmt_ctx, 0);
|
|
if (video_st==NULL){
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
enc_ctx = video_st->codec;
|
|
enc_ctx->codec_id = AV_CODEC_ID_MJPEG; // mjpeg支持的编码器
|
|
enc_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
enc_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; // AV_CODEC_ID_MJPEG 支持的像素格式
|
|
|
|
enc_ctx->width = src_frame->width;
|
|
enc_ctx->height = src_frame->height;
|
|
|
|
enc_ctx->time_base.num = 1;
|
|
enc_ctx->time_base.den = 25;
|
|
//Output some information
|
|
av_dump_format(ofmt_ctx, 0, file_name, 1);
|
|
|
|
codec = avcodec_find_encoder(enc_ctx->codec_id);
|
|
if (!codec){
|
|
LOG(ERROR) << "jpeg Codec not found.";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
if (avcodec_open2(enc_ctx, codec,NULL) < 0){
|
|
LOG(ERROR) << "Could not open jpeg codec.";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
ret = avcodec_parameters_from_context(video_st->codecpar, enc_ctx);
|
|
if(ret < 0) {
|
|
LOG(ERROR) << "avcodec_parameters_from_context failed";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
if(src_frame->format != enc_ctx->pix_fmt) {
|
|
img_convert_ctx = sws_getContext(enc_ctx->width, enc_ctx->height,
|
|
(enum AVPixelFormat)src_frame->format, enc_ctx->width, enc_ctx->height,
|
|
enc_ctx->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
|
|
if (!img_convert_ctx) {
|
|
LOG(ERROR) << "Impossible to create scale context for the conversion fmt:"
|
|
<< av_get_pix_fmt_name((enum AVPixelFormat)src_frame->format)
|
|
<< ", s:" << enc_ctx->width << "x" << enc_ctx->height << " -> fmt:" << av_get_pix_fmt_name(enc_ctx->pix_fmt)
|
|
<< ", s:" << enc_ctx->width << "x" << enc_ctx->height ;
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(jpeg_quality > 0)
|
|
{
|
|
if(jpeg_quality > 100)
|
|
jpeg_quality = 100;
|
|
|
|
enc_ctx->qcompress = (float)jpeg_quality/100.f; // 0~1.0, default is 0.5
|
|
enc_ctx->qmin = 2;
|
|
enc_ctx->qmax = 31;
|
|
enc_ctx->max_qdiff = 3;
|
|
|
|
LOG(ERROR) <<"JPEG quality is: %d" << jpeg_quality;
|
|
}
|
|
pkt = av_packet_alloc();
|
|
//Write Header
|
|
ret = avformat_write_header(ofmt_ctx, NULL);
|
|
if(ret < 0) {
|
|
LOG(ERROR) <<"avformat_write_header failed";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
|
|
if(img_convert_ctx) // 如果需要转换pix_fmt
|
|
{
|
|
// 分配转换后的frame
|
|
picture = allocate_sws_frame(enc_ctx);
|
|
/* make sure the frame data is writable */
|
|
ret = av_frame_make_writable(picture);
|
|
ret = sws_scale(img_convert_ctx, (const uint8_t **) src_frame->data, src_frame->linesize, 0, src_frame->height,
|
|
picture->data, picture->linesize);
|
|
picture->pts = 0;
|
|
ret = avcodec_encode_video2(enc_ctx, pkt, picture, &got_picture);
|
|
}
|
|
else
|
|
{
|
|
ret = avcodec_encode_video2(enc_ctx, pkt, src_frame, &got_picture);
|
|
}
|
|
|
|
if(ret < 0){
|
|
LOG(ERROR) <<"avcodec_encode_video2 Error.";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
if (got_picture==1){
|
|
pkt->stream_index = video_st->index;
|
|
ret = av_write_frame(ofmt_ctx, pkt);
|
|
if(ret < 0) {
|
|
LOG(ERROR) <<"av_write_frame Error.";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
}else {
|
|
LOG(ERROR) <<"no got_picture";
|
|
ret = -1;
|
|
goto fail;
|
|
}
|
|
ret = 0;
|
|
fail:
|
|
//Write Trailer
|
|
ret = av_write_trailer(ofmt_ctx);
|
|
if(ret < 0)
|
|
LOG(ERROR) <<"av_write_trailer Error.";
|
|
if(pkt)
|
|
av_packet_free(&pkt);
|
|
if (enc_ctx)
|
|
avcodec_close(enc_ctx);
|
|
if(picture)
|
|
av_frame_free(&picture);
|
|
if(ofmt_ctx && ofmt_ctx->pb)
|
|
avio_close(ofmt_ctx->pb);
|
|
if(ofmt_ctx)
|
|
avformat_free_context(ofmt_ctx);
|
|
if(img_convert_ctx)
|
|
sws_freeContext(img_convert_ctx);
|
|
|
|
return ret;
|
|
}
|
|
|