SkeyePlayer RTSP Windows端(下文简称:SkeyePlayer)播放器之前抓图代码次要通过OpenCV来实现,且数据格式转换的效率过于低下;故而在过后的代码中采纳线程机制来解决抓图导致视频播放时卡顿的问题;而最新版的SkeyePlayer为了精简代码也为了进步抓图效率,咱们采纳ffmpeg进行抓图,为了保障视频播放的流畅性,线程机制咱们依然保留。
采纳ffmpeg进行抓图代码如下
// 抓图函数实现int take_snapshot(char *file, int w, int h, uint8_t *buffer, AVPixelFormat Format){ char *fileext = NULL; enum AVCodecID codecid = AV_CODEC_ID_NONE; struct SwsContext *sws_ctx = NULL; AVPixelFormat swsofmt = AV_PIX_FMT_NONE; AVFrame picture = {}; int ret = -1; AVFormatContext *fmt_ctxt = NULL; AVOutputFormat *out_fmt = NULL; AVStream *stream = NULL; AVCodecContext *codec_ctxt = NULL; AVCodec *codec = NULL; AVPacket packet = {}; int retry = 8; int got = 0; // init ffmpeg av_register_all(); fileext = file + strlen(file) - 3; if (_stricmp(fileext, "png") == 0) { codecid = AV_CODEC_ID_APNG; swsofmt = AV_PIX_FMT_RGB24; } else { codecid = AV_CODEC_ID_MJPEG; swsofmt = AV_PIX_FMT_YUVJ420P; } AVFrame video; int numBytesIn; numBytesIn = av_image_get_buffer_size(Format, w, h, 1); av_image_fill_arrays(video.data, video.linesize, buffer, Format, w, h, 1); video.width = w; video.height = h; video.format = Format; // alloc picture picture.format = swsofmt; picture.width = w > 0 ? w : video.width; picture.height = h > 0 ? h : video.height; int numBytes = av_image_get_buffer_size(swsofmt, picture.width, picture.height , 1); buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t)); av_image_fill_arrays(picture.data, picture.linesize, buffer, swsofmt, picture.width, picture.height, 1); // scale picture sws_ctx = sws_getContext(video.width, video.height, (AVPixelFormat)Format/*video->format*/, picture.width, picture.height, swsofmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); if (!sws_ctx) { //av_log(NULL, AV_LOG_ERROR, "could not initialize the conversion context jpg\n"); goto done; } sws_scale(sws_ctx, video.data, video.linesize, 0, video.height, picture.data, picture.linesize); // do encoding fmt_ctxt = avformat_alloc_context(); out_fmt = av_guess_format(codecid == AV_CODEC_ID_APNG ? "apng" : "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 = picture.width; codec_ctxt->height = picture.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, &picture, &got) < 0) { //av_log(NULL, AV_LOG_ERROR, "failed to do picture 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); } } // ok ret = 0;done: avcodec_close(codec_ctxt); if (fmt_ctxt) { avio_close(fmt_ctxt->pb); } avformat_free_context(fmt_ctxt); av_packet_unref(&packet); sws_freeContext(sws_ctx); av_free(buffer); return ret;}借助ffmpeg弱小的视频解决和转换性能,咱们能够将一帧图像转换成任意格局的图片,当然如代码所示咱们只选择性地反对了“jpeg”和“png”两种格局的图片格式;采纳ffmpeg抓图的步骤分两步:
...