QT with librtp 开源库
把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.cpprtp_decode_packet 方法; 貌似 unpacker→flags==0是正确解包; input rtp包的seq 必须按顺序, 否则会被认为丢包, 回调packet的时候flags == 1
注意, 海康SDK回调的rtp数据有些包的seq不是按顺序值,不知道什么鬼(音频?), 还需要处理.
过程函数 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");