Files
ZLMediaKit/ext-codec/MP2VRtp.cpp

275 lines
8.8 KiB
C++
Raw Permalink Normal View History

/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include "MP2VRtp.h"
#include "Common/config.h"
namespace mediakit {
// ======================== MP2VRtpDecoder ========================
MP2VRtpDecoder::MP2VRtpDecoder() {
obtainFrame();
}
void MP2VRtpDecoder::obtainFrame() {
_frame = FrameImp::create<MP2VFrame>();
}
bool MP2VRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) {
auto seq = rtp->getSeq();
auto last_gop_dropped = _gop_dropped;
bool is_gop_start = decodeRtp(rtp);
if (!_gop_dropped && seq != (uint16_t)(_last_seq + 1) && _last_seq) {
_gop_dropped = true;
WarnL << "start drop mp2v gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString();
}
_last_seq = seq;
return is_gop_start && !last_gop_dropped;
}
/**
* RFC 2250 MPEG Video-specific header (4 bytes):
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | MBZ |T| TR |AN|N|S|B|E| P | | BFC | | FFC |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* FBV FFV
*
* T: MPEG-2 specific header extension present (1 bit)
* TR: Temporal Reference (10 bits)
* AN: Active N bit (1 bit)
* N: New picture header (1 bit)
* S: Sequence-header-present (1 bit)
* B: Beginning-of-slice (1 bit)
* E: End-of-slice (1 bit)
* P: Picture-Type (3 bits): I(1), P(2), B(3), D(4)
* FBV: full_pel_backward_vector (1 bit)
* BFC: backward_f_code (3 bits)
* FFV: full_pel_forward_vector (1 bit)
* FFC: forward_f_code (3 bits)
*/
bool MP2VRtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) {
auto payload_size = rtp->getPayloadSize();
if (payload_size <= (ssize_t)kMP2VHeaderSize) {
// 负载太小,不包含有效数据
return false;
}
auto payload = rtp->getPayload();
auto stamp = rtp->getStampMS();
auto seq = rtp->getSeq();
// 解析 RFC 2250 MPEG Video-specific header
bool t_bit = (payload[0] >> 2) & 0x01;
// uint16_t temporal_ref = ((payload[0] & 0x03) << 8) | payload[1];
// bool seq_header_present = (payload[2] >> 5) & 0x01;
// bool begin_of_slice = (payload[2] >> 4) & 0x01;
// bool end_of_slice = (payload[2] >> 3) & 0x01;
uint8_t picture_type = (payload[2] & 0x07);
// 如果 T bit 置位,还有 4 字节的 MPEG-2 扩展头需要跳过
size_t header_size = kMP2VHeaderSize + (t_bit ? 4 : 0);
if (payload_size <= (ssize_t)header_size) {
return false;
}
auto es_data = payload + header_size;
auto es_size = payload_size - header_size;
// 检查是否为新帧(时间戳变化)
if (!_frame->_buffer.empty() && stamp != _frame->_pts) {
// 时间戳变化,输出上一帧
outputFrame(rtp);
}
if (_frame->_buffer.empty()) {
// 新帧开始
_frame->_pts = stamp;
_drop_flag = false;
_picture_type = picture_type;
}
if (_drop_flag) {
return false;
}
// 检测 seq 不连续,丢弃当前帧
if (!_frame->_buffer.empty() && seq != (uint16_t)(_last_seq + 1) && _last_seq) {
_drop_flag = true;
_frame->_buffer.clear();
return false;
}
// 追加 ES 数据
_frame->_buffer.append((char *)es_data, es_size);
// RTP mark bit 标识帧结束
if (rtp->getHeader()->mark) {
outputFrame(rtp);
return _picture_type == 1; // I-Picture
}
return false;
}
void MP2VRtpDecoder::outputFrame(const RtpPacket::Ptr &rtp) {
if (_frame->_buffer.empty()) {
return;
}
// 生成 DTSMPEG-2 有 B 帧PTS 和 DTS 不一定相同)
_dts_generator.getDts(_frame->_pts, _frame->_dts);
bool is_key = _frame->keyFrame();
if (is_key && _gop_dropped) {
_gop_dropped = false;
InfoL << "new mp2v gop received, rtp:\r\n" << rtp->dumpString();
}
if (!_gop_dropped) {
RtpCodec::inputFrame(_frame);
}
obtainFrame();
}
// ======================== MP2VRtpEncoder ========================
bool MP2VRtpEncoder::hasSequenceHeader(const uint8_t *data, size_t size) {
// 查找 sequence header start code: 00 00 01 B3
for (size_t i = 0; i + 3 < size; ++i) {
if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01 && data[i + 3] == 0xB3) {
return true;
}
}
return false;
}
void MP2VRtpEncoder::parsePictureInfo(const uint8_t *data, size_t size) {
_temporal_ref = 0;
_picture_type = 0;
_fbv = 0;
_bfc = 0;
_ffv = 0;
_ffc = 0;
_has_seq_header = hasSequenceHeader(data, size);
// 查找 picture start code: 00 00 01 00
for (size_t i = 0; i + 5 < size; ++i) {
if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x01 && data[i + 3] == 0x00) {
// temporal_reference: 10 bits, picture_coding_type: 3 bits
_temporal_ref = (data[i + 4] << 2) | ((data[i + 5] >> 6) & 0x03);
_picture_type = (data[i + 5] >> 3) & 0x07;
// 解析 motion vector codes (vbv_delay 之后)
// picture header: temporal_reference(10) + picture_coding_type(3) + vbv_delay(16)
if (i + 8 < size) {
uint8_t extra_byte = data[i + 8];
if (_picture_type == 2 /* P */ || _picture_type == 3 /* B */) {
// full_pel_forward_vector(1) + forward_f_code(3)
_ffv = (extra_byte >> 2) & 0x01;
_ffc = ((extra_byte & 0x03) << 1);
if (i + 9 < size) {
_ffc |= (data[i + 9] >> 7) & 0x01;
}
}
if (_picture_type == 3 /* B */) {
// full_pel_backward_vector(1) + backward_f_code(3) 紧跟在 forward 之后
if (i + 9 < size) {
_fbv = (data[i + 9] >> 6) & 0x01;
_bfc = (data[i + 9] >> 3) & 0x07;
}
}
}
return;
}
}
}
void MP2VRtpEncoder::buildMpvHeader(uint8_t *buf, const uint8_t *data, size_t size,
bool is_begin_of_slice, bool is_end_of_slice) {
// RFC 2250 Section 3.4
// Byte 0: MBZ(5) + T(1) + TR high 2 bits
// T = 0 (不发送 MPEG-2 扩展头)
buf[0] = (_temporal_ref >> 8) & 0x03;
// Byte 1: TR low 8 bits
buf[1] = _temporal_ref & 0xFF;
// Byte 2: AN(1) + N(1) + S(1) + B(1) + E(1) + P(3)
uint8_t byte2 = 0;
// AN = 0, N = 0
if (_has_seq_header) {
byte2 |= 0x20; // S bit
}
if (is_begin_of_slice) {
byte2 |= 0x10; // B bit
}
if (is_end_of_slice) {
byte2 |= 0x08; // E bit
}
byte2 |= (_picture_type & 0x07);
buf[2] = byte2;
// Byte 3: FBV(1) + BFC(3) + FFV(1) + FFC(3)
buf[3] = ((_fbv & 0x01) << 7) | ((_bfc & 0x07) << 4) | ((_ffv & 0x01) << 3) | (_ffc & 0x07);
}
bool MP2VRtpEncoder::inputFrame(const Frame::Ptr &frame) {
auto ptr = (const uint8_t *)frame->data() + frame->prefixSize();
auto size = frame->size() - frame->prefixSize();
if (size == 0) {
return false;
}
// 解析帧信息picture type, temporal reference 等)
parsePictureInfo(ptr, size);
bool is_key = frame->keyFrame();
auto max_payload = getRtpInfo().getMaxSize() - kMP2VHeaderSize;
size_t offset = 0;
while (offset < size) {
bool is_first = (offset == 0);
size_t payload_size;
bool is_last;
if (size - offset <= max_payload) {
payload_size = size - offset;
is_last = true;
} else {
payload_size = max_payload;
is_last = false;
}
// 构建 MPEG Video-specific header
uint8_t mpv_header[kMP2VHeaderSize];
buildMpvHeader(mpv_header, ptr + offset, payload_size, is_first, is_last);
// 创建 RTP 包MPEG header + ES data
auto rtp = getRtpInfo().makeRtp(TrackVideo, nullptr, kMP2VHeaderSize + payload_size, is_last, frame->pts());
auto rtp_payload = rtp->getPayload();
// 写入 MPEG Video-specific header
memcpy(rtp_payload, mpv_header, kMP2VHeaderSize);
// 写入 ES 数据
memcpy(rtp_payload + kMP2VHeaderSize, ptr + offset, payload_size);
// 输入到 RTP 环形缓存
RtpCodec::inputRtp(rtp, is_key && is_first);
offset += payload_size;
}
return true;
}
} // namespace mediakit