QT开发中文网

实现基于Qt的RTP视频流音频流H.264编码的发送和接收

在Qt中,可以使用FFmpeg库和Live555库来实现基于RTP协议的H.264编码视频流和音频流的发送和接收。下面分别介绍如何实现。

发送端

准备工作

首先需要下载安装Live555库,并将其添加到Qt项目中。具体步骤如下:

  1. 下载最新版本的Live555源代码
  2. 解压缩后,进入目录live,执行命令./genMakefiles linux-clang生成Makefile文件;
  3. 执行命令make进行编译;
  4. 在Qt项目中添加头文件路径、库文件路径和链接库即可。

除此之外,还需要在Qt项目中引入FFmpeg库,方法同上。

实现过程

接下来是一个简单的示例代码,用于将本地摄像头捕获的视频流进行H.264编码,并通过RTP协议进行发送:

#include <QCoreApplication>
#include <QDebug>
#include <QTimer>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"

#define VIDEO_PAYLOAD_TYPE 96
#define AUDIO_PAYLOAD_TYPE 97

class Sender {
public:
    Sender() : env_(nullptr), task_(nullptr), mediaSession_(nullptr),
               videoEncoderCtx_(nullptr), audioEncoderCtx_(nullptr) {}

    ~Sender() { deleteLater(); }

    void init(const QString& destIpAddress, quint16 destPort) {
        env_ = BasicUsageEnvironment::createNew();
        task_ = EventLoopTask::createNew(*env_);

        // 创建视频编码器上下文
        videoEncoderCtx_ = avcodec_alloc_context3(nullptr);
        videoEncoderCtx_->codec_id = AV_CODEC_ID_H264;
        videoEncoderCtx_->bit_rate = 5000000;
        videoEncoderCtx_->width = 640;
        videoEncoderCtx_->height = 480;
        videoEncoderCtx_->time_base.num = 1;
        videoEncoderCtx_->time_base.den = 30;

        AVCodec *videoCodec = avcodec_find_encoder(videoEncoderCtx_->codec_id);
        if (!videoCodec) {
            qDebug() << "Failed to find H.264 encoder.";
            return;
        }

        if (avcodec_open2(videoEncoderCtx_, videoCodec, nullptr) < 0) {
            qDebug() << "Failed to open H.264 encoder.";
            return;
        }

        // 创建音频编码器上下文
        audioEncoderCtx_ = avcodec_alloc_context3(nullptr);
        audioEncoderCtx_->sample_fmt = AV_SAMPLE_FMT_S16P;
        audioEncoderCtx_->sample_rate = 44100;
        audioEncoderCtx_->channels = 2;

        AVCodec *audioCodec = avcodec_find_encoder_by_name("pcm_s16le");
        if (!audioCodec) {
            qDebug() << "Failed to find PCM audio encoder.";
            return;
        }

         if (avcodec_open2(audioEncoderCtx_, audioCodec, nullptr) < 0) {
             qDebug() << "Failed to open PCM audio encoder.";
             return;
         }

         // 创建媒体会话和媒体流
         mediaSession_ =
                 MediaSession::createNew(*env_, "live555Session", "Live streaming session");
         MediaStream *videoStream = MediaStream::createNew(*env_, videoEncoderCtx_);
         MediaStream *audioStream = MediaStream::createNew(*env_, audioEncoderCtx_);

         mediaSession_->addSubsession(videoStream->newClone());
         mediaSession_->addSubsession(audioStream->newClone());

         // 创建RTP发送器
         RTPSink *videoSink =
                 H264VideoRTPSink::createNew(*env_, &rtpGroupsock_,
                                            VIDEO_PAYLOAD_TYPE, 90000, destIpAddress.toUtf8().constData(),
                                            destPort);
        RTPSink *audioSink =
                SimpleRTPSink::createNew(*env_, &rtpGroupsock_,
                                         AUDIO_PAYLOAD_TYPE, 44100,
                                         destIpAddress.toUtf8().constData(), destPort+2);

        videoStream->addDestination(videoSink);
        audioStream->addDestination(audioSink);
    }

    void start() {
        task_->scheduleDelayedTask(0, (TaskFunc*)Sender::startSending, this);
        env_->taskScheduler().doEventLoop(&eventLoopWatchVariable_);
    }

private:
    static void startSending(Sender* sender) {
        AVFrame *frame = av_frame_alloc();
        uint8_t *frameBuffer = new uint8_t[640 * 480 * 3 / 2];
        uint8_t **framePlane = { frameBuffer, frameBuffer + 640*480,
                                 frameBuffer + 640*480*5/4 };
        
        QTimer timer;
        timer.setInterval(33); // 每秒30帧
        QObject::connect(&timer, &QTimer::timeout, [sender, frame]() {
            // 捕获一帧视频帧和一段音频数据
            QImage image = QApplication::primaryScreen()->grabWindow(0).toImage();
            avpicture_fill((AVPicture*)frame, image.bits(), AV_PIX_FMT_RGB32,
                           image.width(), image.height());

            QByteArray audioData;
            QAudioFormat format;
            format.setSampleRate(44100);
            format.setChannelCount(2);
            format.setSampleSize(16);
            format.setCodec("audio/pcm");
            format.setByteOrder(QAudioFormat::LittleEndian);
            format.setSampleType(QAudioFormat::SignedInt);

            QIODevice *device = QAudioDeviceInfo::defaultOutputDevice().start();
            
            if (device && device->isOpen()) {
                QAudioOutput output(format);
                output.start(device);

                // 生成一段5秒钟的音频数据
                qint64 numSamples = 44100 * 5;
                qint16 sampleValue = 32767 / 3;

                for (int i = 0; i < numSamples; ++i)
                    audioData.append(reinterpret_cast<const char*>(&sampleValue), sizeof(sampleValue));

                output.write(audioData.data(), audioData.size());
                
                while (output.state() == QAudio::ActiveState) {
                    if (output.bytesFree() < audioData.size())
                        continue;

                    output.write(audioData.data(), audioData.size());
                    break;
                }

                delete device;
            }

            // 视频编码
            int ret = avcodec_send_frame(sender->videoEncoderCtx_, frame);
            
            while (ret >= 0) {
                AVPacket pkt;
                av_init_packet(&pkt);

                ret = avcodec_receive_packet(sender->videoEncoderCtx_, &pkt);

                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;

                pkt.pts = pkt.dts = av_rescale_q(pkt.pts, sender->videoEncoderCtx_->time_base,
                                                 {1,90000});
                pkt.duration = av_rescale_q(pkt.duration, sender->videoEncoderCtx_->time_base,
                                            {1,90000});

                // 发送RTP数据包
                RTPSink::sendPacket(sender->mediaSession_, &pkt);

                av_packet_unref(&pkt);
            }

            delete frame;
        });

        timer.start();
    }

    void deleteLater() {
        delete videoEncoderCtx_;
        delete audioEncoderCtx_;

        Medium::close(mediaSession_);
        BasicTaskScheduler::deleteBasicTaskScheduler(env_);
    }

private:
    TaskScheduler *env_;
    EventLoopTask *task_;
    MediaSession *mediaSession_;
    Groupsock rtpGroupsock_;

    AVCodecContext *videoEncoderCtx_;
    AVCodecContext *audioEncoderCtx_;

private:
    int eventLoopWatchVariable_{0};
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Sender sender;
    sender.init("192.168.1.100", 5000);
    sender.start();

    return a.exec();
}

这个例子中,首先创建了一个Sender类,用于将本地摄像头捕获的视频流进行H.264编码,并通过RTP协议进行发送。在初始化时,需要指定目标IP地址和端口号。

接着,创建视频编码器上下文和音频编码器上下文,并分别设置一些参数,如编码方式、码率、分辨率等。然后,创建媒体会话和媒体流,并添加到会话中。

接下来是关键步骤:使用Live555库提供的RTP发送器,将视频流和音频流发送出去。创建RTP发送器需要指定负载类型、时间戳等参数,并将其添加到对应的媒体流中。

最后,在一个定时器回调函数中,捕获一帧视频帧和一段音频数据,并将其进行编码。编码完成后,将数据封装成RTP包,并使用RTPSink::sendPacket()方法进行发送。需要注意的是,在发送之前要先设置好时间戳等参数。

接收端

准备工作

接收端同样也需要引入Live555库和FFmpeg库。

实现过程

下面是一个简单的示例代码,用于从网络上接收并解码H.264编码视频流和音频流:

”`cpp #include #include

extern “C” { #include #include }

#include “liveMedia.hh” #include “BasicUsageEnvironment.hh” #include “GroupsockHelper.hh”

class Receiver : public MediaSession::SubsessionIterator { public:

Receiver() : env_(nullptr), task_(nullptr), mediaSession_(nullptr),
             videoDecoderCtx_(nullptr), audioDecoderCtx_(nullptr) {}

~Receiver() { deleteLater(); }

void init(const QString& srcIpAddress, quint16 srcPort) {
    env_ = BasicUsageEnvironment::createNew();
    task_ = EventLoopTask::createNew(*env_);

    // 打开网络连接
    const char *urlFmt = "rtp://%s:%d";
    char url[1024];
    sprintf(url, urlFmt, srcIpAddress.toUtf8().constData(), srcPort);
    mediaSession_ = MediaSession::createNew(*env_, url);

    if (!mediaSession_->hasSubsessions()) {
        qDebug
目录导航
  • 发送端
    • 准备工作
    • 实现过程
  • 接收端
    • 准备工作
    • 实现过程
  • 本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
    转载请注明出处:https://qt.0voice.com/?id=1722

    分享:
    扫描分享到社交APP
    上一篇
    下一篇
    发表列表
    游客 游客
    此处应有掌声~
    评论列表

    还没有评论,快来说点什么吧~

    联系我们

    在线咨询: 点击这里给我发消息

    微信号:3007537140

    上班时间: 10:30-22:30

    关注我们
    x

    注册

    已经有帐号?