ZLmediakit关于mk_media_input_h264()输入时间戳的问题

在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开始的。

作为一个记录吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值