【深度学习】使用ffmpg及gstreamer进行视频拉流及编解码(一):ffmpg

目录

  • 为什么要进行视频编解码
    • 网络带宽
    • 常见的视频编码格式
    • 视频分辨率及其占用的经验带宽
    • 千兆网口及百兆网口
  • 硬件编解码和软件编解码的区别
  • 拉流工具简介
  • 安装ffmpg库
    • 安装必要的依赖库
    • 安装ffmpg库
  • 代码

为什么要进行视频编解码

视频流需要编解码的主要原因是视频文件的数据量很大,直接传输视频文件会占用大量网络带宽和存储空间。而通过对视频进行编码和解码,可以将视频数据压缩到较小的体积,从而实现更高效的传输和存储。
具体来说,编码就是将原始的视频数据转换为压缩后的视频数据,而解码则是将压缩后的视频数据还原为原始的视频数据。编码和解码过程都需要采用一定的算法和规则,以便能够在压缩和解压缩过程中实现最小的数据损失和最高的数据压缩比。常用的视频编解码器包括H.264、H.265、VP8、VP9等。

视频编解码的主要优点是:

节省存储空间:通过视频编码可以将视频文件的大小压缩到原始大小的10%~50%左右,从而节省存储空间。

减少网络带宽:通过视频编码可以将视频流的数据量减小,从而减少传输数据所需要的网络带宽。

提高视频传输质量:通过视频编码可以减少数据传输时的延迟和抖动,从而提高视频的传输质量和稳定性。

加密视频流:通过对视频进行编码,可以加密视频流,保护数据的安全性和隐私性。
总之,视频编解码是实现高效传输和存储视频的重要手段,广泛应用于视频监控、视频直播、视频通话等领域。

网络带宽

视频拉流中的网络带宽指的是在视频流传输过程中所占用的网络传输带宽。在视频拉流过程中,视频流需要通过网络传输到客户端,因此需要一定的网络带宽来支持数据的传输。
网络带宽是指在单位时间内可以传输的数据量,通常用Mbps(兆比特每秒)或Gbps(千兆比特每秒)来表示。网络带宽越大,传输的数据量就越大,传输速度也就越快。因此,在视频拉流过程中,需要保证网络带宽足够大,以便能够快速、稳定地传输视频数据。
视频拉流中的网络带宽大小会受到多种因素的影响,例如网络带宽的限制、网络拥塞、数据传输的距离等。如果网络带宽不足,就会出现视频卡顿、画面模糊、声音不同步等问题,影响用户的观看体验。因此,在进行视频拉流时,需要对网络带宽进行充分考虑,选择合适的传输协议、码率和分辨率,以便实现更好的视频传输效果。

常见的视频编码格式

H.264/AVC:H.264/AVC是一种广泛应用的视频编码格式,具有高压缩比、高画质和低延迟等优点。它通常采用的是CBR(固定码率)或者VBR(可变码率)的编码方式,消耗的带宽大小取决于视频分辨率、帧率和码率等因素。

H.265/HEVC:H.265/HEVC是H.264/AVC的后继版本,具有更高的压缩比和更低的码率,可以在相同的画质下降低带宽消耗。然而,H.265/HEVC相对于H.264/AVC而言,更加计算复杂,需要更高的计算性能和更好的网络环境。

视频分辨率及其占用的经验带宽

1080p、CIF、D1和720p是视频分辨率的标准表示方式,具体解释如下:

1080p:也称为全高清,是指视频分辨率为1920×1080像素的格式,其中“p”表示逐行扫描方式。

CIF:是指视频分辨率为352×288像素的格式,其中“CIF”代表“Common Intermediate Format”,是一种标准格式。

D1:是指视频分辨率为720×576像素的格式,其中“D1”代表“Digital 1”,是一种标准格式。

720p:也称为高清,是指视频分辨率为1280×720像素的格式,其中“p”表示逐行扫描方式。
不同的视频分辨率对带宽的消耗大小不同,一般而言,分辨率越高,需要的带宽就越大。根据经验值,1080p视频流需要的带宽一般在5-10Mbps之间,CIF视频流需要的带宽一般在0.2-0.5Mbps之间,D1视频流需要的带宽一般在1-2Mbps之间,720p视频流需要的带宽一般在2-4Mbps之间。但是需要注意的是,实际消耗的带宽大小还受到视频编码方式、帧率、码率、压缩比以及网络环境和设备性能等因素的影响,因此具体的带宽消耗还需要根据实际情况进行评估。

千兆网口及百兆网口

千兆网口和百兆网口的区别主要在于传输速率和支持的最大带宽不同。具体而言,千兆网口支持的传输速率为1Gbps(即1000Mbps),而百兆网口支持的传输速率为100Mbps,因此千兆网口的传输速率是百兆网口的10倍。同时,千兆网口支持的最大带宽也比百兆网口更大,可以达到1Gbps。
至于为什么网线4根线也能运行,这是因为千兆以太网技术采用了全双工通信方式,并且采用了自适应速率和双工模式,可以在不同的传输环境下自动调整传输速率和双工模式。因此,即使网线只有4根线,也可以通过自适应技术实现千兆网口的运行。不过,为了保证千兆以太网的正常运行,建议使用符合千兆以太网标准的8根网线,以避免出现传输速率降低或者网络不稳定等问题。

硬件编解码和软件编解码的区别

硬件编解码和软件编解码的主要区别在于编解码的实现方式不同。
软件编解码是在CPU上实现的,需要使用软件算法对视频数据进行编解码。这种方式的优点是灵活性高,可以在不同的平台和系统上运行,同时支持多种编解码格式和算法,兼容性好。缺点是速度较慢,尤其是对于高清视频和高帧率视频,需要消耗大量的CPU资源,容易造成系统卡顿。
硬件编解码是通过GPU或专用的硬件模块实现的,可以快速地对视频数据进行编解码。这种方式的优点是速度快,对于高清视频和高帧率视频可以实现实时处理,同时可以减少CPU的负担和功耗。缺点是硬件编解码器通常只支持有限的编解码格式和算法,不够灵活,而且不同平台和系统之间的兼容性也有所差异。
总的来说,硬件编解码和软件编解码各有优缺点,需要根据具体的应用场景和需求进行选择。对于需要实现高速编解码和实时处理的场景,硬件编解码是更好的选择;对于对兼容性和灵活性有更高要求的场景,软件编解码则更适合。

拉流工具简介

GStreamer:一款功能强大的多媒体框架,可以用于音视频的采集、编码、解码、处理和传输。

FFmpeg:一个开源的音视频处理工具,可以用于音视频的采集、编码、解码、转码、处理等。

FFmpeg和GStreamer都是流媒体处理框架,它们在音视频编解码、转码、过滤、采集等方面都有广泛的应用。
FFmpeg是一个开源的音视频处理库,支持多种音视频格式的编解码、转码、过滤等操作。它可以在多个平台上运行,包括Windows、Linux、macOS等,被广泛应用于多媒体软件开发、流媒体服务器、视频编辑、转码等领域。FFmpeg提供了一系列的命令行工具,可以方便地进行各种音视频处理操作,如播放、录制、转码等。同时,FFmpeg也提供了一系列的API接口,允许开发者在自己的应用程序中集成音视频处理功能。
GStreamer是一个基于插件的开源多媒体框架,可以进行音视频的采集、编解码、处理和播放等操作。它提供了一套组件和插件体系结构,可以通过组合不同的插件来实现不同的功能。GStreamer可以运行在多个平台上,包括Linux、macOS、Windows等,被广泛应用于数字电视、音视频编辑、流媒体服务器等领域。GStreamer提供了一套完整的API接口,可以通过编写C/C++、Python等语言的代码来实现音视频处理功能。
区别方面,FFmpeg更加注重音视频编解码方面的处理,被广泛应用于媒体格式转换、编码解码等方面;而GStreamer则更加注重音视频处理的整体框架,在音视频采集、流媒体传输、播放等方面有着广泛应用。同时,GStreamer的应用范围更加广泛,可以用于多媒体软件开发、数字电视、流媒体服务器等领域。

安装ffmpg库

安装必要的依赖库

在编译FFmpeg之前,需要安装一些必要的依赖库,包括:
h264解码库
nasm

先安装nasm依赖,安装完成后,将帮助安装h264.

wget https://www.nasm.us/pub/nasm/releasebuilds/2.14/nasm-2.14.tar.gz
/home/imagedepth/streamWork/nasm-2.14.tar.gz
 tar zxvf /home/imagedepth/streamWork/nasm-2.14.tar.gz -C ./
 cd nasm-2.14
 ./configure
 make
 make install

下面安装x264:

使用:

git clone https://code.videolan.org/videolan/x264.git
cd x264
./configure --prefix=/usr/x264/ --includedir=/usr/local/include --libdir=/usr/local/lib --enable-shared
make
make install

如果出现:ERROR: x264 not found using pkg-config

vim /etc/profile
末尾加入内容export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig,具体看各位自己x264的安装路径
source /etc/profile
然后再./configure ...就没问题了

安装ffmpg库

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg
./configure --enable-gpl --enable-libx264 --enable-shared
make
make install
#添加动态库搜索路径,解决找不到libx264.so.164的问题
sudo sh -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/local.conf'
sudo ldconfig

安装后可执行文件默认放在/usr /local/bin,库文件默认放在/usr/local/lib,配置文件默认放在/usr/local/etc,其它的资源文件放在/usr /local/share,头文件在/usr/local/include

代码

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
int main(int argc, char* argv[]) {
    // 输入流地址,对应大华摄像头
    const char* input_url = "rtsp://admin:admin123@192.168.25.253:554/cam/realmonitor?channel=1&subtype=0";
    // 输出视频宽度
    const int output_width = 640;
    // 输出视频高度
    const int output_height = 480;
    // 初始化FFmpeg
    // av_register_all();
    avformat_network_init();
    // 打开输入流
    AVFormatContext* input_format_context = avformat_alloc_context();
    if (avformat_open_input(&input_format_context, input_url, nullptr, nullptr) != 0) {
        std::cerr << "Could not open input stream: " << input_url << std::endl;
        return EXIT_FAILURE;
    }
    // 获取输入流信息
    if (avformat_find_stream_info(input_format_context, nullptr) < 0) {
        std::cerr << "Could not find stream information." << std::endl;
        return EXIT_FAILURE;
    }
    // 获取视频流索引
    int video_stream_index = -1;
    for (unsigned int i = 0; i < input_format_context->nb_streams; i++) {
        if (input_format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }
    if (video_stream_index == -1) {
        std::cerr << "Could not find video stream." << std::endl;
        return EXIT_FAILURE;
    }
    // 获取视频流解码器
    AVCodecParameters* codec_parameters = input_format_context->streams[video_stream_index]->codecpar;
    AVCodec* codec = const_cast<AVCodec*>(avcodec_find_decoder(codec_parameters->codec_id));
    if (!codec) {
        std::cerr << "Could not find decoder." << std::endl;
        return EXIT_FAILURE;
    }
    // 打开视频流解码器
    AVCodecContext* codec_context = avcodec_alloc_context3(codec);
    if (avcodec_parameters_to_context(codec_context, codec_parameters) < 0) {
        std::cerr << "Could not create codec context." << std::endl;
        return EXIT_FAILURE;
    }
    if (avcodec_open2(codec_context, codec, nullptr) < 0) {
        std::cerr << "Could not open codec." << std::endl;
        return EXIT_FAILURE;
    }
    // 初始化视频帧
    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        std::cerr << "Could not allocate frame." << std::endl;
        return EXIT_FAILURE;
    }
    // 初始化视频帧RGB格式
    AVFrame* frame_rgb = av_frame_alloc();
    if (!frame_rgb) {
        std::cerr << "Could not allocate frame." << std::endl;
        return EXIT_FAILURE;
    }
    int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, output_width, output_height, 1);
    uint8_t* buffer = (uint8_t*)av_malloc(num_bytes * sizeof(uint8_t));
    av_image_fill_arrays(frame_rgb->data, frame_rgb->linesize, buffer, AV_PIX_FMT_RGB24, output_width, output_height, 1);
    // 初始化图像转换器
    SwsContext* sws_context = sws_getContext(codec_context->width, codec_context->height, codec_context->pix_fmt, output_width, output_height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
    // 读取视频帧
    AVPacket packet;
    av_init_packet(&packet);
    while (av_read_frame(input_format_context, &packet) >= 0) {
        if (packet.stream_index == video_stream_index) {
            // 解码视频帧
            int ret = avcodec_send_packet(codec_context, &packet);
            if (ret < 0) {
                std::cerr << "Error sending a packet for decoding." << std::endl;
                av_packet_unref(&packet);
                continue;
            }
            while (ret >= 0) {
                ret = avcodec_receive_frame(codec_context, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                }
                else if (ret < 0) {
                    std::cerr << "Error during decoding." << std::endl;
                    return EXIT_FAILURE;
                }
                // 转换图像格式
                sws_scale(sws_context, (const uint8_t* const*)frame->data, frame->linesize, 0, codec_context->height, frame_rgb->data, frame_rgb->linesize);
                // 将图像数据复制到OpenCV的Mat中
                cv::Mat mat(output_height, output_width, CV_8UC3, frame_rgb->data[0]);
                // 显示图像
                cv::imwrite("output.jpg", mat, {cv::IMWRITE_JPEG_QUALITY, 80});
                // cv::imshow("Video", mat);
                // cv::waitKey(1);
            }
            av_packet_unref(&packet);
        }
    }
    // 释放资源
    av_free(buffer);
    av_frame_free(&frame_rgb);
    av_frame_free(&frame);
    avcodec_close(codec_context);
    avformat_close_input(&input_format_context);
    avformat_network_deinit();
    return EXIT_SUCCESS;
}

对应cmakelist:

cmake_minimum_required(VERSION 3.0)
project(pro)
add_definitions(-std=c++11)
option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_BUILD_TYPE Debug)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/workspace)
set(ffmpeg_libs_DIR /usr/local/lib)
set(ffmpeg_headers_DIR /usr/local/include)
set(OpenCV_DIR   "/usr/local/opencv/lib64/cmake/opencv4")

find_package(OpenCV)
include_directories(
    ${PROJECT_SOURCE_DIR}/src
    ${OpenCV_INCLUDE_DIRS}
    ${ffmpeg_headers_DIR} 
)
link_directories(${OpenCV_LIBRARY_DIRS} ${ffmpeg_libs_DIR} )
set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -O0 -Wfatal-errors -pthread -w -g")
#递归地添加的相关文件
file(GLOB_RECURSE cpp_srcs ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB_RECURSE c_srcs ${PROJECT_SOURCE_DIR}/src/*.c)
add_executable(pro ${cpp_srcs} ${c_srcs})


target_link_libraries( pro ${OpenCV_LIBS} ${ffmpeg_libs_DIR} avcodec.a avformat.a avutil.a swscale.a avfilter.a swresample.a z bz2 x264)

拉流效果:

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2023年8月17日
下一篇 2023年8月17日

相关推荐