微信公众号:幼儿园的学霸
个人的学习笔记,关于OpenCV,关于机器学习, …。问题或建议,请公众号留言;
之前写的ADAS客户端软件和ADAS程序之间的视频传输采用了cv::imencode和cv::imdecode
函数实现编解码,最近偶然间发现可以利用H.264对视频进行编解码,并且效果还不错,特此记录。
代码如下.使用时,首先进行编码器的初始化,然后在while循环中调用编码函数实现编码;如果需要进行网络传输,发送编码后的数据即可,在接收端,采用H264进行解码,恢复视频信息。
//
// Created by liheng on 19-12-9.
//https://www.cnblogs.com/ziyu-trip/p/7075003.html
#ifndef ADAS_X264_ENCODER_H
#define ADAS_X264_ENCODER_H
#include
#include "x264.h"
#include "opencv/cv.h"
#include "opencv/highgui.h"
struct x264_encoder{
x264_param_t param;
char preset[20];
char tune[20];
char profile[20];
x264_t* h;
x264_picture_t pic_in;
x264_picture_t pic_out;
long colorspace;
x264_nal_t* nal;
int iframe;
int iframe_size;
int inal;
};
class x264Encoder
{
public:
x264Encoder();
x264Encoder(int videoWidth, int videoHeight, int channel, int fps);
~x264Encoder();
/** 创建X264编码器
* @param[in] videoWidth 视频宽度
* @param[in] videoHeight 视频高度
* @param[in] fps 帧率
* @return 成功返回true, 失败返回false.
*/
bool Create(int videoWidth, int videoHeight, int channel = 3, int fps = 30);
/** 编码一帧
* @param[in] frame 输入的一帧图像
* @return 返回编码后数据尺寸, 0表示编码失败
*/
int EncodeOneFrame(const cv::Mat& frame);
/** 获取编码后的帧数据
* 说明: EncodeOneFrame 后调用
* @return 返回裸x264数据
*/
uchar* GetEncodedFrame() const;
/** 销毁X264编码器
*/
void Destory();
// 编码器是否可用
bool IsValid() const;
private:
void Init();
public:
int m_width;
int m_height;
int m_channel;
int m_fps;
protected:
int m_widthstep;
int m_lumaSize;
int m_chromaSize;
x264_encoder* m_encoder;
};
#endif //ADAS_X264_ENCODER_H
//
// Created by liheng on 19-12-9.
//
#include "x264_encoder.h"
#include
#include
#include
#include
#include "opencv2/imgproc.hpp"
#define ENCODER_TUNE "zerolatency"
#define ENCODER_PROFILE "baseline"
#define ENCODER_PRESET "veryfast"//"superfast"
#define ENCODER_COLORSPACE X264_CSP_I420
#define CLEAR(x) (memset((&x),0,sizeof(x)))
x264Encoder::x264Encoder()
{
Init();
}
x264Encoder::x264Encoder(int videoWidth, int videoHeight, int channel, int fps)
{
Init();
Create(videoWidth, videoHeight, channel, fps);
}
x264Encoder::~x264Encoder()
{
Destory();
}
void x264Encoder::Init()
{
m_width = 0;
m_height = 0;
m_channel = 0;
m_widthstep = 0;
m_fps = 30;
m_lumaSize = 0;
m_chromaSize = 0;
m_encoder = NULL;
}
bool x264Encoder::Create(int videoWidth, int videoHeight, int channel, int fps)
{
int ret;
int imgSize;
if (videoWidth <= 0 || videoHeight <= 0 || channel < 0 || fps <= 0){
printf("wrong input param\n");
return false;
}
m_width = videoWidth;
m_height = videoHeight;
m_channel = channel;
m_fps = fps;
m_widthstep = videoWidth * channel;
m_lumaSize = m_width * m_height;
m_chromaSize = m_lumaSize / 4;
imgSize = m_lumaSize * channel;
m_encoder = (x264_encoder *)malloc(sizeof(x264_encoder));
if (!m_encoder){
printf("cannot malloc x264_encoder !\n");
return false;
}
CLEAR(*m_encoder);
m_encoder->iframe = 0;
m_encoder->iframe_size = 0;
strcpy(m_encoder->preset, ENCODER_PRESET);
strcpy(m_encoder->tune, ENCODER_TUNE);
/*初始化编码器*/
CLEAR(m_encoder->param);
x264_param_default(&m_encoder->param);
ret = x264_param_default_preset(&m_encoder->param, m_encoder->preset, m_encoder->tune);
if (ret < 0){
printf("x264_param_default_preset error!\n");
return false;
}
/*cpuFlags 去空缓冲区继续使用不死锁保证*/
m_encoder->param.i_threads = X264_SYNC_LOOKAHEAD_AUTO;
/*视频选项*/
m_encoder->param.i_csp = X264_CSP_I420;
m_encoder->param.i_width = m_width; // 要编码的图像的宽度
m_encoder->param.i_height = m_height; // 要编码的图像的高度
m_encoder->param.i_frame_total = 0; // 要编码的总帧数,不知道用0
m_encoder->param.i_keyint_max = 10*fps;// 关键帧间隔
/*流参数*/
m_encoder->param.i_bframe = 5;
m_encoder->param.b_open_gop = 0;
m_encoder->param.i_bframe_pyramid = 0;
m_encoder->param.i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
/*log参数,不需要打印编码信息时直接注释掉*/
m_encoder->param.i_log_level = X264_LOG_NONE;
m_encoder->param.i_fps_num = fps;//码率分子
m_encoder->param.i_fps_den = 1; //码率分母
m_encoder->param.b_intra_refresh = 1;
m_encoder->param.b_annexb = 1;
m_encoder->param.rc.f_rf_constant = 24;
m_encoder->param.rc.i_rc_method = X264_RC_CRF;
/////////////////////////////////////////////////////////////////////////////////////////////////////
strcpy(m_encoder->profile, ENCODER_PROFILE);
ret = x264_param_apply_profile(&m_encoder->param, m_encoder->profile);
if (ret < 0){
printf("x264_param_apply_profile error!\n");
return false;
}
/*打开编码器*/
m_encoder->h = x264_encoder_open(&m_encoder->param);
m_encoder->colorspace = ENCODER_COLORSPACE;
/*初始化pic*/
ret = x264_picture_alloc(&m_encoder->pic_in, m_encoder->colorspace, m_width, m_height);
if ( ret < 0 ){
printf("x264_picture_alloc error! ret=%d\n", ret);
return false;
}
m_encoder->pic_in.img.i_csp = m_encoder->colorspace;
m_encoder->pic_in.img.i_plane = 3;
m_encoder->pic_in.i_type = X264_TYPE_AUTO;
m_encoder->inal = 0;
m_encoder->nal = (x264_nal_t *)calloc(2, sizeof(x264_nal_t));
if (!m_encoder->nal){
printf("malloc x264_nal_t error!\n");
return false;
}
CLEAR(*(m_encoder->nal));
return true;
}
int x264Encoder::EncodeOneFrame(const cv::Mat& frame)
{
if (frame.empty()){
return 0;
}
cv::Mat bgr(frame), yuv;
if(1 == frame.channels()){
cv::cvtColor(frame, bgr, CV_GRAY2BGR);
}
cv::cvtColor(bgr, yuv, CV_BGR2YUV_I420);
memcpy(m_encoder->pic_in.img.plane[0], yuv.data, m_lumaSize);
memcpy(m_encoder->pic_in.img.plane[1], yuv.data + m_lumaSize, m_chromaSize);
memcpy(m_encoder->pic_in.img.plane[2], yuv.data + m_lumaSize + m_chromaSize, m_chromaSize);
m_encoder->pic_in.i_pts = m_encoder->iframe ++;
m_encoder->iframe_size = x264_encoder_encode(m_encoder->h, &m_encoder->nal, &m_encoder->inal, &m_encoder->pic_in, &m_encoder->pic_out);
return m_encoder->iframe_size;
}
uchar* x264Encoder::GetEncodedFrame() const
{
return m_encoder->nal->p_payload;
}
void x264Encoder::Destory()
{
if (m_encoder){
if (m_encoder->h){
x264_encoder_close(m_encoder->h);
m_encoder->h = NULL;
}
free(m_encoder);
m_encoder = NULL;
}
}
bool x264Encoder::IsValid() const
{
return ((m_encoder != NULL) && (m_encoder->h != NULL));
}
代码如下,代码来源见代码说明。在原代码基础上进行了微调,以保证视频质量无损。
#ifndef _AV_H264_H_
#define _AV_H264_H_
/***********************************************************
** Author:kaychan
** Data:2019-11-29
** Mail:[email protected]
** Explain:a h264 codec
***********************************************************/
#include
#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
};
#endif
typedef struct AvH264EncConfig_T {
int width = 320;
int height = 240;
int frame_rate = 25;
//int64_t bit_rate = 320000;
int gop_size = 250;
int max_b_frames = 0;
}AvH264EncConfig;
class h264Encoder {
public:
h264Encoder();
~h264Encoder();
int Init(AvH264EncConfig h264_config);
AVPacket *encode(const cv::Mat& mat);
void Destory();
private:
AVCodec *cdc_;
AVCodecContext *cdc_ctx_;
AVFrame *avf_;
AVPacket *avp_;
int frame_size_;
int pts_;
};
#endif
#include "h264encoder.h"
h264Encoder::h264Encoder() {
cdc_ = NULL;
cdc_ctx_ = NULL;
avf_ = NULL;
avp_ = NULL;
}
h264Encoder::~h264Encoder()
{
Destory();
}
int h264Encoder::Init(AvH264EncConfig h264_config) {
pts_ = 0;
cdc_ = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!cdc_) {
return -1;
}
cdc_ctx_ = avcodec_alloc_context3(cdc_);
if (!cdc_ctx_) {
return -1;
}
//cdc_ctx_->bit_rate = h264_config.bit_rate;//导致画面模糊
cdc_ctx_->width = h264_config.width;
cdc_ctx_->height = h264_config.height;
cdc_ctx_->time_base = { 1, h264_config.frame_rate };
cdc_ctx_->framerate = { h264_config.frame_rate, 1 };
cdc_ctx_->gop_size = h264_config.gop_size;
cdc_ctx_->max_b_frames = h264_config.max_b_frames;
cdc_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
cdc_ctx_->codec_id = AV_CODEC_ID_H264;
cdc_ctx_->codec_type = AVMEDIA_TYPE_VIDEO;
//cdc_ctx_->qmin = 10;
//cdc_ctx_->qmax = 51;
//cdc_ctx_->qcompress = 0.6;
AVDictionary *dict = nullptr;
//av_dict_set(&dict, "preset", "slow", 0);
av_dict_set(&dict, "preset", "veryfast", 0);
av_dict_set(&dict, "tune", "zerolatency", 0);
av_dict_set(&dict, "profile", "main", 0);
avf_ = av_frame_alloc();
avp_ = av_packet_alloc();
if (!avf_ || !avp_) {
return -1;
}
av_init_packet(avp_);
frame_size_ = cdc_ctx_->width * cdc_ctx_->height;
avf_->format = cdc_ctx_->pix_fmt;
avf_->width = cdc_ctx_->width;
avf_->height = cdc_ctx_->height;
// alloc memory
int r = av_frame_get_buffer(avf_, 0);
if (r < 0) {
return -1;
}
r = av_frame_make_writable(avf_);
if (r < 0) {
return -1;
}
return avcodec_open2(cdc_ctx_, cdc_, &dict);
}
void h264Encoder::Destory() {
if(cdc_ctx_) avcodec_free_context(&cdc_ctx_);
if (avf_) av_frame_free(&avf_);
if (avp_) av_packet_free(&avp_);
}
AVPacket *h264Encoder::encode(const cv::Mat& mat) {
if (mat.empty()) return NULL;
cv::resize(mat, mat, cv::Size(cdc_ctx_->width, cdc_ctx_->height));
cv::Mat yuv;
cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
unsigned char *pdata = yuv.data;
// fill yuv420
// yyy yyy yyy yyy
// uuu
// vvv
avf_->data[0] = pdata;
avf_->data[1] = pdata + frame_size_;
avf_->data[2] = pdata + frame_size_ * 5 / 4;
avf_->pts = pts_++;
int r = avcodec_send_frame(cdc_ctx_, avf_);
if (r >= 0) {
r = avcodec_receive_packet(cdc_ctx_, avp_);
if (r == 0) {
avp_->stream_index = avf_->pts;
return avp_;
}
if (r == AVERROR(EAGAIN) || r == AVERROR_EOF) {
return NULL;
}
}
return NULL;
}
#ifndef CH264DECODER_H
#define CH264DECODER_H
#include
#include
//C++引用C语言的头文件
extern "C"
{
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
}
class CH264Decoder
{
public:
CH264Decoder();
~CH264Decoder();
/*************************************************
Function:initial
Description:初始化
Input:无
Output:无
Return:错误代码
Others:无
*************************************************/
int initial();
/*************************************************
Function:decode
Description:解码
Input:pDataIn-待解码数据,nInSize-待解码数据长度
Output:pDataOut-解码后的数据,nWidth-解码后的图像宽度,nHeight-解码后的图像高度
Return:错误代码
Others:解码后的数据为RGB16格式
*************************************************/
int decode(uint8_t *pDataIn, int nInSize, cv::Mat& res);
/*************************************************
Function:unInitial
Description:销毁
Input:无
Output:无
Return:无
Others:无
*************************************************/
void unInitial();
private:
int avframe_to_cvmat(AVFrame *frame,cv::Mat& res);
AVFrame *cvmat2avframe(cv::Mat mat);
private:
AVCodec *codec;
AVCodecContext *context;
AVFrame *frame;
AVPacket packet;
};
#endif // CH264DECODER_H
#include "h264decoder.h"
CH264Decoder::CH264Decoder()
{
initial();
}
CH264Decoder::~CH264Decoder()
{
unInitial();
}
int CH264Decoder::initial()
{
//avcodec_register_all();//新版本应该不需要这句话
av_init_packet(&packet);
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec)
{
printf("avcodec_find_encoder failed");
return -1;
}
context = avcodec_alloc_context3(codec);
if (!context)
{
printf("avcodec_alloc_context3 failed");
return -2;
}
context->codec_type = AVMEDIA_TYPE_VIDEO;
context->pix_fmt = AV_PIX_FMT_YUV420P;
if (avcodec_open2(context, codec, NULL) < 0)
{
printf("avcodec_open2 failed");
return -3;
}
frame = av_frame_alloc();
if (!frame)
{
return -4;
}
return 0;
}
void CH264Decoder::unInitial()
{
avcodec_close(context);
av_free(context);
av_frame_free(&frame);
}
int CH264Decoder::decode(uint8_t *pDataIn, int nInSize, cv::Mat& res)
{
// av_init_packet(&packet);
packet.size = nInSize;
packet.data = pDataIn;
if (packet.size > 0)
{
int got_picture=0;
//int ret= avcodec_decode_video2(context, frame, &got_picture, &packet);
//新版用法
int ret = avcodec_send_packet(context, &packet);
if (ret == 0) got_picture = avcodec_receive_frame(context, frame); //got_picture = 0 success, a frame was returned
if (ret < 0)
{
printf("avcodec_encode_video2 failed");
return -2;
}
if (got_picture==0)//采用avcodec_decode_video2时,此处为if (got_picture)
{
avframe_to_cvmat(frame,res);
}
}
else
{
printf("no data to decode");
return -1;
}
return 0;
}
int CH264Decoder::avframe_to_cvmat(AVFrame *frame,cv::Mat& res)
{
int width = frame->width, height = frame->height;
res.create(height*3/2, width, CV_8UC1);
memcpy( res.data, frame->data[0], width*height );
memcpy( res.data + width*height, frame->data[1], width*height/4 );
memcpy( res.data + width*height*5/4, frame->data[2], width*height/4 );
//cv::imshow( "yuv_show", res );//yuv格式
cv::cvtColor( res, res, cv::COLOR_YUV2BGR_I420 );//bgr格式
//cv::imshow( "bgr_show", bgr );
return 0;
}
AVFrame * CH264Decoder::cvmat2avframe(cv::Mat mat) {
// alloc avframe
AVFrame *avframe = av_frame_alloc();
if (avframe && !mat.empty()) {
avframe->format = AV_PIX_FMT_YUV420P;
avframe->width = mat.cols;
avframe->height = mat.rows;
av_frame_get_buffer(avframe, 0);
av_frame_make_writable(avframe);
cv::Mat yuv; // convert to yuv420p first
cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
// calc frame size
int frame_size = mat.cols * mat.rows;
unsigned char *pdata = yuv.data;
// fill yuv420
// yyy yyy yyy yyy
// uuu
// vvv
avframe->data[0] = pdata; // fill y
avframe->data[1] = pdata + frame_size; // fill u
avframe->data[2] = pdata + frame_size * 5 / 4; // fill v
}
return avframe;
}
X264EncoderDemo.cpp
//
// Created by liheng on 19-12-9.
//
#include "x264_encoder.h"
#include "h264decoder.h"
#include
#include
int main()
{
cv::Mat frame;
cv::VideoCapture videoCapture("/home/liheng/ADAS_Video/1120/ADAS_Video-20191120-151849.mp4");
for (int i = 0; i < 10; ++i)
{
videoCapture >> frame;
}
//x264 encode
x264Encoder m_x264Encoder;
m_x264Encoder.Create(1280,720,3,30);
cv::Mat x264_dst;
//h264 decode
CH264Decoder m_h264Decoder;
m_h264Decoder.initial();
//cv encode
int jpeg_quality = 75;
std::vector params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(jpeg_quality);
cv::Mat cv_dst;
int nWaitTime =1;
while (1)
{
videoCapture >>frame;
if( frame.empty() )
break;
cv::imshow("src",frame);
cv::Mat _frame;
cv::resize(frame,_frame,cv::Size(),0.5,0.5);
//cv ecode + cv decode
double timePoint1 = cv::getTickCount();
std::vector jpgSize;
cv::imencode(".jpg", _frame, jpgSize, params);
cv_dst = cv::imdecode(jpgSize,cv::IMREAD_COLOR);
cv::imshow("cv_dst",cv_dst);
//x264 ecode + h264 decode
double timePoint2 = cv::getTickCount();
int size = m_x264Encoder.EncodeOneFrame(frame);
uchar* data = nullptr;
data = m_x264Encoder.GetEncodedFrame();
m_h264Decoder.decode(data,size,x264_dst);
cv::imshow("x264_dst",x264_dst);
double timePoint3 = cv::getTickCount();
//cv::Mat diff = x264_dst - frame;
//cv::imshow("diff",diff);//查看编解码前后图像是否有差异
printf("cv::encode size:%d Fps:%.2f, x264 encode size:%d,Fps:%.2f\n",
jpgSize.size(),cv::getTickFrequency()/(timePoint2-timePoint1),
size,cv::getTickFrequency()/(timePoint3-timePoint2));
char chKey = cv::waitKey(nWaitTime);
//ESC
if (27 == chKey)break;
else if (' ' == chKey) nWaitTime = !nWaitTime;
}
return 0;
}
上面代码中对比了cv::encode
和x264对Mat进行编码,在编码后数据量的大小,和视频质量两个方面的效果对比。从数据量上来看,cv::encode
编码后数据量远大于x264编码数据量,即使采用75%的压缩率,cv::encode
的数据量仍远高于x264,并且压缩后视频质量不如x264压缩后视频质量。
H264EncoderDemo.cpp
//
// Created by liheng on 19-12-9.
//
#include "h264encoder.h"
#include "h264decoder.h"
#include
#include
int main()
{
cv::Mat frame;
cv::Mat dst;
cv::VideoCapture videoCapture("/home/liheng/ADAS_Video/1120/ADAS_Video-20191120-151849.mp4");
for (int i = 0; i < 10; ++i)
{
videoCapture >> frame;
}
h264Encoder h264;
AvH264EncConfig conf;
conf.width = 1280;
conf.height = 720;
conf.gop_size = 10;
conf.max_b_frames = 0;
conf.frame_rate = 30;
h264.Init(conf);
CH264Decoder m_h264Decoder;
m_h264Decoder.initial();
int jpeg_quality = 75;
std::vector params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(jpeg_quality);
cv::Mat cvDst;
int nWaitTime =1;
while (1)
{
videoCapture >>frame;
if( frame.empty() )
break;
cv::imshow("src",frame);
cv::Mat _frame;
cv::resize(frame,_frame,cv::Size(),0.5,0.5);
double timePoint1 = cv::getTickCount();
std::vector jpgSize;
cv::imencode(".jpg", _frame, jpgSize, params);
double timePoint2 = cv::getTickCount();
cvDst = cv::imdecode(jpgSize,cv::IMREAD_COLOR);
cv::imshow("cvdecode",cvDst);
// do encode
AVPacket *pkt = h264.encode(frame);
int size = pkt->size;
uchar* data = nullptr;
data = pkt->data;
m_h264Decoder.decode(data,size,dst);
cv::imshow("decode",dst);
double timePoint3 = cv::getTickCount();
//cv::Mat diff = dst - frame;
//cv::imshow("diff",diff);//查看编解码前后图像是否有差异
printf("cv::encode size:%d Fps:%.2f, h264 encode size:%d,Fps:%.2f\n",
jpgSize.size(),cv::getTickFrequency()/(timePoint2-timePoint1),
size,cv::getTickFrequency()/(timePoint3-timePoint2));
char chKey = cv::waitKey(nWaitTime);
//ESC
if (27 == chKey)break;
else if (' ' == chKey) nWaitTime = !nWaitTime;
}
return 0;
}
以上两个.cpp
文件对应的CMakeLists.txt
文件内容:
CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(H264Demo)
set(CMAKE_CXX_STANDARD 11)
if (CMAKE_BUILD_TYPE STREQUAL "")
message(STATUS "CMAKE_BUILD_TYPE not defined, 'Release' will be used")
set(CMAKE_BUILD_TYPE "Release")
endif()
find_package( OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
set(OpenCV_DIR /usr/local/opencv3.4.1/share/OpenCV)
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
message(STATUS "OPENCV found")
else (OpenCV_FOUND)
message(WARNING "OPENCV is disabled or not found")
return()
endif()
add_executable(X264EncoderDemo X264EncoderDemo.cpp x264_encoder.cpp h264encoder.cpp h264decoder.cpp)
target_link_libraries(X264EncoderDemo ${OpenCV_LIBRARIES}
x264
avformat avdevice avcodec avutil avfilter postproc swresample swscale)
add_executable(H264EncoderDemo H264EncoderDemo.cpp h264decoder.cpp h264encoder.cpp)
target_link_libraries(H264EncoderDemo ${OpenCV_LIBRARIES}
avformat avdevice avcodec avutil avfilter postproc swresample swscale)
针对x264编码,需要安装libx264库,安装命令为:sudo apt install libx264-dev
,H264编解码需要安装ffmpeg库,同样采用apt命令进行安装即可。
我在ADAS程序中将视频发送部分修改为采用x264进行编码后,能够在不缩放视频的情况下进行传输,效果满足使用要求,非常满意。不过,随后,我发现,利用gstreamer-rtsp-sever
也能够实现同样的效果,该方式采用rtsp视频流的形式进行视频传输,在接收端,可以直接利用cv::VideoCapture
进行视频接收,不用编写额外的解码代码,非常方便,此外,也可以利用VLC media 进行视频接收,此时甚至不用专门编写视频接收代码了;建议有需求的可以尝试一下该方法,代码也是非常简单,此处就不贴出了(主要是感觉我的这个代码并未写完善,有缺陷)。
下面的是我的公众号二维码图片,欢迎关注。