ffmpeg实现接收RTSP流并抓拍生存图片的功能

打印 上一主题 下一主题

主题 887|帖子 887|积分 2661

rtsp_capture.h
  1. #ifndef RTSP_CAPTURE_H
  2. #define RTSP_CAPTURE_H
  3. #pragma once
  4. #include <string>
  5. #include <thread>
  6. #include <atomic>
  7. extern "C" {
  8. #include <libavcodec/avcodec.h>
  9. #include <libavformat/avformat.h>
  10. #include <libavutil/imgutils.h>
  11. #include <libswscale/swscale.h>
  12. }
  13. class RtspCapture {
  14. public:
  15.   RtspCapture(const std::string& url);
  16.   ~RtspCapture();
  17.   void take(uint32_t width, uint32_t height);
  18. private:
  19.   bool initFFmpeg();
  20.   void cleanup();
  21.   bool saveFrame(const std::string& filename, AVFrame* frame, uint32_t width, uint32_t height);
  22. private:
  23.   std::string m_url;
  24.   std::string m_outputDir;
  25.   AVFormatContext* m_formatContext;
  26.   AVCodecContext* m_codecContext;
  27.   SwsContext* m_swsContext;
  28.   int m_videoStreamIndex;
  29.   std::string m_rtspUrl;
  30. };
  31. #endif // RTSPCAPTURE_H
复制代码
rtsp_capture.cpp
  1. #include "rtsp_capture.h"
  2. #include <chrono>
  3. #include <iostream>
  4. RtspCapture::RtspCapture(const std::string& url)
  5.     : m_url(url)
  6.       , m_formatContext(nullptr)
  7.       , m_codecContext(nullptr)
  8.       , m_swsContext(nullptr)
  9.       , m_videoStreamIndex(-1)
  10. {
  11.   initFFmpeg();
  12. }
  13. RtspCapture::~RtspCapture() {
  14.   cleanup();
  15. }
  16. bool RtspCapture::initFFmpeg() {
  17. #if LIBAVFORMAT_VERSION_MAJOR < 58
  18.   av_register_all(); // 对于旧版本,显式调用以确保所有编解码器和格式被注册
  19. #endif
  20.   avformat_network_init();
  21.   // 设置RTSP超时选项
  22.   AVDictionary* options = nullptr;
  23.   // av_dict_set(&options, "rtsp_transport", "tcp", 0);
  24.   av_dict_set(&options, "stimeout", "30000000", 0);
  25.   av_dict_set(&options, "max_delay", "500000", 0);  // 设置最大延迟
  26.   av_dict_set(&options, "buffer_size", "1024000", 0);  // 设置缓冲区大小
  27.   // av_log_set_level(AV_LOG_DEBUG);
  28.   // 打开RTSP流
  29.   m_formatContext = avformat_alloc_context();
  30.   if (avformat_open_input(&m_formatContext, m_url.c_str(), nullptr, &options) != 0) {
  31.     std::cerr << "无法打开RTSP流:" << m_url << std::endl;
  32.     return false;
  33.   }
  34.   std::cout << "打开RTSP流成功:" << m_url << std::endl;
  35.   // 获取流信息
  36.   if (avformat_find_stream_info(m_formatContext, nullptr) < 0) {
  37.     std::cerr << "无法获取流信息" << std::endl;
  38.     return false;
  39.   }
  40.   // 查找视频流
  41.   m_videoStreamIndex = -1;
  42.   for (unsigned int i = 0; i < m_formatContext->nb_streams; i++) {
  43.     if (m_formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
  44.       m_videoStreamIndex = i;
  45.       break;
  46.     }
  47.   }
  48.   if (m_videoStreamIndex == -1) {
  49.     std::cerr << "找不到视频流" << std::endl;
  50.     return false;
  51.   }
  52.   // 获取解码器
  53.   const AVCodec* codec = avcodec_find_decoder(m_formatContext->streams[m_videoStreamIndex]->codecpar->codec_id);
  54.   if (!codec) {
  55.     std::cerr << "找不到解码器" << std::endl;
  56.     return false;
  57.   }
  58.   // 创建解码器上下文
  59.   m_codecContext = avcodec_alloc_context3(codec);
  60.   avcodec_parameters_to_context(m_codecContext, m_formatContext->streams[m_videoStreamIndex]->codecpar);
  61.   // 打开解码器
  62.   if (avcodec_open2(m_codecContext, codec, nullptr) < 0) {
  63.     std::cerr << "无法打开解码器" << std::endl;
  64.     return false;
  65.   }
  66.   std::cout << "FFmpeg初始化成功!" << std::endl;
  67.   return true;
  68. }
  69. static uint64_t GetCurTick()
  70. {
  71.   auto now = std::chrono::system_clock::now();
  72.   auto duration = now.time_since_epoch();
  73.   return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
  74. }
  75. void RtspCapture::take(uint32_t width, uint32_t height) {
  76.   AVPacket* packet = av_packet_alloc();
  77.   AVFrame* frame = av_frame_alloc();
  78.   if (av_read_frame(m_formatContext, packet) >= 0) {
  79.     if (packet->stream_index == m_videoStreamIndex) {
  80.       // 解码视频帧
  81.       if (avcodec_send_packet(m_codecContext, packet) >= 0) {
  82.         if (avcodec_receive_frame(m_codecContext, frame) >= 0) {
  83.           // 生成文件名
  84.           std::string filename = "img.jpg";
  85.           // std::string filename = "snapshot_" +
  86.           //                        std::to_string(GetCurTick()) + ".jpg";
  87.           // 保存帧为JPEG
  88.           saveFrame(filename, frame, width, height);
  89.         }
  90.       }
  91.     }
  92.     av_packet_unref(packet);
  93.   }
  94.   av_frame_free(&frame);
  95.   av_packet_free(&packet);
  96. }
  97. bool RtspCapture::saveFrame(const std::string& filename,  AVFrame* frame, uint32_t width, uint32_t height) {
  98.   // 获取JPEG编码器
  99.   const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
  100.   if (!jpegCodec) {
  101.     std::cerr << "无法找到JPEG编码器" << std::endl;
  102.     return false;
  103.   }
  104.   // 创建编码器上下文
  105.   AVCodecContext* jpegContext = avcodec_alloc_context3(jpegCodec);
  106.   if (!jpegContext) {
  107.     std::cerr << "无法创建JPEG编码器上下文" << std::endl;
  108.     return false;
  109.   }
  110.   // 设置编码器参数
  111.   jpegContext->width = width;
  112.   jpegContext->height = height;
  113.   jpegContext->time_base = (AVRational){1, 1};
  114.   jpegContext->pix_fmt = AV_PIX_FMT_YUVJ420P;  // JPEG使用的颜色空间
  115.   jpegContext->codec_type = AVMEDIA_TYPE_VIDEO;
  116.   jpegContext->codec_id = AV_CODEC_ID_MJPEG;
  117.   // 打开编码器
  118.   if (avcodec_open2(jpegContext, jpegCodec, nullptr) < 0) {
  119.     std::cerr << "无法打开JPEG编码器" << std::endl;
  120.     avcodec_free_context(&jpegContext);
  121.     return false;
  122.   }
  123.   // 分配转换后的帧
  124.   AVFrame* yuvFrame = av_frame_alloc();
  125.   yuvFrame->format = AV_PIX_FMT_YUVJ420P;
  126.   yuvFrame->width = m_codecContext->width;
  127.   yuvFrame->height = m_codecContext->height;
  128.   av_frame_get_buffer(yuvFrame, 0);
  129.   // 创建缩放上下文
  130.   SwsContext* swsContext = sws_getContext(
  131.       m_codecContext->width, m_codecContext->height, m_codecContext->pix_fmt,
  132.       m_codecContext->width, m_codecContext->height, AV_PIX_FMT_YUVJ420P,
  133.       SWS_BILINEAR, nullptr, nullptr, nullptr
  134.       );
  135.   if (!swsContext) {
  136.     std::cerr << "无法创建转换上下文" << std::endl;
  137.     av_frame_free(&yuvFrame);
  138.     avcodec_free_context(&jpegContext);
  139.     return false;
  140.   }
  141.   // 转换颜色空间
  142.   sws_scale(swsContext, frame->data, frame->linesize,
  143.             0, m_codecContext->height, yuvFrame->data, yuvFrame->linesize);
  144.   // 编码为JPEG
  145.   AVPacket* jpegPacket = av_packet_alloc();
  146.   int ret = avcodec_send_frame(jpegContext, yuvFrame);
  147.   if (ret < 0) {
  148.     std::cerr << "发送帧到编码器失败" << std::endl;
  149.     av_packet_free(&jpegPacket);
  150.     av_frame_free(&yuvFrame);
  151.     avcodec_free_context(&jpegContext);
  152.     sws_freeContext(swsContext);
  153.     return false;
  154.   }
  155.   ret = avcodec_receive_packet(jpegContext, jpegPacket);
  156.   if (ret < 0) {
  157.     std::cerr << "从编码器接收数据失败" << std::endl;
  158.     av_packet_free(&jpegPacket);
  159.     av_frame_free(&yuvFrame);
  160.     avcodec_free_context(&jpegContext);
  161.     sws_freeContext(swsContext);
  162.     return false;
  163.   }
  164.   // 写入文件
  165.   FILE* outFile = fopen(filename.c_str(), "wb");
  166.   if (outFile) {
  167.     fwrite(jpegPacket->data, 1, jpegPacket->size, outFile);
  168.     fclose(outFile);
  169.     // std::cout << "已保存图片: " << filename << std::endl;
  170.   } else {
  171.     std::cerr << "无法创建输出文件" << std::endl;
  172.   }
  173.   return true;
  174. }
  175. void RtspCapture::cleanup() {
  176.   if (m_codecContext) {
  177.     avcodec_free_context(&m_codecContext);
  178.   }
  179.   if (m_formatContext) {
  180.     avformat_close_input(&m_formatContext);
  181.     avformat_free_context(m_formatContext);
  182.   }
  183. }
复制代码
main.cpp
  1. #include "rtsp_capture.h"
  2. int main()
  3. {
  4.     std::unique_ptr<RtspCapture> rtsp_capture_ptr_;
  5.     std::string url = "rtsp://admin:passwd@127.0.0.1:554/live1";
  6.     rtsp_capture_ptr_ = std::make_unique<RtspCapture>(url);
  7.     rtsp_capture_ptr_->take(640, 480);
  8.     getchar();
  9.     return 0;
  10. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表