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;
 | 
						|
}
 | 
						|
 |