Skip to content

基于opencv和ffmpeg的视频编解码、推拉流库、工具

Notifications You must be signed in to change notification settings

LSH9832/easyvideo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

easyvideo: 基于OpenCV和FFMPEG实现的快速视频处理工具

为何要做这么一个库

  • OpenCV确实简单易用,小白用一个cv::VideoCapture就能搞定几乎一切的视频文件/视频流的读取与处理,问题在于这么直接使用经常耗费较多的CPU资源,并且也不支持硬解码。

  • FFMPEG支持丰富的硬解码方式,但是高灵活性带来的是上手难度以及代码中大量的配置项,对新手不友好。

本项目在保留FFMPEG一定灵活性的同时,也保证了较好的易用性,接口与OpenCV基本一样。同时,即使是使用软解码,本项目的CPU占用率也在多种解码格式上优于OpenCV。

安装本项目

需要先安装好OpenCV和FFMPEG并在系统路径下能找到这两个库

为了编译demo,需要安装好yaml-cpp:sudo apt install libyaml-cpp-dev,或手动编译

mkdir build;cd build
cmake -DENABLE_RKMPP=OFF ..   # 如果在瑞芯微平台上使用,可以打开该选项通过mpp和rga库进行硬件加速
make -j8

将生成libeasyvideo.so和推流的demo可执行文件pushRTSP,将文件夹include/easyvideo和该动态库文件放到相应环境下即可使用

使用

1. 视频流捕获

接口

// 创建视频流捕获器
BaseCapture* createCapture(
    std::string url,                 // 同cv::VideoCapture
    int apiPreference=cv::CAP_ANY,   // 同cv::VideoCapture
    int type=CAP_TYPE_JPEG,          // 视频流类型
    bool dropFrame=false             // 当为网络视频流时,若读取速度较慢是否丢弃中间未读取的图像帧,目前设置为true有bug,不建议使用
    std::string decoder_name="auto"  // 当为网络视频流时,手动选取ffmpeg解码器,rkmpp平台支持(h264/h265/vp8/vp9)_rkmpp
);

// 以下使用方法同cv::VideoCapture
virtual bool BaseCapture::open(std::string url, int apiPreference=::cv::CAP_ANY);
virtual bool BaseCapture::read(::cv::Mat &frame);
virtual void BaseCapture::release();
virtual void BaseCapture::operator >> (::cv::Mat &frame);
virtual void BaseCapture::set(int propId, double value);
virtual double BaseCapture::get(int propId);

示例

#include <easyvideo/opencv/capture.h>

int main(int argc, char** argv)
{
    // 读取本地相机
    auto cap_local_camera = easyvideo::Capture::createCapture(
        "/dev/video0", cv::CAP_V4L2, 
        easyvideo::Capture::CAP_TYPE_JPEG  // 相机输出为JPEG格式
        // 该选项为CAP_TYPE_YUYV时相机输出为YUYV格式
        // 不知道相机输出为何种格式时填写CAP_TYPE_NORMAL,此时退化为cv::VideoCapture,无任何性能优化
    );

    // 读取本地视频文件,此处相当直接使用cv::VideoCapture
    auto cap_local_file = easyvideo::Capture::createCapture(
        "/path/to/your/file.mp4", cv::CAP_ANY,
        easyvideo::Capture::CAP_TYPE_NORMAL
    );

    // 读取网络视频流(目前支持rtsp)
    // 网络视频流会自动优先选择硬解码以降低CPU占用率
    // 目前支持的硬解方案:(h264/h265)_(cuvid/nvmpi/rkmpp)
    // 即:英伟达显卡/英伟达Jetson平台/瑞芯微平台硬解,需要在FFMPEG编译时打开相应选项
    auto cap_stream = easyvideo::Capture::createCapture(
        "rtsp://[url]:[port]/[stream path]", cv::CAP_ANY,
        easyvideo::Capture::CAP_TYPE_STREAM,
        false, "h264_rkmpp"
    );

    return 0;
}

2. 视频流单帧处理

当只想关注对视频单帧/每帧图像的处理,可以使用视频服务接口

接口

easyvideo::VideoServer::VideoServer();

void easyvideo::VideoServer::setupSource(sensor::camera::Info::Device info);
void easyvideo::VideoServer::setupSource(YAML::Node config)
void easyvideo::VideoServer::setupSource(
    std::string device_name,
    std::string device_source,
    std::string device_type="auto",
    std::string device_format="auto",
    int width=-1,
    int height=-1,
    int fps=-1, 
    int reopen_times=-1,
    int reopen_delay=100,  // ms
    std::vector<int> roi={},
    std::string decoder_name="auto"
);


void easyvideo::VideoServer::setCallback(void(*func)(const cv::Mat&));

template <class T>
void easyvideo::VideoServer::setCallback(void(T::*func)(const cv::Mat&), T* obj);

void easyvideo::VideoServer::start_service();
void easyvideo::VideoServer::stop_service();

// 结构体定义
struct sensor::camera::Info::Device
{
    // name: 自己随便取名,source:即url,type: "usb/file/stream", format: "yuyv/jpeg/h264/h265/auto"
    std::string name, source, type, format;
    std::string decoder_name="auto";
    int fps, width, height;     // -1为自动
    cv::Rect crop;              // 设置后会取对应的矩形区域而不是整张图像
    int reopen_times=-1;        // 断连后重启次数,-1代表无限
    int reopen_delay=100;       // ms
    YAML::Node _config;         // 如果使用yaml文件加载则上述信息也会保存在该变量中
};

yaml配置文件示例

name: my_usb_camera
source: /dev/video0
type: usb
format: jpeg
width: 1920
height: 1080
fps: 30
decoder_name: h264_rkmpp
preprocess:
  reopen:
    times: -1
    delay: 100  # ms
  crop: [0,0,0,0]  # 全0代表不进行裁剪

示例

#include <easyvideo/videoServer.h>
#include <signal>

void callBack(const cv::Mat& image)
{
    // do something
    // ...
    cv::imshow("image view", image);
    int key = cv::waitKey(1);
    switch (key) {
    case 's':
        cv::imwrite("image.jpg", image);
        break
    // ...
    default:
        break;
    };
}

class ServerHandle
{
public:
    void callback(const cv::Mat& image)
    {
        // do something
    }
};

int main(int argc, char** argv)
{
    easyvideo::VideoServer server;
    server.setupSource("/path/to/your/config.yaml");

    // 以下二选一,不能同时设置,同时设置则以最后设置的为准
    // 1. 普通函数
    server.setCallback(&callBack);

    // 2. 成员函数
    ServerHandle handle;
    server.setCallBack(&ServerHandle::callback, &handle);

    signal(SIGINT, [server](int sig) {
        server.stop_service();
    });
    // 启动服务,阻塞
    server.start_service();
    return 0;
}

3. 推流

接口

easyvideo::RTSPPusher::RTSPPusher(std::string url);

void easyvideo::RTSPPusher::start();  // 在子线程中启动推流
void easyvideo::RTSPPusher::stop();   // 停止当前子线程
void easyvideo::RTSPPusher::pushFrameData(cv::Mat &frame);  // 推流当前图像

// 打开指定编码器
int easyvideo::RTSPPusher::open_codec(int width, int height, int den, int kB=100, std::string encoder_name="");

使用步骤: 先定义,再打开编码器,再启动推流。

#include <easyvideo/push.h>

int main(int argc, char** argv)
{

    int w=1920, h=1080, fps=30, kb=512; // 4Mbps
    auto pusher = new easyvideo::RTSPPusher("rtsp://localhost/stream/test");
    pusher->open_codec(w, h, fps, kb, "h264_cuvid");
    pusher->start();

    while (1)
    {
        cv::Mat mat(w, h, CV_8UC3);
        // 处理图像
        // ...

        pusher->pushFrameData(mat);
    }

    pusher->stop();
    delete pusher;
    pusher = nullptr;
    
    return 0;
}

About

基于opencv和ffmpeg的视频编解码、推拉流库、工具

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published