// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/platform/peerconnection/webrtc_util.h"

#include <cstring>
#include <optional>

#include "base/feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/video_codecs.h"
#include "media/media_buildflags.h"
#include "media/webrtc/webrtc_features.h"
#include "third_party/blink/public/common/buildflags.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/webrtc/api/units/time_delta.h"
#include "third_party/webrtc/api/video_codecs/sdp_video_format.h"

namespace blink {

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
    BUILDFLAG(IS_ANDROID)
// Enables H.264 CBP encode acceleration.
BASE_FEATURE(kPlatformH264CbpEncoding,
#if BUILDFLAG(IS_WIN)
             base::FEATURE_DISABLED_BY_DEFAULT);
#else
             base::FEATURE_ENABLED_BY_DEFAULT);
#endif  // BUILDFLAG(IS_WIN)
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
        // BUILDFLAG(IS_ANDROID)

StringView WebrtcCodecNameFromMimeType(const StringView& mime_type,
                                       const StringView& prefix) {
  if (mime_type.starts_with(prefix) && mime_type.length() > prefix.length()) {
    return mime_type.substr(prefix.length() + 1);
  }
  return "";
}

std::map<std::string, std::string> ConvertToSdpVideoFormatParameters(
    const ParsedContentHeaderFieldParameters& parameters) {
  std::map<std::string, std::string> sdp_parameters;
  for (const auto& parameter : parameters) {
    sdp_parameters[parameter.name.Utf8()] = parameter.value.Utf8();
  }
  return sdp_parameters;
}

base::TimeTicks PLATFORM_EXPORT ConvertToBaseTimeTicks(webrtc::Timestamp time) {
  if (time == webrtc::Timestamp::PlusInfinity()) {
    return base::TimeTicks::Max();
  } else if (time == webrtc::Timestamp::MinusInfinity()) {
    return base::TimeTicks::Min();
  } else {
    return base::TimeTicks() + base::Microseconds(time.us());
  }
}

base::TimeDelta PLATFORM_EXPORT
ConvertToBaseTimeDelta(webrtc::TimeDelta time_delta) {
  if (time_delta == webrtc::TimeDelta::PlusInfinity()) {
    return base::TimeDelta::Max();
  } else if (time_delta == webrtc::TimeDelta::MinusInfinity()) {
    return base::TimeDelta::Min();
  } else {
    return base::Microseconds(time_delta.us());
  }
}

std::optional<media::VideoCodecProfile> WebRTCFormatToCodecProfile(
    const webrtc::SdpVideoFormat& sdp) {
  if (sdp.name == "H264") {
    if (UseH264AcceleratedEncoderForWebRTC()) {
      return media::VideoCodecProfile::H264PROFILE_MIN;
    } else {
      return std::nullopt;
    }
  } else if (sdp.name == "VP8") {
    return media::VideoCodecProfile::VP8PROFILE_MIN;
  } else if (sdp.name == "VP9") {
    return media::VideoCodecProfile::VP9PROFILE_MIN;
  } else if (sdp.name == "AV1") {
    return media::VideoCodecProfile::AV1PROFILE_MIN;
  }
#if BUILDFLAG(RTC_USE_H265)
  else if (sdp.name == "H265") {
    return media::VideoCodecProfile::HEVCPROFILE_MIN;
  }
#endif  // BUILDFLAG(RTC_USE_H265)
  return std::nullopt;
}

std::optional<base::TimeTicks> PLATFORM_EXPORT
ConvertToOptionalTimeTicks(std::optional<webrtc::Timestamp> time,
                           std::optional<base::TimeTicks> offset) {
  // Converting minimal timestamps to DOMHighResTimeStamps can result in UB.
  // Return nullopt in that case. See https://crbug.com/399818722
  if (!time || time->IsMinusInfinity()) {
    return std::nullopt;
  }
  base::TimeTicks time_ticks = ConvertToBaseTimeTicks(*time);
  if (offset) {
    return *offset + (time_ticks - base::TimeTicks());
  }
  return time_ticks;
}

std::optional<base::TimeDelta> PLATFORM_EXPORT
ConvertToOptionalTimeDelta(std::optional<webrtc::TimeDelta> time_delta) {
  // Converting minimal deltas to DOMHighResTimeStamps can result in UB.
  // Return nullopt in that case. See https://crbug.com/399818722
  if (!time_delta || time_delta->IsMinusInfinity()) {
    return std::nullopt;
  }
  return ConvertToBaseTimeDelta(*time_delta);
}

bool PLATFORM_EXPORT
IsH264ConstrainedBaselineProfileAvailableForAcceleratedEncoder() {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
    BUILDFLAG(IS_ANDROID)
  return base::FeatureList::IsEnabled(kPlatformH264CbpEncoding);
#else
  return false;
#endif
}

bool PLATFORM_EXPORT UseH264AcceleratedEncoderForWebRTC() {
#if !BUILDFLAG(IS_ANDROID)
  // On non-Android, H264 HW encoder cannot be used unless SW encoder is also
  // available because of assumptions that SW fallback is always possible. This
  // check should be removed when SW implementation is always available.
  return ::features::IsOpenH264SoftwareEncoderEnabledForWebRTC();
#else
  // On Android, H264 HW encoder can be used without SW encoder because the SW
  // fallback logic can be explicitly disabled.
  return true;
#endif
}

}  // namespace blink
