in vireo/mux/mp2ts.cpp [96:202]
std::vector<common::Data32> cache_and_flush(int64_t pts, int64_t dts, bool keyframe,
const std::vector<common::Data32>& video_frame) {
// This function caches some of the data in video_frame (maybe none), and
// returns a vector containing data for a PES packet. You must use the
// return value of this function before calling cache_and_flush() again.
remaining_frames--;
std::vector<common::Data32> result_packet;
// Clear cached_data, and move cached contents to result.
result_packet.swap(cached_data);
if (video_frame.empty()) {
// Nothing to write. Exit early.
return result_packet;
}
// Add NAL delimiter before each frame. If we don't add it explicitly,
// libavformat would add it for us -- even for the first frame.
result_packet.emplace_back(nalu_aud.data() + nalu_aud.a(),
nalu_aud.count(), nullptr /* deleter */);
// We might split the last video_frame item. Safe to return the rest.
for (uint32_t i = 0; i < video_frame.size() - 1; ++i) {
result_packet.emplace_back(video_frame[i].data() + video_frame[i].a(),
video_frame[i].count(), nullptr /* deleter */);
}
// Calculate packet size of all data, if we wouldn't split.
uint32_t desired_packet_size = 0;
for (const auto& data: result_packet) {
desired_packet_size += data.count();
}
desired_packet_size += video_frame.back().count();
// Compute mandatory overhead for starting a PES.
uint32_t overhead = 3; // PES start code prefix
overhead += 1; // PES stream ID
overhead += 2; // PES packet length;
overhead += 2; // flags
overhead += 1; // PES header data length field.
CHECK(pts != AV_NOPTS_VALUE);
overhead += 5;
CHECK(dts != AV_NOPTS_VALUE);
if (dts != pts) {
overhead += 5;
}
if (keyframe) {
// Adaptation field. On a keyframe, libavformat sets the PCR, and it
// sets Random Access Indicator bit, telling us we can decode without
// errors from this point.
//
overhead += 8;
}
const uint32_t max_overhead = 3 + 1 + 2 + 2 + 1 + 5 + 5 + 8;
// It only makes sense to end this packet on a TS packet end. Otherwise,
// we wouldn't save space.
uint32_t num_bytes_to_cache = (desired_packet_size + overhead) % kTSPayloadSize;
bool split_frame = false;
// We only split if the packet is more than the payload size,
// and we can safely start a new packet afterwards.
// We need to be able to at least put the NALU delimiter for the next
// frame.
if (desired_packet_size + overhead > kTSPayloadSize &&
num_bytes_to_cache + max_overhead + kNALUDelimiterSize <= kTSPayloadSize &&
remaining_frames > 0) {
split_frame = true;
}
if (!split_frame) {
num_bytes_to_cache = 0;
}
common::Data32 frame_data_h264_annexb(
video_frame.back().data() + video_frame.back().a(),
video_frame.back().count(), nullptr /* deleter */);
internal::decode::ANNEXB<internal::decode::H264NalType> annexb_parser(frame_data_h264_annexb);
if (annexb_parser.count() == 0) {
// Do not cache if we can't recognise the video data format.
num_bytes_to_cache = 0;
} else {
const uint32_t last_nal_offset = annexb_parser(annexb_parser.count() - 1).byte_offset;
// We must split after the last nal because the PTS & DTS from the
// PES apply to the first NAL unit inside this PES - this must be the
// NAL unit delimiter for the next frame.
//
CHECK(frame_data_h264_annexb.count() >= last_nal_offset);
if (frame_data_h264_annexb.count() - last_nal_offset < num_bytes_to_cache) {
num_bytes_to_cache = 0;
}
}
frame_data_h264_annexb.set_bounds(frame_data_h264_annexb.a(),
frame_data_h264_annexb.b() - num_bytes_to_cache);
result_packet.emplace_back(frame_data_h264_annexb.data() + frame_data_h264_annexb.a(),
frame_data_h264_annexb.count(), nullptr /* deleter */);
if (num_bytes_to_cache > 0) {
// Copy the last num_bytes_to_cache bytes from frame_data_h264_annexb
// to data cache.
frame_data_h264_annexb.set_bounds(frame_data_h264_annexb.b(),
frame_data_h264_annexb.b() + num_bytes_to_cache);
// Copy-construct data to be cached.
cached_data.emplace_back(frame_data_h264_annexb);
}
return result_packet;
}