615 lines
18 KiB
C++
615 lines
18 KiB
C++
#include "rtsp-demuxer.h"
|
|
#include "rtp-demuxer.h"
|
|
#include "rtp-profile.h"
|
|
#include "rtcp-header.h"
|
|
#include "sdp-a-fmtp.h"
|
|
#include "mpeg-ps.h"
|
|
#include "mpeg-ts.h"
|
|
#include "avbsf.h"
|
|
#include "avpbs.h" // https://github.com/ireader/avcodec #avbsf/include
|
|
#include "mpeg4-aac.h"
|
|
#include "rtsp-payloads.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#define RTSP_STREAM_MAX 4
|
|
#define RTSP_PAYLOAD_MAX 8
|
|
#define RTSP_PAYLOAD_MAX_SIZE (10 * 1024 * 1024)
|
|
|
|
#if defined(OS_WINDOWS)
|
|
#if !defined(strcasecmp)
|
|
#define strcasecmp _stricmp
|
|
#endif
|
|
#endif
|
|
|
|
struct rtsp_demuxer_t;
|
|
struct rtp_payload_info_t
|
|
{
|
|
struct rtsp_demuxer_t* ctx;
|
|
|
|
int frequency;
|
|
int payload;
|
|
char encoding[64];
|
|
|
|
uint8_t* extra;
|
|
int extra_bytes;
|
|
|
|
uint16_t seq; // RTP-Info seq
|
|
uint32_t base; // RTP-Info timestamp
|
|
|
|
uint32_t last; // last rtp packet timestamp
|
|
int64_t timestamp; // rtp timestamp extend to 64bit
|
|
int64_t pts; // last mapped rtp packet timestamp
|
|
|
|
struct rtp_demuxer_t* rtp;
|
|
void* ts, * ps; // only one
|
|
|
|
union
|
|
{
|
|
struct sdp_a_fmtp_h264_t h264;
|
|
struct sdp_a_fmtp_h265_t h265;
|
|
struct sdp_a_fmtp_h266_t h266;
|
|
struct sdp_a_fmtp_mpeg4_t mpeg4;
|
|
|
|
struct mpeg4_aac_t aac;
|
|
} fmtp;
|
|
|
|
struct avpbs_t* bs;
|
|
void* filter;
|
|
|
|
// 264/265 only
|
|
struct avbsf_t* avbsf;
|
|
void* h2645;
|
|
|
|
// ps/ts only
|
|
struct
|
|
{
|
|
AVPACKET_CODEC_ID codec;
|
|
struct avpbs_t* bs;
|
|
void* filter;
|
|
int pid; // ts/ps only
|
|
} tracks[RTSP_STREAM_MAX];
|
|
|
|
// ps only
|
|
struct {
|
|
uint8_t* ptr;
|
|
int len, cap;
|
|
} ptr;
|
|
};
|
|
|
|
struct rtsp_demuxer_t
|
|
{
|
|
void* param;
|
|
rtsp_demuxer_onpacket onpacket;
|
|
|
|
int stream;
|
|
int jitter;
|
|
struct rtp_payload_info_t pt[RTSP_PAYLOAD_MAX];
|
|
int count; // payload type count
|
|
int idx; // last payload index
|
|
|
|
uint8_t ptr[8 * 1024];
|
|
int off;
|
|
};
|
|
|
|
int sdp_aac_latm_load(uint8_t* data, int bytes, const char* config);
|
|
int sdp_aac_mpeg4_load(uint8_t* data, int bytes, const char* config);
|
|
int sdp_h264_load(uint8_t* data, int bytes, const char* config);
|
|
int sdp_h265_load(uint8_t* data, int bytes, const char* vps, const char* sps, const char* pps, const char* sei);
|
|
int sdp_h266_load(uint8_t* data, int bytes, const char* vps, const char* sps, const char* pps, const char* sei, const char* dci);
|
|
|
|
static int rtsp_demuxer_avpbs_onpacket(void* param, struct avpacket_t* pkt)
|
|
{
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
return pt->ctx->onpacket(pt->ctx->param, pkt);
|
|
}
|
|
|
|
static int rtsp_demuxer_mpeg2_fetch_stream(struct rtp_payload_info_t* pt, int pid, int codecid)
|
|
{
|
|
int i, mpeg2;
|
|
|
|
mpeg2 = avpayload_find_by_mpeg2((uint8_t)codecid);
|
|
if (mpeg2 < 0)
|
|
return -EPROTONOSUPPORT;
|
|
|
|
for (i = 0; i < sizeof(pt->tracks) / sizeof(pt->tracks[0]); i++)
|
|
{
|
|
if (0 == pt->tracks[i].pid || pt->tracks[i].pid == pid)
|
|
break;
|
|
}
|
|
|
|
if (i >= sizeof(pt->tracks) / sizeof(pt->tracks[0]))
|
|
return -E2BIG;
|
|
|
|
if(pt->tracks[i].codec != s_payloads[mpeg2].codecid && pt->tracks[i].bs && pt->tracks[i].filter)
|
|
pt->tracks[i].bs->destroy(&pt->tracks[i].filter);
|
|
|
|
if (!pt->tracks[i].bs || !pt->tracks[i].filter)
|
|
{
|
|
pt->tracks[i].codec = s_payloads[mpeg2].codecid;
|
|
pt->tracks[i].bs = avpbs_find(s_payloads[mpeg2].codecid);
|
|
if(pt->tracks[i].bs)
|
|
pt->tracks[i].filter = pt->tracks[i].bs->create(i, s_payloads[mpeg2].codecid, NULL, 0, rtsp_demuxer_avpbs_onpacket, pt);
|
|
}
|
|
|
|
pt->tracks[i].pid = pid;
|
|
return pt->tracks[i].filter ? i : -1;
|
|
}
|
|
|
|
static inline int rtsp_demuxer_mpegts_onpacket(void* param, int program, int track, int codecid, int flags, int64_t pts, int64_t dts, const void* data, size_t bytes)
|
|
{
|
|
int i;
|
|
struct rtp_payload_info_t* pt;
|
|
|
|
#if defined(FIX_DAHUA_AAC_FROM_G711)
|
|
if ((codecid == PSI_STREAM_AUDIO_G711A || codecid == PSI_STREAM_AUDIO_G711U)
|
|
&& bytes > 7 && 0xFF == ((uint8_t*)data)[0] && 0xF0 == (((uint8_t*)data)[1] & 0xF0))
|
|
{
|
|
int n = 7; // fix n = 0
|
|
for (i = 0; i + 7 < bytes && n >= 7; i += n)
|
|
n = mpeg4_aac_adts_frame_length((const uint8_t*)data + i, bytes - i);
|
|
codecid = i == bytes ? PSI_STREAM_AAC : codecid; // fix it
|
|
}
|
|
#endif
|
|
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
i = rtsp_demuxer_mpeg2_fetch_stream(pt, track, codecid);
|
|
if (i >= 0)
|
|
{
|
|
flags = flags ? AVPACKET_FLAG_KEY : 0;
|
|
return pt->tracks[i].bs->input(pt->tracks[i].filter, pts / 90, dts / 90, (const uint8_t*)data, (int)bytes, flags);
|
|
}
|
|
|
|
if (0xbd == codecid || 0 == codecid)
|
|
return 0; // ignore HIK private stream
|
|
|
|
(void)program; //ignore
|
|
assert(0);
|
|
return -1; // discard
|
|
}
|
|
|
|
static inline int rtsp_demuxer_mpegps_onpacket(void* param, int track, int codecid, int flags, int64_t pts, int64_t dts, const void* data, size_t bytes)
|
|
{
|
|
//struct rtp_payload_info_t* pt;
|
|
//pt = (struct rtp_payload_info_t*)param;
|
|
(void)param;
|
|
return rtsp_demuxer_mpegts_onpacket(param, 0, track, codecid, flags, pts, dts, data, bytes);
|
|
}
|
|
|
|
static inline int rtsp_demuxer_ontspacket(void* param, const void* packet, int bytes, uint32_t timestamp, int flags)
|
|
{
|
|
int r;
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
|
|
r = 0;
|
|
while (0 == r && bytes >= 188)
|
|
{
|
|
r = (int)ts_demuxer_input(pt->ts, packet, 188);
|
|
assert(0 == r);
|
|
bytes -= 188;
|
|
packet = (const uint8_t*)packet + 188;
|
|
}
|
|
(void)timestamp, (void)flags; //ignore
|
|
|
|
return r;
|
|
}
|
|
|
|
static inline int rtsp_demuxer_merge_ps_buffer(struct rtp_payload_info_t* pt, const uint8_t* packet, int bytes)
|
|
{
|
|
int n;
|
|
uint8_t* ptr;
|
|
if (bytes < 0)
|
|
return -EINVAL;
|
|
|
|
if (pt->ptr.len + bytes > pt->ptr.cap)
|
|
{
|
|
if (pt->ptr.len + bytes > RTSP_PAYLOAD_MAX_SIZE)
|
|
{
|
|
pt->ptr.len = 0; // clear
|
|
return -E2BIG;
|
|
}
|
|
|
|
n = pt->ptr.len + bytes + 64 * 1024;
|
|
ptr = (uint8_t*)realloc(pt->ptr.ptr, n);
|
|
if (!ptr)
|
|
{
|
|
pt->ptr.len = 0; // clear
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pt->ptr.ptr = ptr;
|
|
pt->ptr.cap = n;
|
|
}
|
|
|
|
if (bytes > 0)
|
|
{
|
|
memmove(pt->ptr.ptr + pt->ptr.len, packet, bytes);
|
|
pt->ptr.len += bytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int rtsp_demuxer_onpspacket(void* param, const void* packet, int bytes, uint32_t timestamp, int flags)
|
|
{
|
|
int r, n;
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
#if 1
|
|
if (pt->ptr.len > 0)
|
|
{
|
|
r = rtsp_demuxer_merge_ps_buffer(pt, packet, bytes);
|
|
if (0 != r)
|
|
return r;
|
|
|
|
packet = pt->ptr.ptr;
|
|
bytes = pt->ptr.len;
|
|
pt->ptr.len = 0;
|
|
}
|
|
|
|
n = (int)ps_demuxer_input(pt->ps, packet, bytes);
|
|
assert(n <= bytes);
|
|
if (n >= 0 && n < bytes)
|
|
r = rtsp_demuxer_merge_ps_buffer(pt, (const uint8_t*)packet + n, bytes - n);
|
|
else
|
|
r = n < 0 ? n : 0;
|
|
|
|
(void)timestamp, (void)flags; //ignore
|
|
return r;
|
|
#else
|
|
if (!pt->tracks[0].bs || !pt->tracks[0].filter)
|
|
{
|
|
pt->tracks[0].codec = AVCODEC_DATA_MP2P;
|
|
pt->tracks[0].bs = avpbs_find(AVCODEC_DATA_MP2P);
|
|
if (pt->tracks[0].bs)
|
|
pt->tracks[0].filter = pt->tracks[0].bs->create(0, AVCODEC_DATA_MP2P, NULL, 0, rtsp_demuxer_avpbs_onpacket, pt);
|
|
}
|
|
|
|
// RTP timestamp => PTS/DTS
|
|
if (0 == pt->last && INT64_MIN == pt->pts)
|
|
pt->timestamp = timestamp;
|
|
else
|
|
pt->timestamp += (int32_t)(timestamp - pt->last);
|
|
pt->last = timestamp;
|
|
pt->pts = pt->timestamp * 1000 / pt->frequency;
|
|
|
|
flags = flags ? AVPACKET_FLAG_KEY : 0;
|
|
return pt->tracks[0].bs->input(pt->tracks[0].filter, pt->pts, pt->pts, (const uint8_t*)packet, (int)bytes, flags);
|
|
#endif
|
|
}
|
|
|
|
static inline int rtsp_demuxer_onrtppacket(void* param, const void* data, int bytes, uint32_t timestamp, int flags)
|
|
{
|
|
int r;
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
|
|
// RTP timestamp => PTS/DTS
|
|
if (0 == pt->last && INT64_MIN == pt->pts)
|
|
pt->timestamp = timestamp;
|
|
else
|
|
pt->timestamp += (int32_t)(timestamp - pt->last);
|
|
pt->last = timestamp;
|
|
pt->pts = pt->timestamp * 1000 / pt->frequency;
|
|
|
|
r = -1;
|
|
if (pt->bs && pt->filter)
|
|
{
|
|
//flags = 0;
|
|
r = pt->bs->input(pt->filter, pt->pts, pt->pts, (const uint8_t*)data, bytes, flags);
|
|
}
|
|
//else
|
|
// r = pt->ctx->onpacket(pt->ctx->param, 0, pt->payload, pt->encoding, pt->pts, pt->pts, data, bytes, flags);
|
|
|
|
return r;
|
|
}
|
|
|
|
static inline int rtsp_demuxer_onh2645nalu(void* param, const void* data, int bytes, uint32_t timestamp, int flags)
|
|
{
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
|
|
// RTP timestamp => PTS/DTS
|
|
if (0 == pt->last && INT64_MIN == pt->pts)
|
|
pt->timestamp = timestamp;
|
|
else
|
|
pt->timestamp += (int32_t)(timestamp - pt->last);
|
|
pt->last = timestamp;
|
|
pt->pts = pt->timestamp * 1000 / pt->frequency;
|
|
|
|
assert(pt->avbsf && pt->h2645);
|
|
if (pt->avbsf && pt->h2645)
|
|
{
|
|
//flags = 0;
|
|
return pt->avbsf->input(pt->h2645, pt->pts, pt->pts, (const uint8_t*)data, bytes);
|
|
}
|
|
|
|
(void)flags; // ignore
|
|
return -1;
|
|
}
|
|
|
|
static int rtsp_demuxer_onh2645packet(void* param, int64_t pts, int64_t dts, const uint8_t* data, int bytes, int flags)
|
|
{
|
|
struct rtp_payload_info_t* pt;
|
|
pt = (struct rtp_payload_info_t*)param;
|
|
|
|
if (pt->bs && pt->filter)
|
|
{
|
|
//flags = 0;
|
|
return pt->bs->input(pt->filter, pts, dts, (const uint8_t*)data, bytes, flags);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int rtsp_demuxer_payload_close(struct rtp_payload_info_t* pt)
|
|
{
|
|
int i;
|
|
|
|
if (pt->ptr.ptr)
|
|
{
|
|
assert(pt->ptr.cap > 0);
|
|
free(pt->ptr.ptr);
|
|
pt->ptr.ptr = NULL;
|
|
}
|
|
|
|
for (i = 0; i < sizeof(pt->tracks) / sizeof(pt->tracks[0]); i++)
|
|
{
|
|
if (pt->tracks[i].bs && pt->tracks[i].filter)
|
|
{
|
|
pt->tracks[i].bs->destroy(&pt->tracks[i].filter);
|
|
pt->tracks[i].filter = NULL;
|
|
pt->tracks[i].bs = NULL;
|
|
pt->tracks[i].pid = 0;
|
|
}
|
|
}
|
|
|
|
if (pt->bs && pt->filter)
|
|
{
|
|
pt->bs->destroy(&pt->filter);
|
|
pt->bs = NULL;
|
|
}
|
|
|
|
if (pt->avbsf && pt->h2645)
|
|
{
|
|
pt->avbsf->destroy(&pt->h2645);
|
|
pt->avbsf = NULL;
|
|
}
|
|
|
|
if (pt->ts)
|
|
{
|
|
ts_demuxer_destroy(pt->ts);
|
|
pt->ts = NULL;
|
|
}
|
|
|
|
if (pt->ps)
|
|
{
|
|
ps_demuxer_destroy(pt->ps);
|
|
pt->ps = NULL;
|
|
}
|
|
|
|
if (pt->rtp)
|
|
{
|
|
rtp_demuxer_destroy(&pt->rtp);
|
|
pt->rtp = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rtsp_demuxer_add_payload(struct rtsp_demuxer_t* demuxer, int frequency, int payload, const char* encoding, const char* fmtp)
|
|
{
|
|
int avp, len;
|
|
AVPACKET_CODEC_ID codec;
|
|
struct rtp_payload_info_t* pt;
|
|
int (*onpacket)(void* param, const void* data, int bytes, uint32_t timestamp, int flags);
|
|
|
|
if (demuxer->count >= sizeof(demuxer->pt) / sizeof(demuxer->pt[0]))
|
|
return -E2BIG; // too many payload type
|
|
|
|
avp = avpayload_find_by_rtp((uint8_t)payload, encoding);
|
|
// fixme: ffmpeg g711u sample rate 0
|
|
frequency = frequency ? frequency : (-1!=avp?s_payloads[avp].frequency:90000);
|
|
|
|
pt = &demuxer->pt[demuxer->count];
|
|
memset(pt, 0, sizeof(*pt));
|
|
snprintf(pt->encoding, sizeof(pt->encoding) - 1, "%s", encoding);
|
|
pt->frequency = frequency;
|
|
pt->payload = payload;
|
|
pt->extra = demuxer->ptr + demuxer->off;
|
|
pt->extra_bytes = 0;
|
|
pt->ctx = demuxer;
|
|
pt->pts = INT64_MIN;
|
|
|
|
if (RTP_PAYLOAD_MP2T == payload)
|
|
{
|
|
onpacket = rtsp_demuxer_ontspacket;
|
|
pt->ts = ts_demuxer_create(rtsp_demuxer_mpegts_onpacket, pt);
|
|
}
|
|
else if (0 == strcasecmp(encoding, "MP2P") || 0 == strcasecmp(encoding, "PS"))
|
|
{
|
|
onpacket = rtsp_demuxer_onpspacket;
|
|
pt->ps = ps_demuxer_create(rtsp_demuxer_mpegps_onpacket, pt);
|
|
}
|
|
else
|
|
{
|
|
onpacket = rtsp_demuxer_onrtppacket;
|
|
assert(demuxer->off <= sizeof(demuxer->ptr));
|
|
len = sizeof(demuxer->ptr) - demuxer->off;
|
|
codec = -1 != avp ? s_payloads[avp].codecid : AVCODEC_NONE;
|
|
switch (codec)
|
|
{
|
|
case AVCODEC_VIDEO_H264:
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_h264(fmtp, &payload, &pt->fmtp.h264))
|
|
pt->extra_bytes = sdp_h264_load(pt->extra, len, pt->fmtp.h264.sprop_parameter_sets);
|
|
pt->avbsf = avbsf_find(AVCODEC_VIDEO_H264);
|
|
pt->h2645 = pt->avbsf->create(pt->extra, pt->extra_bytes, rtsp_demuxer_onh2645packet, pt);
|
|
onpacket = rtsp_demuxer_onh2645nalu;
|
|
break;
|
|
|
|
case AVCODEC_VIDEO_H265:
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_h265(fmtp, &payload, &pt->fmtp.h265))
|
|
pt->extra_bytes = sdp_h265_load(pt->extra, len, pt->fmtp.h265.sprop_vps, pt->fmtp.h265.sprop_sps, pt->fmtp.h265.sprop_pps, pt->fmtp.h265.sprop_sei);
|
|
pt->avbsf = avbsf_find(AVCODEC_VIDEO_H265);
|
|
pt->h2645 = pt->avbsf->create(pt->extra, pt->extra_bytes, rtsp_demuxer_onh2645packet, pt);
|
|
onpacket = rtsp_demuxer_onh2645nalu;
|
|
break;
|
|
|
|
case AVCODEC_VIDEO_H266:
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_h266(fmtp, &payload, &pt->fmtp.h266))
|
|
pt->extra_bytes = sdp_h266_load(pt->extra, len, pt->fmtp.h266.sprop_vps, pt->fmtp.h266.sprop_sps, pt->fmtp.h266.sprop_pps, pt->fmtp.h266.sprop_sei, pt->fmtp.h266.sprop_dci);
|
|
pt->avbsf = avbsf_find(AVCODEC_VIDEO_H266);
|
|
pt->h2645 = pt->avbsf->create(pt->extra, pt->extra_bytes, rtsp_demuxer_onh2645packet, pt);
|
|
onpacket = rtsp_demuxer_onh2645nalu;
|
|
break;
|
|
|
|
case AVCODEC_AUDIO_AAC:
|
|
if (0 == strcasecmp(encoding, "MPEG4-GENERIC"))
|
|
{
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_mpeg4(fmtp, &payload, &pt->fmtp.mpeg4))
|
|
pt->extra_bytes = sdp_aac_mpeg4_load(pt->extra, len, pt->fmtp.mpeg4.config);
|
|
}
|
|
else if (0 == strcasecmp(encoding, "MP4A-LATM"))
|
|
{
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_mpeg4(fmtp, &payload, &pt->fmtp.mpeg4))
|
|
pt->extra_bytes = sdp_aac_latm_load(pt->extra, len, pt->fmtp.mpeg4.config);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
break;
|
|
|
|
case AVCODEC_VIDEO_MPEG4:
|
|
if (fmtp && *fmtp && 0 == sdp_a_fmtp_mpeg4(fmtp, &payload, &pt->fmtp.mpeg4))
|
|
pt->extra_bytes = sdp_aac_mpeg4_load(pt->extra, len, pt->fmtp.mpeg4.config);
|
|
break;
|
|
|
|
// TODO:
|
|
case AVCODEC_VIDEO_AV1:
|
|
case AVCODEC_VIDEO_VP8:
|
|
case AVCODEC_VIDEO_VP9:
|
|
case AVCODEC_AUDIO_MP3:
|
|
case AVCODEC_AUDIO_OPUS:
|
|
default:// common
|
|
break;
|
|
}
|
|
|
|
pt->bs = avpbs_find(codec);
|
|
if (pt->bs)
|
|
pt->filter = pt->bs->create(demuxer->stream+demuxer->count, codec, pt->extra, pt->extra_bytes > 0 ? pt->extra_bytes : 0, rtsp_demuxer_avpbs_onpacket, pt);
|
|
}
|
|
|
|
pt->rtp = rtp_demuxer_create(demuxer->jitter, frequency, pt->payload, encoding, onpacket, pt);
|
|
if (!pt->rtp || (pt->bs && !pt->filter))
|
|
{
|
|
rtsp_demuxer_payload_close(pt);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
demuxer->count++;
|
|
demuxer->off += pt->extra_bytes;
|
|
assert(demuxer->off <= sizeof(demuxer->ptr));
|
|
return 0;
|
|
}
|
|
|
|
struct rtsp_demuxer_t* rtsp_demuxer_create(int stream, int jitter, rtsp_demuxer_onpacket onpkt, void* param)
|
|
{
|
|
struct rtsp_demuxer_t* demuxer;
|
|
demuxer = calloc(1, sizeof(*demuxer));
|
|
if (!demuxer) return NULL;
|
|
|
|
demuxer->stream = stream;
|
|
demuxer->jitter = jitter;
|
|
demuxer->param = param;
|
|
demuxer->onpacket = onpkt;
|
|
return demuxer;
|
|
}
|
|
|
|
int rtsp_demuxer_destroy(struct rtsp_demuxer_t* demuxer)
|
|
{
|
|
int i;
|
|
if (demuxer)
|
|
{
|
|
for (i = 0; i < demuxer->count; i++)
|
|
rtsp_demuxer_payload_close(&demuxer->pt[i]);
|
|
free(demuxer);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int rtsp_demuxer_rtpinfo(struct rtsp_demuxer_t* demuxer, uint16_t seq, uint32_t timestamp)
|
|
{
|
|
struct rtp_payload_info_t* pt;
|
|
if (demuxer->idx >= demuxer->count || demuxer->idx < 0)
|
|
{
|
|
assert(0);
|
|
return -ENOENT;
|
|
}
|
|
|
|
pt = &demuxer->pt[demuxer->idx];
|
|
pt->seq = seq;
|
|
pt->base = timestamp;
|
|
|
|
// fixme
|
|
//if (0 == pt->last)
|
|
// pt->last = timestamp;
|
|
//pt->pts = ((int64_t)(int32_t)(pt->last - timestamp)) * 1000 / pt->frequency;
|
|
return 0;
|
|
}
|
|
|
|
int rtsp_demuxer_input(struct rtsp_demuxer_t* demuxer, const void* data, int bytes)
|
|
{
|
|
int i;
|
|
uint8_t id;
|
|
struct rtp_payload_info_t* pt;
|
|
|
|
id = bytes > 1 ? ((const uint8_t*)data)[1] : 255;
|
|
for (i = demuxer->idx; i < demuxer->count + demuxer->idx; i++)
|
|
{
|
|
pt = &demuxer->pt[i % demuxer->count];
|
|
|
|
// RFC7983 SRTP: https://tools.ietf.org/html/draft-ietf-avtcore-rfc5764-mux-fixes
|
|
assert(((const uint8_t*)data)[0] > 127 && ((const uint8_t*)data)[0] < 192);
|
|
|
|
// http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4
|
|
// RFC 5761 (RTCP-mux) states this range for secure RTCP/RTP detection.
|
|
// RTCP packet types in the ranges 1-191 and 224-254 SHOULD only be used when other values have been exhausted.
|
|
if ( (id & 0x7F) == pt->payload || (192 <= id && id <= 223) )
|
|
{
|
|
demuxer->idx = i % demuxer->count;
|
|
return rtp_demuxer_input(pt->rtp, data, bytes);
|
|
}
|
|
}
|
|
|
|
//assert(0);
|
|
//return -1;
|
|
return 0;
|
|
}
|
|
|
|
int rtsp_demuxer_rtcp(struct rtsp_demuxer_t* demuxer, void* buf, int len)
|
|
{
|
|
if (!demuxer || demuxer->idx >= demuxer->count || demuxer->idx < 0 || !demuxer->pt[demuxer->idx].rtp)
|
|
{
|
|
assert(0);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return rtp_demuxer_rtcp(demuxer->pt[demuxer->idx].rtp, buf, len);
|
|
}
|
|
|
|
int rtsp_demuxer_stats(struct rtsp_demuxer_t* demuxer, int* lost, int* late, int* misorder, int* duplicate)
|
|
{
|
|
if (!demuxer || demuxer->idx >= demuxer->count || demuxer->idx < 0 || !demuxer->pt[demuxer->idx].rtp)
|
|
{
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
rtp_demuxer_stats(demuxer->pt[demuxer->idx].rtp, lost, late, misorder, duplicate);
|
|
return 0;
|
|
}
|