简介

  • ffmpeg C开发 常用技巧

ffmpeg AVPacket 初始化和反初始化

  • av_init_packet()函数被弃用了
  • 应该使用 av_packet_alloc()函数对packet进行申请,使用av_packet_free()函数继续释放

ffmpeg AVFrame 转 cv::Mat

  • 上面函数传入的参数frame中的图像格式,可以是YUV, RGB ,等等,,经过sws_scale()转换之后,写入cv::Mat 数据区。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    //AVFrame 转 cv::mat  
    cv::Mat frame_to_mat(const AVFrame * frame) {
      int width = frame->width;
      int height = frame->height;
    
      cv::Mat image(height, width, CV_8UC3);
      int cvLinesizes[1];
      cvLinesizes[0] = image.step1();
      if( NULL == _swsContext) {
          _swsContext = sws_getContext(width, height,
              (AVPixelFormat)frame->format, width, height,
              AVPixelFormat::AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
      }
    
      sws_scale(_swsContext, frame->data, 
          frame->linesize, 0, height, &image.data, cvLinesizes);
        
      return image;
    }
    
  • 还有一种方式 是直接将AVFrame 中的RGB数据赋值给cv::Mat
    1
    2
    3
    
    cv::Mat img;
    img = cv::Mat(height, width, CV_8UC3);
    img.data =  _rgb_frame->data[0];
    

ffmpeg开发库 C++ 输入rtsp流地址 录制指定长度的视频到本地 编程示例

要使用FFmpeg的C++开发库来录制指定长度的RTSP流视频到本地,你可以使用libavcodec、libavformat等库来完成。下面是一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
}

int main() {
    // av_register_all();  // 新版本弃用
    avformat_network_init();

    AVFormatContext *pFormatCtx = avformat_alloc_context();
    AVDictionary *options = NULL;
    av_dict_set(&options, "rtsp_transport", "tcp", 0);

    const char *rtsp_url = "rtsp://example.com/stream";  // RTSP流地址
    const char *output_file = "output.mp4";             // 输出文件名
    int duration = 60;                                   // 录制时长(秒)

    if (avformat_open_input(&pFormatCtx, rtsp_url, NULL, &options) != 0) {
        std::cerr << "无法打开RTSP流" << std::endl;
        return -1;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        std::cerr << "无法获取流信息" << std::endl;
        return -1;
    }

    AVFormatContext *pOutputFormatCtx;
    if (avformat_alloc_output_context2(&pOutputFormatCtx, NULL, NULL, output_file) < 0) {
        std::cerr << "无法分配输出格式上下文" << std::endl;
        return -1;
    }

    AVStream *pStream = avformat_new_stream(pOutputFormatCtx, NULL);
    if (!pStream) {
        std::cerr << "无法创建输出流" << std::endl;
        return -1;
    }

    if (avcodec_parameters_copy(pStream->codecpar, pFormatCtx->streams[0]->codecpar) < 0) {
        std::cerr << "无法拷贝编解码参数" << std::endl;
        return -1;
    }

    if (!(pOutputFormatCtx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&pOutputFormatCtx->pb, output_file, AVIO_FLAG_WRITE) < 0) {
            std::cerr << "无法打开输出文件" << std::endl;
            return -1;
        }
    }

    avformat_write_header(pOutputFormatCtx, NULL);

    AVPacket packet;
    int64_t startTime = av_gettime();
    int64_t endTime = startTime + duration * 1000000;

    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (av_gettime() >= endTime) {
            break;
        }

        av_packet_rescale_ts(&packet, pFormatCtx->streams[0]->time_base, pStream->time_base);
        packet.stream_index = pStream->index;

        av_interleaved_write_frame(pOutputFormatCtx, &packet);
        av_packet_unref(&packet);
    }

    av_write_trailer(pOutputFormatCtx);

    avformat_close_input(&pFormatCtx);
    avio_closep(&pOutputFormatCtx->pb);
    avformat_free_context(pOutputFormatCtx);

    av_dict_free(&options);

    return 0;
}

在这个示例中,我们首先初始化了FFmpeg库,然后打开了指定的RTSP流。我们创建了一个输出格式上下文,并且设置了输出文件名。然后,我们复制了输入流的编解码参数到输出流,接着开始逐帧读取输入流,并将其写入输出流。当录制时长达到指定时长后,我们退出录制,并释放相关资源。请确保将 rtsp_url 替换为你的 RTSP 流地址,output_file 为输出文件的路径和名称,duration 为录制时长(以秒为单位)。