ZLmediakit关于mk_media_input_h264()输入时间戳的问题
程序员文章站
2022-07-02 08:34:26
...
在zlmediakit的mk_media_input_h264()函数中,我们输入pts和dts如果为0,则代码会自己生成时间戳,但是这个时间戳不平滑,输出视频有卡顿现象,如果输入原始视频流的时间戳,需要将时间戳转化为时间基为{1,1000}才可行。
看代码:
API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) {
assert(ctx && data && len > 0);
MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx;
(*obj)->getChannel()->inputH264((char *) data, len, dts, pts);
}
void DevChannel::inputH264(const char *data, int len, uint32_t dts, uint32_t pts) {
//作者在这里自己生成时间戳,单位是毫秒,所以我们传入的也是毫秒
if(dts == 0){
dts = (uint32_t)_aTicker[0].elapsedTime();
}
if(pts == 0){
pts = dts;
}
//由于rtmp/hls/mp4需要缓存时间戳相同的帧,
//所以使用FrameNoCacheAble类型的帧反而会在转换成FrameCacheAble时多次内存拷贝
//在此处只拷贝一次,性能开销更低
auto frame = FrameImp::create<H264Frame>();
frame->_dts = dts;
frame->_pts = pts;
frame->_buffer.assign(data, len);
frame->_prefix_size = prefixSize(data,len);
inputFrame(frame);
}
具体代码在H264RtpEncoder中有体现:
/**
* 264 rtp打包类
*/
class H264RtpEncoder : public H264RtpDecoder ,public RtpInfo{
public:
typedef std::shared_ptr<H264RtpEncoder> Ptr;
/**
* @param ssrc ssrc
* @param mtu mtu大小
* @param sample_rate 采样率,强制为90000
* @param pt pt类型
* @param interleaved rtsp interleaved
*/
H264RtpEncoder(uint32_t ssrc,
uint32_t mtu = 1400,
uint32_t sample_rate = 90000,//记得这个值
uint8_t pt = 96,
uint8_t interleaved = TrackVideo * 2);
~H264RtpEncoder() {}
/**
* 输入264帧
* @param frame 帧数据,必须
*/
void inputFrame(const Frame::Ptr &frame) override;
private:
void makeH264Rtp(const void *pData, size_t uiLen, bool bMark, bool gop_pos, uint32_t uiStamp);
};
H264RtpEncoder::H264RtpEncoder(uint32_t ssrc, uint32_t mtu, uint32_t sample_rate, uint8_t pt, uint8_t interleaved)
: RtpInfo(ssrc, mtu, sample_rate, pt, interleaved) {
}
void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) {
auto ptr = frame->data() + frame->prefixSize();
auto len = frame->size() - frame->prefixSize();
//frame内部的pts就是我们传入的pts
auto pts = frame->pts();
auto nal_type = H264_TYPE(ptr[0]);
auto packet_size = getMaxSize() - 2;
//末尾5bit为nalu type,固定为28(FU-A)
auto fu_char_0 = (ptr[0] & (~0x1F)) | 28;
auto fu_char_1 = nal_type;
FuFlags *fu_flags = (FuFlags *) (&fu_char_1);
fu_flags->start_bit = 1;
//超过MTU则按照FU-A模式打包
if (len > packet_size + 1) {
size_t offset = 1;
while (!fu_flags->end_bit) {
if (!fu_flags->start_bit && len <= offset + packet_size) {
//FU-A end
packet_size = len - offset;
fu_flags->end_bit = 1;
}
//传入nullptr先不做payload的内存拷贝
auto rtp = makeRtp(getTrackType(), nullptr, packet_size + 2, fu_flags->end_bit, pts);
//rtp payload 负载部分
uint8_t *payload = rtp->getPayload();
//FU-A 第1个字节
payload[0] = fu_char_0;
//FU-A 第2个字节
payload[1] = fu_char_1;
//H264 数据
memcpy(payload + 2, (uint8_t *) ptr + offset, packet_size);
//输入到rtp环形缓存
RtpCodec::inputRtp(rtp, fu_flags->start_bit && nal_type == H264Frame::NAL_IDR);
offset += packet_size;
fu_flags->start_bit = 0;
}
} else {
//如果帧长度不超过mtu, 则按照Single NAL unit packet per H.264 方式打包
makeH264Rtp(ptr, len, false, false, pts);
}
}
void H264RtpEncoder::makeH264Rtp(const void* data, size_t len, bool mark, bool gop_pos, uint32_t uiStamp) {
//makeRtp()这个函数内部会生成pts
RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), gop_pos);
}
//具体代码如下:
RtpPacket::Ptr RtpInfo::makeRtp(TrackType type, const void* data, size_t len, bool mark, uint32_t stamp) {
uint16_t payload_len = (uint16_t) (len + RtpPacket::kRtpHeaderSize);
auto rtp = RtpPacket::create();
rtp->setCapacity(payload_len + RtpPacket::kRtpTcpHeaderSize);
rtp->setSize(payload_len + RtpPacket::kRtpTcpHeaderSize);
rtp->sample_rate = _sample_rate;
rtp->type = type;
//rtsp over tcp 头
auto ptr = (uint8_t *) rtp->data();
ptr[0] = '$';
ptr[1] = _interleaved;
ptr[2] = payload_len >> 8;
ptr[3] = payload_len & 0xFF;
//rtp头
auto header = rtp->getHeader();
header->version = RtpPacket::kRtpVersion;
header->padding = 0;
header->ext = 0;
header->csrc = 0;
header->mark = mark;
header->pt = _pt;
header->seq = htons(_seq++);
//stamp就是当时你传入的时间戳,从这里可以看出你传入的必须是毫秒
header->stamp = htonl(uint64_t(stamp) * _sample_rate / 1000);
header->ssrc = htonl(_ssrc);
//有效负载
if (data) {
memcpy(&ptr[RtpPacket::kRtpHeaderSize + RtpPacket::kRtpTcpHeaderSize], data, len);
}
return rtp;
}
另外,rtsp服务器输出的时间戳在打开视频的时候不一定是从0开始的,所以不能用ffmpeg录像,http是从0开始的。
作为一个记录吧
上一篇: 牛油火锅底料怎么炒吗
下一篇: python 地图纠偏/坐标系转换