Files
gb28181/GB28181Device/rtp/rtp_tx.cpp
2024-12-15 20:42:32 +08:00

970 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2022, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "rtp_tx.h"
#include "media_util.h"
#include "rtp.h"
#include "h264.h"
#include "h265.h"
#include "media_format.h"
/************************************************************************/
int gb_rtp_tcp_tx(SUA * p_sua, UA_MEDIA * p_media, uint8 * p_data, int len)
{
int offset = 0;
SOCKET fd = p_media->ua_m_fd;
if (p_sua->uaf_rtp_mux)
{
fd = p_media->mux_fd;
}
if (fd <= 0)
{
return -1;
}
while (p_sua->uaf_rtp_tx && offset < len)
{
#if __WINDOWS_OS__
int tlen = send(fd, (const char *)p_data+offset, len-offset, 0);
#else
int tlen = send(fd, (const char *)p_data+offset, len-offset, MSG_DONTWAIT);
#endif
if (tlen > 0)
{
offset += tlen;
}
else
{
int sockerr = sys_os_get_socket_error_num();
if (sockerr == EINTR || sockerr == EAGAIN)
{
usleep(1000);
continue;
}
log_print(HT_LOG_ERR, "%s, send failed, fd[%d], tlen[%d,%d], err[%d][%s]!!!\r\n",
__FUNCTION__, fd, tlen, len-offset, sockerr, sys_os_get_socket_error());
return -1;
}
}
return offset;
}
/**
* Send rtp data by UDP socket
*
* @param p_rua rtsp user agent
* @param av_t whether video rtp data
* @param p_data rtp data
* @param len rtp data len
* @return -1 on error, or the data length has been sent
*/
int gb_rtp_udp_tx(SUA * p_sua, UA_MEDIA * p_media, uint8 * p_data, int len)
{
int offset = 0;
SOCKET fd;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = p_sua->remote_media_ip;
addr.sin_port = htons(p_media->remote_port);
if (p_sua->uaf_multicast)
{
addr.sin_addr.s_addr = p_sua->multicast_addr;
}
fd = p_media->ua_m_fd;
if (p_sua->uaf_rtp_mux)
{
fd = p_media->mux_fd;
}
if (fd <= 0)
{
return -1;
}
while (p_sua->uaf_rtp_tx && offset < len)
{
int tlen = sendto(fd, (char *)p_data+offset, len-offset, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (tlen > 0)
{
offset += tlen;
}
else
{
int sockerr = sys_os_get_socket_error_num();
if (sockerr == EINTR || sockerr == EAGAIN)
{
usleep(1000);
continue;
}
log_print(HT_LOG_ERR, "%s, send failed, fd[%d], tlen[%d,%d], err[%d][%s]!!!\r\n",
__FUNCTION__, fd, tlen, len-offset, sockerr, sys_os_get_socket_error());
return -1;
}
}
return offset;
}
int gb_rtp_video_build(SUA * p_sua, uint8 * p_data, int len, int mbit)
{
int slen = 0;
int offset = 0;
uint8 buff[40];
uint8 * p_hdr_ptr;
if (p_sua->uaf_v_tcp_rtp)
{
offset += rtp_write_uint16(buff+offset, len+12);
}
if (p_sua->uaf_rtp_mux)
{
offset += rtp_write_uint32(buff+offset, p_sua->video_rtp_media.mux_id);
}
buff[offset] = (RTP_VERSION << 6);
offset++;
buff[offset] = ((p_sua->v_rtp_info.rtp_pt) | ((mbit & 0x01) << 7));
offset++;
offset += rtp_write_uint16(buff+offset, p_sua->v_rtp_info.rtp_cnt);
offset += rtp_write_uint32(buff+offset, p_sua->v_rtp_info.rtp_ts);
offset += rtp_write_uint32(buff+offset, p_sua->v_rtp_info.rtp_ssrc);
p_hdr_ptr = p_data - offset;
memcpy(p_hdr_ptr, buff, offset);
if (p_sua->uaf_v_tcp_rtp)
{
slen = gb_rtp_tcp_tx(p_sua, &p_sua->video_rtp_media, p_hdr_ptr, offset+len);
}
else
{
slen = gb_rtp_udp_tx(p_sua, &p_sua->video_rtp_media, p_hdr_ptr, offset+len);
}
p_sua->v_rtp_info.rtp_cnt = (p_sua->v_rtp_info.rtp_cnt + 1) & 0xFFFF;
return (slen == offset+len) ? slen : -1;
}
/**
* Build video rtp packet and send (not fragment)
*
* @param p_rua rtsp user agent
* @param p_data payload data
* @param len payload data length
* @ts the packet timestamp
* @return 1 on success, -1 on error
*/
int gb_rtp_video_tx(SUA * p_sua, uint8 * p_data, int size, uint32 ts)
{
int ret = 0;
int len, max_packet_size;
uint8 * p = p_data;
max_packet_size = RTP_MAX_LEN;
p_sua->v_rtp_info.rtp_ts = ts;
while (size > 0)
{
len = max_packet_size;
if (len > size)
{
len = size;
}
ret = gb_rtp_video_build(p_sua, p, len, (len == size));
if (ret < 0)
{
break;
}
p += len;
size -= len;
}
return ret;
}
/**
* Judgment and segmentation h264 rtp data into a single package
*
* @param fu_flag fragment flag
* @param fu_s fragment start flag
* @param fu_e fragment end flag
* @param len data length
* @return the fragmention length
*/
int gb_rtp_h264_fu_split(int * fu_flag, int * fu_s, int * fu_e, int len)
{
if ((*fu_flag) == 0) // have not yet begun fragment
{
if (len <= H264_RTP_MAX_LEN) return len; // Need not be fragmented
*fu_flag = 1;
*fu_s = 1;
*fu_e = 0;
return H264_RTP_MAX_LEN;
}
else // It has begun to fragment
{
*fu_s = 0;
if (len <= H264_RTP_MAX_LEN) // End fragmentation
{
*fu_e = 1;
return len;
}
else
{
return H264_RTP_MAX_LEN;
}
}
}
/**
* Build and send h264 fragement rtp packet
*
* @param p_rua rtsp user agent
* @nalu_t the NALU type
* @fu_s fragement start flag
* @fu_e fragement end start
* @param p_data payload data
* @param len payload data length
* @return the rtp packet length, -1 on error
*/
int gb_rtp_h264_single_fu_build(SUA * p_sua, uint8 nalu_t, int fu_s, int fu_e, uint8 * p_data, int len)
{
int mbit = 0;
int slen;
int offset = 0;
uint8 buff[40];
uint8 * p_rtp_ptr;
if (p_sua->uaf_v_tcp_rtp)
{
offset += rtp_write_uint16(buff+offset, len+12+2);
}
if (p_sua->uaf_rtp_mux)
{
offset += rtp_write_uint32(buff+offset, p_sua->video_rtp_media.mux_id);
}
if (fu_e == 1)
{
mbit = 1;
}
else
{
mbit = 0;
}
buff[offset] = (RTP_VERSION << 6);
offset++;
buff[offset] = ((p_sua->v_rtp_info.rtp_pt) | ((mbit & 0x01) << 7));
offset++;
offset += rtp_write_uint16(buff+offset, p_sua->v_rtp_info.rtp_cnt);
offset += rtp_write_uint32(buff+offset, p_sua->v_rtp_info.rtp_ts);
offset += rtp_write_uint32(buff+offset, p_sua->v_rtp_info.rtp_ssrc);
buff[offset] = (nalu_t & 0x60) | 28;
offset++;
buff[offset] = (nalu_t & 0x1F);
if (fu_s == 1)
{
buff[offset] |= 0x80;
}
if (fu_e == 1)
{
buff[offset] |= 0x40;
}
offset++;
p_rtp_ptr = p_data - offset;
memcpy(p_rtp_ptr, buff, offset);
if (p_sua->uaf_v_tcp_rtp)
{
slen = gb_rtp_tcp_tx(p_sua, &p_sua->video_rtp_media, p_rtp_ptr, offset+len);
}
else
{
slen = gb_rtp_udp_tx(p_sua, &p_sua->video_rtp_media, p_rtp_ptr, offset+len);
}
p_sua->v_rtp_info.rtp_cnt = (p_sua->v_rtp_info.rtp_cnt + 1) & 0xFFFF;
return (slen == offset+len) ? slen : -1;
}
/**
* Send h264 rtp packet
*
* @param p_rua rtsp user agent
* @ts the packet timestamp
* @nalu_t the NALU type
* @fu_flag fragment flag
* @fu_s fragement start flag
* @fu_e fragement end start
* @param p_data payload data
* @param len payload data length
* @return the rtp packet length, -1 on error
*/
int gb_rtp_h264_tx(SUA * p_sua, uint8 nalu_t, int fu_flag, int fu_s, int fu_e, uint8 * p_data, int len)
{
int ret = 0;
if (fu_flag == 0)
{
ret = gb_rtp_video_build(p_sua, p_data, len, 1);
}
else
{
ret = gb_rtp_h264_single_fu_build(p_sua, nalu_t, fu_s, fu_e, p_data, len);
}
return ret;
}
/**
* Build h264 video rtp packet and send
*
* @param p_rua rtsp user agent
* @param p_data payload data
* @param len payload data length
* @ts the packet timestamp
* @i_flag the I-Frame flag
* @return 1 on success, -1 on error
*/
int gb_rtp_h264_video_pkt_tx(SUA * p_sua, uint8 * p_data, int len)
{
int ret = 1;
int frame_len = len;
int fu_s = 0, fu_e = 0, fu_flag = 0; // Fragement start, end flag
uint8 * p_tx_data = p_data;
uint8 nalu_t = p_tx_data[0];
int keyframe = ((nalu_t & 0x1F) == 5);
while (frame_len > 0)
{
int tlen = gb_rtp_h264_fu_split(&fu_flag, &fu_s, &fu_e, frame_len);
if (fu_flag == 1)
{
if (fu_s == 1)
{
p_tx_data++;
tlen--;
frame_len--;
}
}
// the sps and pps frame
if ((nalu_t & 0x1F) == 7 || (nalu_t & 0x1F) == 8)
{
ret = gb_rtp_h264_tx(p_sua, nalu_t, fu_flag, fu_s, fu_e, p_tx_data, tlen);
if (ret == -1)
{
break;
}
}
else if (p_sua->uaf_iframe_tx == 0 && 0 == keyframe)
{
// Non-I-frame, not yet sent I-frame, skip P frame
}
else
{
ret = gb_rtp_h264_tx(p_sua, nalu_t, fu_flag, fu_s, fu_e, p_tx_data, tlen);
if (ret == -1)
{
break;
}
p_sua->uaf_iframe_tx = 1;
}
p_tx_data += tlen;
frame_len -= tlen;
}
return ret;
}
/**
* Build h264 video rtp packet and send
*
* @param p_rua rtsp user agent
* @param p_data payload data
* @param len payload data length
* @ts the packet timestamp
* @return 1 on success, -1 on error
*/
int gb_rtp_h264_video_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
int ret = 1;
uint8 *r, *end = p_data + len;
r = avc_find_startcode(p_data, end);
p_sua->v_rtp_info.rtp_ts = ts;
while (r < end)
{
uint8 *r1;
while (!*(r++));
r1 = avc_find_startcode(r, end);
ret = gb_rtp_h264_video_pkt_tx(p_sua, r, r1 - r);
if (ret < 0)
{
break;
}
r = r1;
}
return ret;
}
int gb_rtp_h265_video_pkt_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
int frame_len = len;
uint8 * p_tx_data = p_data;
int rtp_payload_size = H265_RTP_MAX_LEN - 3;
int nal_type = (p_tx_data[0] >> 1) & 0x3F;
/* send it as one single NAL unit? */
if (frame_len <= H265_RTP_MAX_LEN)
{
gb_rtp_video_build(p_sua, p_tx_data, frame_len, 1);
}
else
{
/*
create the HEVC payload header and transmit the buffer as fragmentation units (FU)
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F| Type | LayerId | TID |
+-------------+-----------------+
F = 0
Type = 49 (fragmentation unit (FU))
LayerId = 0
TID = 1
*/
uint8 tbuf[2048];
uint8 * buf1 = tbuf+32;
uint8 * buf = p_tx_data;
buf1[0] = 49 << 1;
buf1[1] = 1;
/*
create the FU header
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|S|E| FuType |
+---------------+
S = variable
E = variable
FuType = NAL unit type
*/
buf1[2] = nal_type;
/* set the S bit: mark as start fragment */
buf1[2] |= 1 << 7;
/* pass the original NAL header */
buf += 2;
frame_len -= 2;
while (frame_len > rtp_payload_size)
{
memcpy(buf1+3, buf, rtp_payload_size);
gb_rtp_video_build(p_sua, buf1, H265_RTP_MAX_LEN, 0);
buf += rtp_payload_size;
frame_len -= rtp_payload_size;
/* reset the S bit */
buf1[2] &= ~(1 << 7);
}
/* set the E bit: mark as last fragment */
buf1[2] |= 1 << 6;
/* complete and send last RTP packet */
memcpy(buf1+3, buf, frame_len);
gb_rtp_video_build(p_sua, buf1, frame_len + 3, 1);
}
return 1;
}
int gb_rtp_h265_video_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
int ret = -1;
uint8 *r, *end = p_data + len;
r = avc_find_startcode(p_data, end);
p_sua->v_rtp_info.rtp_ts = ts;
while (r < end)
{
uint8 *r1;
while (!*(r++));
r1 = avc_find_startcode(r, end);
ret = gb_rtp_h265_video_pkt_tx(p_sua, r, r1 - r, ts);
if (ret < 0)
{
break;
}
r = r1;
}
return ret;
}
int gb_rtp_ps_video_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
int ps_cnt = 0;
int i_flag = 0;
int ps_hdr_len, remain_len = len, tx_remain, offset, tx_len;
uint8 nalu_t;
uint8 tmp_buf[512];
uint32 used_size = 0;
int vtype = 0x1b;
int atype = 0x90;
if (NULL == p_data || len < 5)
{
return -1;
}
if (VIDEO_CODEC_H264 == p_sua->media_info.v_info.codec)
{
vtype = 0x1b;
nalu_t = p_data[4] & 0x1F;
if (nalu_t == H264_NAL_SPS || nalu_t == H264_NAL_PPS || nalu_t == H264_NAL_IDR || nalu_t == H264_NAL_SEI)
{
i_flag = 1;
}
}
else if (VIDEO_CODEC_H265 == p_sua->media_info.v_info.codec)
{
vtype = 0x24;
nalu_t = (p_data[4] & 0x7E) >> 1;
if ((nalu_t >= 16 && nalu_t <= 21) || nalu_t == HEVC_NAL_VPS || nalu_t == HEVC_NAL_SPS || nalu_t == HEVC_NAL_PPS)
{
i_flag = 1;
}
}
else if (VIDEO_CODEC_MP4 == p_sua->media_info.v_info.codec)
{
vtype = 0x10;
int pos = 0;
while (pos < len - 5)
{
if (p_data[pos] == 0 && p_data[pos+1] == 0 && p_data[pos+2] == 1)
{
if (p_data[pos+3] == 0xb6)
{
if ((p_data[4] & 0xC0) == 0)
{
i_flag = 1;
}
break;
}
else
{
pos += 4;
}
}
else
{
pos++;
}
}
}
if (AUDIO_CODEC_G711A == p_sua->media_info.a_info.codec)
{
atype = 0x90;
}
else if (AUDIO_CODEC_G711U == p_sua->media_info.a_info.codec)
{
atype = 0x91;
}
else if (AUDIO_CODEC_G722 == p_sua->media_info.a_info.codec)
{
atype = 0x92;
}
else if (AUDIO_CODEC_AAC == p_sua->media_info.a_info.codec)
{
atype = 0x0f;
}
sys_os_mutex_enter(p_sua->pstxi_mutex);
ps_init_info(&p_sua->pstxi, vtype, atype);
while (remain_len > 0)
{
int pkt_type = (i_flag == 1) ? 3 : 1;
if (ps_cnt > 0)
{
pkt_type = 4;
}
ps_hdr_len = ps_make_header(&p_sua->pstxi, tmp_buf, remain_len, ts, pkt_type, &used_size);
tx_remain = used_size + ps_hdr_len; // ±¾´Î×éPS°üµÄ×ܳ¤¶È
offset = len - remain_len - ps_hdr_len;
memcpy(p_data + offset, tmp_buf, ps_hdr_len);
remain_len -= used_size;
while (tx_remain > 0)
{
tx_len = (tx_remain > H264_RTP_MAX_LEN) ? H264_RTP_MAX_LEN : tx_remain;
p_sua->v_rtp_info.rtp_ts = ts;
tx_remain -= tx_len;
int mark = (tx_remain > 0 || remain_len > 0) ? 0 : 1;
int ret = gb_rtp_video_build(p_sua, p_data + offset, tx_len, mark);
if (ret < 0)
{
sys_os_mutex_leave(p_sua->pstxi_mutex);
return -1;
}
p_sua->video_rtp_media.last_pkt_time = sys_os_get_uptime();
offset += tx_len;
}
ps_cnt++;
p_sua->video_rtp_media.tx_pkt_cnt++;
}
sys_os_mutex_leave(p_sua->pstxi_mutex);
return 0;
}
int gb_rtp_ps_audio_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
int ps_cnt = 0;
int ps_hdr_len, remain_len = len, tx_remain, offset, tx_len;
uint8 tmp_buf[512];
uint32 used_size = 0;
int vtype = 0x1b;
int atype = 0x90;
if (VIDEO_CODEC_H264 == p_sua->media_info.v_info.codec)
{
vtype = 0x1b;
}
else if (VIDEO_CODEC_H265 == p_sua->media_info.v_info.codec)
{
vtype = 0x24;
}
else if (VIDEO_CODEC_MP4 == p_sua->media_info.v_info.codec)
{
vtype = 0x10;
}
if (AUDIO_CODEC_G711A == p_sua->media_info.a_info.codec)
{
atype = 0x90;
}
else if (AUDIO_CODEC_G711U == p_sua->media_info.a_info.codec)
{
atype = 0x91;
}
else if (AUDIO_CODEC_G722 == p_sua->media_info.a_info.codec)
{
atype = 0x92;
}
else if (AUDIO_CODEC_AAC == p_sua->media_info.a_info.codec)
{
atype = 0x0f;
}
sys_os_mutex_enter(p_sua->pstxi_mutex);
ps_init_info(&p_sua->pstxi, vtype, atype);
while (remain_len > 0)
{
int pkt_type = 2;
if (ps_cnt > 0)
{
pkt_type = 5;
}
ps_hdr_len = ps_make_header(&p_sua->pstxi, tmp_buf, remain_len, ts, pkt_type, &used_size);
tx_remain = used_size + ps_hdr_len; // ±¾´Î×éPS°üµÄ×ܳ¤¶È
offset = len - remain_len - ps_hdr_len;
memcpy(p_data + offset, tmp_buf, ps_hdr_len);
remain_len -= used_size;
while (tx_remain > 0)
{
tx_len = (tx_remain > H264_RTP_MAX_LEN) ? H264_RTP_MAX_LEN : tx_remain;
p_sua->v_rtp_info.rtp_ts = ts;
tx_remain -= tx_len;
int mark = (tx_remain > 0 || remain_len > 0) ? 0 : 1;
int ret = gb_rtp_video_build(p_sua, p_data + offset, tx_len, mark);
if (ret < 0)
{
sys_os_mutex_leave(p_sua->pstxi_mutex);
return -1;
}
p_sua->video_rtp_media.last_pkt_time = sys_os_get_uptime();
offset += tx_len;
}
ps_cnt++;
p_sua->video_rtp_media.tx_pkt_cnt++;
}
sys_os_mutex_leave(p_sua->pstxi_mutex);
return 0;
}
int gb_rtp_ps_aac_tx(SUA * p_sua, uint8 * p_data, int len, uint32 ts)
{
if (p_data[0] != 0xFF || (p_data[1] & 0xF0) != 0xF0)
{
// add ADTS header
int profile = 2; // AAC LC
int freqIdx = 4;
switch (p_sua->media_info.a_info.samplerate)
{
case 96000:
freqIdx = 0;
break;
case 88200:
freqIdx = 1;
break;
case 64000:
freqIdx = 2;
break;
case 48000:
freqIdx = 3;
break;
case 44100:
freqIdx = 4;
break;
case 32000:
freqIdx = 5;
break;
case 24000:
freqIdx = 6;
break;
case 22050:
freqIdx = 7;
break;
case 16000:
freqIdx = 8;
break;
case 12000:
freqIdx = 9;
break;
case 11025:
freqIdx = 10;
break;
case 8000:
freqIdx = 11;
break;
case 7350:
freqIdx = 12;
break;
default:
freqIdx = 4;
break;
}
p_data -= 7;
len += 7;
p_data[0] = 0xFF;
p_data[1] = 0xF9;
p_data[2] = ((profile - 1) << 6) + (freqIdx << 2) + (p_sua->media_info.a_info.channels >> 2);
p_data[3] = ((p_sua->media_info.a_info.channels & 3) << 6) + (len >> 11);
p_data[4] = (len & 0x7FF) >> 3;
p_data[5] = ((len & 7) << 5) + 0x1F;
p_data[6] = 0xFC;
}
return gb_rtp_ps_audio_tx(p_sua, p_data, len, ts);
}
int gb_rtp_audio_build(SUA * p_sua, uint8 * p_data, int len, int mbit)
{
int slen = 0;
int offset = 0;
uint8 buff[40];
uint8 * p_hdr_ptr;
if (p_sua->uaf_a_tcp_rtp)
{
offset += rtp_write_uint16(buff+offset, len+12);
}
if (p_sua->uaf_rtp_mux)
{
offset += rtp_write_uint32(buff+offset, p_sua->audio_rtp_media.mux_id);
}
buff[offset] = (RTP_VERSION << 6);
offset++;
buff[offset] = ((p_sua->a_rtp_info.rtp_pt) | ((mbit & 0x01) << 7));
offset++;
offset += rtp_write_uint16(buff+offset, p_sua->a_rtp_info.rtp_cnt);
offset += rtp_write_uint32(buff+offset, p_sua->a_rtp_info.rtp_ts);
offset += rtp_write_uint32(buff+offset, p_sua->a_rtp_info.rtp_ssrc);
p_hdr_ptr = p_data - offset;
memcpy(p_hdr_ptr, buff, offset);
if (p_sua->uaf_a_tcp_rtp)
{
slen = gb_rtp_tcp_tx(p_sua, &p_sua->audio_rtp_media, p_hdr_ptr, offset+len);
}
else
{
slen = gb_rtp_udp_tx(p_sua, &p_sua->audio_rtp_media, p_hdr_ptr, offset+len);
}
p_sua->a_rtp_info.rtp_cnt = (p_sua->a_rtp_info.rtp_cnt + 1) & 0xFFFF;
return (slen == offset+len) ? slen : -1;
}
int gb_rtp_audio_tx(SUA * p_sua, uint8 * p_data, int size, uint32 ts)
{
int len, max_packet_size;
uint8 * p = p_data;
if (p_sua == NULL)
{
return -1;
}
p_sua->a_rtp_info.rtp_ts = ts;
max_packet_size = RTP_MAX_LEN;
while (size > 0)
{
len = max_packet_size;
if (len > size)
{
len = size;
}
gb_rtp_audio_build(p_sua, p, len, (len == size));
p += len;
size -= len;
}
p_sua->audio_rtp_media.last_pkt_time = sys_os_get_uptime();
p_sua->audio_rtp_media.tx_pkt_cnt++;
return 0;
}