in vireo/internal/demux/mp2ts.cpp [287:408]
void process_h264_frame(int64_t pts, int64_t dts, const common::Data32& packet_data) {
// H.264 nal unit in Annex-B format
bool keyframe = false;
CHECK(ANNEXB<H264NalType>::StartCodePrefixSize(packet_data));
ANNEXB<H264NalType> annexb_parser(packet_data);
vector<common::Data32> caption_contents;
bool frame_data_found = false;
for (uint32_t index = 0; index < annexb_parser.b(); index++) {
NalInfo<H264NalType> info = annexb_parser(index);
// Check if there is an SPS + PPS
if (info.type == H264NalType::SPS) {
// Found an SPS
common::Data16 packet_sps = common::Data16(packet_data.data() + info.byte_offset, info.size, nullptr);
// Parse width/height from SPS
uint16_t packet_width = 0;
uint16_t packet_height = 0;
if (!tracks(SampleType::Video).initialized) {
h264_info_t h264_info;
THROW_IF(h264_setup_parser(&h264_info, 1) != 0, Invalid);
THROW_IF(h264_parse_sps(&h264_info, h264_info.buffer.rbsp, (uint8_t*)(packet_sps.data() + 1), packet_sps.count() - 1) != 0, Invalid);
h264_cleanup_parser(&h264_info);
packet_width = h264_info.sps.cropped_width;
packet_height = h264_info.sps.cropped_height;
}
// After SPS, expect a PPS
index++;
if (index >= annexb_parser.count()) {
CHECK(ANNEXB<H264NalType>::StartCodePrefixSize(packet_data));
video.cache.cache_new_packet(pts, dts, packet_data);
return;
}
info = annexb_parser(index);
THROW_IF(info.type != H264NalType::PPS, Invalid);
common::Data16 packet_pps = common::Data16(packet_data.data() + info.byte_offset, info.size, nullptr);
auto sps_pps = header::SPS_PPS(packet_sps, packet_pps, kNaluLengthSize);
auto sps_pps_extradata = sps_pps.as_extradata(header::SPS_PPS::ExtraDataType::annex_b);
// Save all unique SPS / PPS
if (video.sps_pps_extradatas.empty() || video.sps_pps_extradatas.back() != sps_pps_extradata) {
video.sps_pps.push_back(sps_pps);
video.sps_pps_extradatas.push_back(sps_pps_extradata);
}
// Mark track as initialized
if (!tracks(SampleType::Video).initialized) {
video.width = packet_width;
video.height = packet_height;
THROW_IF(!security::valid_dimensions(video.width, video.height), Unsafe);
tracks(SampleType::Video).initialized = true;
}
}
if (info.type == decode::H264NalType::SEI) {
common::Data32 data = common::Data32(packet_data.data() + info.byte_offset, info.size, nullptr);
util::CaptionPayloadInfo caption_info = util::CaptionHandler::ParsePayloadInfo(data);
if (caption_info.valid) {
if (!caption_info.byte_ranges.empty()) {
const uint32_t max_caption_size = info.size + kNaluLengthSize + 2; // data + nal length + nal type + trailing bits (1 byte)
common::Data32 caption_data = common::Data32(new uint8_t[max_caption_size], max_caption_size, [](uint8_t* p) { delete[] p; });
uint32_t caption_size = util::CaptionHandler::CopyPayloadsIntoData(data, caption_info, kNaluLengthSize, caption_data);
caption_data.set_bounds(0, caption_size);
caption_contents.push_back(caption_data);
}
} else {
CHECK(ANNEXB<H264NalType>::StartCodePrefixSize(packet_data));
video.cache.cache_new_packet(pts, dts, packet_data);
return;
}
}
// Find the first frame data, check if it is a keyframe
// Save the packet contents starting from the first frame data
if (!frame_data_found && (info.type == decode::H264NalType::IDR || info.type == decode::H264NalType::FRM)) {
frame_data_found = true;
keyframe = (info.type == H264NalType::IDR) ? true : false;
vector<common::Data32> contents;
const uint32_t byte_offset = info.byte_offset - info.start_code_prefix_size;
THROW_IF(byte_offset < 0, Invalid);
common::Data32 packet_video = common::Data32(packet_data.data() + byte_offset, packet_data.b() - byte_offset, nullptr);
CHECK(packet_video.count());
if (keyframe) {
CHECK(video.sps_pps_extradatas.size() > 0);
const auto& sps_pps_extradata = video.sps_pps_extradatas.back();
contents.emplace_back(sps_pps_extradata.data() + sps_pps_extradata.a(), sps_pps_extradata.count(), nullptr);
}
contents.push_back(packet_video);
if (tracks(SampleType::Video).samples.size()) {
int64_t prev_dts = tracks(SampleType::Video).samples.back().dts;
THROW_IF(dts < prev_dts, Invalid);
tracks(SampleType::Video).dts_offsets_per_packet.push_back((uint32_t)(dts - prev_dts));
}
tracks(SampleType::Video).samples.push_back({ contents, (uint32_t)pts, (uint32_t)dts, keyframe });
THROW_IF(tracks(SampleType::Video).samples.size() >= kMaxMP2TSSampleCount, Unsafe);
}
}
if (!frame_data_found) {
CHECK(ANNEXB<H264NalType>::StartCodePrefixSize(packet_data));
video.cache.cache_new_packet(pts, dts, packet_data);
return;
}
if (!caption_contents.empty()) {
tracks(SampleType::Caption).initialized = true;
caption.codec = settings::Caption::Codec::Unknown;
tracks(SampleType::Caption).timescale = tracks(SampleType::Video).timescale;
}
if (tracks(SampleType::Video).dts_offsets_per_packet.size()) {
tracks(SampleType::Caption).dts_offsets_per_packet.push_back(tracks(SampleType::Video).dts_offsets_per_packet.back());
}
tracks(SampleType::Caption).samples.push_back({ caption_contents, (uint32_t)pts, (uint32_t)dts, true });
THROW_IF(tracks(SampleType::Caption).samples.size() >= kMaxMP2TSSampleCount, Unsafe);
video.cache.clear();
}