void process_h264_frame()

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