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

音视频系列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

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

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

发布了51 篇原创文章 · 获赞 52 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Hanghang_/article/details/104964143