std::vector cache_and_flush()

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;
    }