音视频系列5:ffmpeg拉流并引入ROS库

6 篇文章 11 订阅

音视频系列5:ffmpeg拉流并引入ROS系统

前言

音视频系列博客:
音视频系列1:ffmpeg+rtmp拉流
音视频系列2:ffmpeg将H.264解码为RGB
音视频系列3:使用ffmpeg + nginx搭建本地转发服务器
音视频系列4:新手如何入门ffmpeg(以FLV解码H.264为例)

有兴趣的小伙伴们可以看看。

上节我们更新了ffmpeg拉流中过时的API,本节将对上节的代码进行封装,并引入ROS包,使得ffmpeg拉来的流可以通过ROS系统进行传输,伸手党可以直接拉到最下面,附github代码。
在这里插入图片描述

封装

原本只有一个main.cpp,现在我们定义一个Transdata类。
所以现在扩展成三个文件:
transdata.cpp
transdata.h
Trans_node.cpp

其中,transdata.cpp里写明类的函数的具体过程,transdata.h用于定义函数名,Trans_node.cpp用于实例化Transdata类,并调用其函数。

Trans_node.cpp:

#include <iostream>
#include "transdata.h"

Transdata transdata;
using namespace std;
int main(int argc, char** argv)
{
    if(transdata.Transdata_init() < 0)
    {
        cout <<"init error !" << endl;
        return -1;
    }
    while(1)
    {
       transdata.Transdata_Recdata();
    }
    transdata.Transdata_free();
    return 0;
}

transdata.cpp 里定义了几个重要的函数,分别是:

初始化:Transdata_init()


int Transdata::Transdata_init() {
    //Register
    av_register_all();
    //Network
    avformat_network_init();
    //Input
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf("Could not open input file.");
        return -1;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        return -1;
    }
    videoindex = -1;
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoindex = i;
            codecpar = ifmt_ctx->streams[i]->codecpar;
        }
    }
    //Find H.264 Decoder
    pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (pCodec == NULL) {
        printf("Couldn't find Codec.\n");
        return -1;
    }
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Couldn't open codec.\n");
        return -1;
    }
    pframe = av_frame_alloc();
    if (!pframe) {
        printf("Could not allocate video frame\n");
        exit(1);
    }
    buffersrc = av_bsf_get_by_name("h264_mp4toannexb");

    if(av_bsf_alloc(buffersrc, &bsf_ctx) < 0)
        return -1;
    if(avcodec_parameters_copy(bsf_ctx->par_in,codecpar) < 0)
        return -1;
    if(av_bsf_init(bsf_ctx) < 0)
        return -1;
}

拉流:Transdata_Recdata()

int Transdata::Transdata_Recdata()
{
    if(av_read_frame(ifmt_ctx, &pkt)<0) {
        return -1;
    }
    if (pkt.stream_index == videoindex) {
        // H.264 Filter
        if(av_bsf_send_packet(bsf_ctx, &pkt) < 0)
        {
            cout << " bsg_send_packet is error! " << endl;
            return -1;
        }
        if(av_bsf_receive_packet(bsf_ctx, &pkt) < 0)
        {
            cout << " bsg_receive_packet is error! " << endl;
            return -1;
        }
        printf("Write Video Packet. size:%d\tpts:%ld\n", pkt.size, pkt.pts);
        // Decode AVPacket
        if(pkt.size)
        {
            ret = avcodec_send_packet(pCodecCtx, &pkt);
            if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                std::cout << "avcodec_send_packet: " << ret << std::endl;
                return -1;
            }
            //Get AVframe
            ret = avcodec_receive_frame(pCodecCtx, pframe);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                std::cout << "avcodec_receive_frame: " << ret << std::endl;
                return -1;
            }
            //AVframe to rgb

            AVFrame2Img(pframe,image_test);
        }
    }
    //Free AvPacket
    av_packet_unref(&pkt);
    return 0;
}

释放资源:Transdata_free()


int Transdata::Transdata_free()
{
    av_bsf_free(&bsf_ctx);
    avformat_close_input(&ifmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF)
    {
        printf( "Error occurred.\n");
        return -1;
    }
    return 0;
}

测试无问题,继续下一步,引入ROS

引入ROS库

要引入ROS库,可又不懂,那么推荐你去看ROS WIKI,上面介绍得很详细,并且有中文翻译,如果还觉得吃力,那么可以去看看古月居的ROS二十一讲。

,我也是刚接触到ROS,于是我先实现了一个小demo,就是定义两个node,一发一收传送字符串,建议对ROS不熟悉的小伙子们可以先按照我的步骤去实现一遍,可以帮助熟悉ROS的编程。
相关的教程在此:
编写简单的消息发布器和订阅器 (C++)
测试消息发布器和订阅器

写完测试能用之后,进而把刚刚我们封装好的代码拉进来,这里具体的步骤较多,就不详细说了,等移进来调用没问题之后,就可以考虑考虑如何发送图像数据了。

还是直接上官网的教程:
Writing a Simple Image Publisher (C++)
Writing a Simple Image Subscriber (C++)

需要注意的是,如果直接引入cv_bridge包或者是image_transport包,是会报错的,你还要在CMakeLists.txt和package.xml进行修改。

修改CMakeLists.txt:增加库名

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  cv_bridge
  image_transport
)

修改package.xml: 增加以下内容

  <build_depend>image_transport</build_depend>
  <exec_depend>image_transport</exec_depend>
  <build_depend>cv_bridge</build_depend>
  <exec_depend>cv_bridge</exec_depend>

这样才可以调用。

另外,因为你要对图像数据进行操作(传送到message里),所以你还得使用互斥锁,保证数据的安全。

好了,话不多说,直接上源码。
https://github.com/Hectoor/Ffmpeg_ros_image_trans

测试图: 一发一收
在这里插入图片描述

如果我的文章对你有帮助,欢迎点赞、评论、关注。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++是一种通用的编程语言,泛应用于软件开发领域。它具有高效、灵活和可扩展的特性,被广泛用于开发各种类型的应用程序,包括嵌入式系统、桌面应用、游戏开发等。 FFmpeg是一个开源的跨平台音视频处理工具,它提供了一组丰富的功能和接口,可以用于音视频的编解码、转码、流媒体处理等。在C++中使用FFmpeg进行拉流并存储到本地的例子如下: ```cpp #include <iostream> extern "C" { #include <libavformat/avformat.h> } int main() { av_register_all(); AVFormatContext* formatContext = nullptr; AVPacket packet; // 打开输入文件 if (avformat_open_input(&formatContext, "input_url", nullptr, nullptr) != 0) { std::cout << "无法打开输入文件" << std::endl; return -1; } // 查找流信息 if (avformat_find_stream_info(formatContext, nullptr) < 0) { std::cout << "无法获取流信息" << std::endl; return -1; } // 打开输出文件 FILE* outputFile = fopen("output_file", "wb"); if (!outputFile) { std::cout << "无法打开输出文件" << std::endl; return -1; } // 遍历所有流 for (int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { AVCodecParameters* codecParameters = formatContext->streams[i]->codecpar; // 可以在这里进行视频流的处理 } else if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { AVCodecParameters* codecParameters = formatContext->streams[i]->codecpar; // 可以在这里进行音频流的处理 } } // 读取数据包并写入输出文件 while (av_read_frame(formatContext, &packet) >= 0) { fwrite(packet.data, 1, packet.size, outputFile); av_packet_unref(&packet); } // 关闭文件和释放资源 fclose(outputFile); avformat_close_input(&formatContext); return 0; } ``` 这个例子演示了如何使用FFmpeg进行拉流并将数据存储到本地文件中。你可以根据自己的需求对视频流和音频流进行处理。请注意,你需要先安装FFmpeg,并在编译时链接相应的文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值