N_QT

QT with librtp 开源库

解RTP包参考

把include, payload, source 加到QT

rtcp-interval.c 第6行#if defined(OS_WINDOWS) win环境的宏定义要改下

#include <QtGlobal>
#if defined(Q_OS_WIN)
#include <limits.h>
double drand48(void)
{
	unsigned int v = 0;
	rand_s(&v);
	return (v * 1.0) / UINT_MAX;
}
#endif

关于QT的<QtGlobal> 这是qt的定义操作系统宏

研究怎么用..

结构体 ctx形式

rtp-payload-internal.h 头文件, 定义了两个结构体,作为ctx

 
// 用作 编包
struct rtp_payload_encode_t
{
	/// create RTP packer
	/// @param[in] size maximum RTP packet payload size(don't include RTP header)
	/// @param[in] payload RTP header PT filed (see more about rtp-profile.h)
	/// @param[in] seq RTP header sequence number filed
	/// @param[in] ssrc RTP header SSRC filed
	/// @param[in] handler user-defined callback
	/// @param[in] cbparam user-defined parameter
	/// @return RTP packer
	void* (*create)(int size, uint8_t payload, uint16_t seq, uint32_t ssrc, struct rtp_payload_t *handler, void* cbparam);
	/// destroy RTP Packer
	void(*destroy)(void* packer);
 
	void(*get_info)(void* packer, uint16_t* seq, uint32_t* timestamp);
 
	/// PS/H.264 Elementary Stream to RTP Packet
	/// @param[in] packer
	/// @param[in] data stream data
	/// @param[in] bytes stream length in bytes
	/// @param[in] time stream UTC time
	/// @return 0-ok, ENOMEM-alloc failed, <0-failed
	int(*input)(void* packer, const void* data, int bytes, uint32_t time);
};
 
 
// 用作  解包
struct rtp_payload_decode_t
{
	void* (*create)(struct rtp_payload_t *handler, void* param);
	void (*destroy)(void* packer);
 
	/// RTP packet to PS/H.264 Elementary Stream
	/// @param[in] decoder RTP packet unpackers
	/// @param[in] packet RTP packet
	/// @param[in] bytes RTP packet length in bytes
	/// @param[in] time stream UTC time
	/// @return 1-packet handled, 0-packet discard, <0-failed
	int (*input)(void* decoder, const void* packet, int bytes);
};

下面还有预定义的函数返回 结构指针, 作为ctx

例如解h264 rtp 是: rtp_payload_decode_t *rtp_h264_decode()

 
 //void* rtp_payload_decode_create(int payload, const char* name, struct rtp_payload_t *handler, void* cbparam)
rtp_payload_decode_t *rtp_dec = rtp_h264_decode();
//回调
struct rtp_payload_t handler;
handler.alloc = rtp_alloc;
handler.free = rtp_free;
handler.packet = rtp_decode_packet;
 
struct  rtp_decode_h264_t *decoder= static_cast<struct  rtp_decode_h264_t *> (rtp_dec->create(&handler, nullptr ) );
 
for (int var = 0; var < 29; var++) {
    QString path = QString("F:/test/stream/farme%1.dat").arg(var);
    QFile frame(path);
    frame.open(QIODevice::ReadOnly);
    QByteArray array = frame.readAll();
    char* data = array.data();
    rtp_dec->input(decoder, data, array.size());
}
 
 
 
 
 

rtp_payload_t 结构体 参数的作用

struct rtp_payload_t *handler 参数的作用

-在create

struct rtp_decode_h264_t *unpacker;
unpacker = (struct rtp_decode_h264_t *)calloc(1, sizeof(*unpacker));
if(!unpacker)
    return NULL;
 
//把 rtp_payload_t handler 的所有内容 复制到 unpacker->handler ; 可以复制结构体
memcpy(&unpacker->handler, handler, sizeof(unpacker->handler));

关于 strcpy和memcpy strcpy和memcpy主要有以下3方面的区别; 1, 复制的内容不同; strcpy只能复制字符串, 而memcpy可以复制任意内容, 例如字符数组, 整型, 结构体, 类等; 2, 复制的方法不同; strcpy不需要指定长度, 它遇到被复制字符的串结束符”\0”才结束, 所以容易溢出; memcpy则是根据其第3个参数决定复制的长度; 3, 用途不同; 通常在复制字符串时用strcpy, 而需要复制其他类型数据时则一般用memcpy

另一个参数是回调附加参数.

  • 在input
...
default: // 1-23 NAL unit
    unpacker->handler.packet(unpacker->cbparam, (const uint8_t*)pkt.payload, pkt.payloadlen, pkt.rtp.timestamp, unpacker->flags);
    unpacker->flags = 0;
    unpacker->size = 0;

实例 解rtp为 h264

#include <QApplication>
#include <QTextCodec>
#include "mainwindow.h"
extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
//librtp
    #include "librtp/payload/rtp-payload-internal.h"
}
#include <QTime>
#include <QDebug>
#include <stdio.h>
#include<iostream>
///编包的两个回调 , 没用
static void* rtp_alloc(void* /*param*/, int bytes)
{
    qDebug()<<"rtp_alloc whf?";
}
static void rtp_free(void* /*param*/, void * /*packet*/)
{
    qDebug()<<"rtp_free whf?";
}
//测试 输出到文件
static QDataStream * g_out_stream = nullptr;
 
static void rtp_decode_packet(void* param, const void *packet, int bytes, uint32_t timestamp, int flags)
{
    static const uint8_t start_code[4] = { 0, 0, 0, 1 };
    //最后结果 包括 h264 开始码
    uint8_t buffer[1 * 1024 * 1024];//1M
    assert(bytes + 4 < sizeof(buffer));
    assert(0 == flags);
    size_t size = 0;
    memcpy(buffer, start_code, sizeof(start_code));
    size += sizeof(start_code);
    //
    memcpy(buffer + size, packet, bytes);
    size += bytes;
    g_out_stream->writeRawData((char *)buffer, size);
    qDebug()<<"rtp_decode_packet flags="<<flags<< ", size="<<size<<", de size"<<size-4;
}
 
int readRtpHeadSeq(char * pkt){
    int seq = 0;
   seq = pkt[2];
   seq = seq<<8|pkt[3];
   return seq;
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    QTextCodec *codec = QTextCodec::codecForName("UTF-8"); //设置程序 编码格式为UTF-8
    QTextCodec::setCodecForLocale(codec);
    MainWindow w;
    //输出到文件 测试
    QFile deFrame("F:/test/stream/deframe.dat");
    deFrame.open( QIODevice::WriteOnly);
    g_out_stream = new QDataStream(&deFrame);
    //void* rtp_payload_decode_create(int payload, const char* name, struct rtp_payload_t *handler, void* cbparam)
    rtp_payload_decode_t *rtp_dec = rtp_h264_decode();
    //回调
    struct rtp_payload_t handler;
    handler.alloc = rtp_alloc;
    handler.free = rtp_free;
    handler.packet = rtp_decode_packet;
    struct  rtp_decode_h264_t *decoder= static_cast<struct  rtp_decode_h264_t *> (rtp_dec->create(&handler, nullptr ) );
    int last_req = 0;
    for (int var = 0; var < 200; var++) {
        QString path = QString("F:/test/stream/farme%1.dat").arg(var);
        QFile frame(path);
        frame.open(QIODevice::ReadOnly);
        QByteArray array = frame.readAll();
        char* data = array.data();
        int req = readRtpHeadSeq(data);
        if(req!= last_req+1){
           qDebug()<<"fail excepted req: "<<last_req+1<<", actal: "<<req;
           continue;
        }
        int result =  rtp_dec->input(decoder, data, array.size());
        if(result == 1){
            last_req = req;
        }
        qDebug()<<"input pak "<<var<<", result "<<result;
    }
    w.show();
    return a.exec();
}

简而言之 就是回调处理, 解包之后回调 packet 方法; alloc, free 和都是编包的时候调用, packet 回调flags 参数参考rtp-payload-test.cpp rtp_decode_packet 方法; 貌似 unpackerflags==0是正确解包; input rtp包的seq 必须按顺序, 否则会被认为丢包, 回调packet的时候 flags == 1

注意, 海康SDK回调的rtp数据有些包的seq不是按顺序值,不知道什么鬼(音频?), 还需要处理.

librtp 源文件

过程函数 ctx形式

参考rtp-payload-test.cpp 的测试代码

/**
返回 RTP packet decoder ; struct rtp_payload_delegate_t* ctx; 返回作为ctx
 
/// Create RTP packet decoder
/// @param[in] payload RTP payload type, value: [0, 127] (see more about rtp-profile.h)
/// @param[in] name RTP payload name
/// @param[in] handler user-defined callback functions
/// @param[in] cbparam user-defined parameter
/// @return NULL-error, other-ok
*/
ctx.encoder = rtp_payload_decode_create(payload, encoding, seq, ssrc, &handler2, &ctx);
 
 
 
//再调用 解包
 
/// Decode RTP packet
/// @param[in] decoder RTP packet decoder(create by rtp_payload_decode_create)
/// @param[in] packet RTP packet, include rtp header(12 bytes)
/// @param[in] bytes RTP packet length in bytes
/// @return 1-packet handled, 0-packet discard, <0-failed
rtp_payload_decode_input(ctx.decoder, ctx.packet, ctx.size);
 
 
参考 rtp-payload-test.cpp

参考项目的 rtp-payload-test.cpp 测试代码

struct rtp_payload_t handler1;
handler1.alloc = rtp_alloc;
handler1.free = rtp_free;
handler1.packet = rtp_decode_packet;
ctx.decoder = rtp_payload_decode_create(payload, encoding, &handler1, &ctx);
 
 
 
while (1)
{
    uint8_t s2[2];
    if (2 != fread(s2, 1, 2, ctx.frtp))
        break;
 
    ctx.size = (s2[0] << 8) | s2[1];
    assert(ctx.size < sizeof(ctx.packet));
    if (ctx.size != (int)fread(ctx.packet, 1, ctx.size, ctx.frtp))
        break;
 
    rtp_payload_decode_input(ctx.decoder, ctx.packet, ctx.size);
}
 
 
//rtp_payload_test(96, "H264", 12686, 1957754144, "E:\\video\\rtp\\live555-test.h264.rtp", "E:\\video\\rtp\\live555-test.h264");