#include "VideoTools.h" #include "logger.hpp" extern "C" { #include #include #include #include #include #include #include #include } namespace VideoTools { FFImgInfo* snapshot(const string& uri){ if (uri.empty()){ return nullptr; } AVFormatContext* ifmt_ctx = nullptr; AVCodecContext* codec_ctx = nullptr; AVCodec* codec = nullptr; AVPacket* pkt = nullptr; AVFrame *frame = nullptr; AVFrame *pFrameRGB = nullptr; int video_index = -1; AVStream* st = nullptr; SwsContext *img_convert_ctx = nullptr; uint8_t *buffer = nullptr; int numBytes = 0; int index = 0; FFImgInfo* imgInfo = nullptr; //av_register_all(); avformat_network_init(); // 参数设置 AVDictionary *options = nullptr; av_dict_set( &options, "bufsize", "655360", 0 ); av_dict_set( &options, "rtsp_transport", "tcp", 0 ); av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒 ///打开输入的流 int ret = avformat_open_input(&ifmt_ctx, uri.c_str(), nullptr, &options); if (ret != 0){ printf("Couldn't open input stream.\n"); goto end_flag ; } //查找流信息 if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0){ printf("Couldn't find stream information.\n"); goto end_flag ; } //找到视频流索引 video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); st = ifmt_ctx->streams[video_index]; //找到解码器 codec = avcodec_find_decoder(st->codecpar->codec_id); if (!codec){ fprintf(stderr, "Codec not found\n"); goto end_flag ; } //申请AVCodecContext codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx){ goto end_flag ; } avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[video_index]->codecpar); //打开解码器 if ((ret = avcodec_open2(codec_ctx, codec, nullptr) < 0)){ goto end_flag ; } // 计算解码后原始数据所需缓冲区大小,并分配内存空间 Determine required buffer size and allocate buffer numBytes = av_image_get_buffer_size(AV_PIX_FMT_BGR24, codec_ctx->width, codec_ctx->height, 1); buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t)); pFrameRGB = av_frame_alloc(); av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_BGR24, codec_ctx->width, codec_ctx->height, 1); img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height,codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, nullptr, nullptr, nullptr); pkt = av_packet_alloc(); frame = av_frame_alloc(); while (av_read_frame(ifmt_ctx, pkt) >= 0){ if (pkt->stream_index == video_index){ int ret = avcodec_send_packet(codec_ctx, pkt); if (ret >= 0){ ret = avcodec_receive_frame(codec_ctx, frame); if ((ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) || ret < 0){ LOG_ERROR("Failed to receive frame: {}",ret); continue; } index ++ ; if (index >= 5){ // 取解码出来的第三帧,应该可以一定程度优化花屏问题 sws_scale(img_convert_ctx, (const unsigned char* const*)frame->data, frame->linesize, 0, codec_ctx->height, pFrameRGB->data, pFrameRGB->linesize); imgInfo = new FFImgInfo(); imgInfo->pData = buffer; imgInfo->height = codec_ctx->height; imgInfo->width = codec_ctx->width; break; } } } av_packet_unref(pkt); } end_flag: if (codec_ctx != nullptr){ avcodec_close(codec_ctx); avcodec_free_context(&codec_ctx); } if (ifmt_ctx != nullptr){ avformat_close_input(&ifmt_ctx); } if (frame != nullptr){ av_frame_free(&frame); } if (pFrameRGB != nullptr){ av_frame_free(&pFrameRGB); } if (pkt != nullptr){ av_packet_free(&pkt); } return imgInfo; } void releaseFFImgInfo(FFImgInfo* info) { if(nullptr != info){ if(info->pData != nullptr){ av_free(info->pData); info->pData = nullptr; } delete info; info = nullptr; } } } // namespace