/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WebrtcVideoCodecFactory.h"

#include "GmpVideoCodec.h"
#include "MediaDataCodec.h"
#include "VideoConduit.h"
#include "mozilla/StaticPrefs_media.h"

// libwebrtc includes
#include "api/rtp_headers.h"
#include "api/video_codecs/video_codec.h"
#include "api/video_codecs/video_encoder_software_fallback_wrapper.h"
#include "media/engine/simulcast_encoder_adapter.h"
#include "modules/video_coding/codecs/vp8/include/vp8.h"
#include "modules/video_coding/codecs/vp9/include/vp9.h"

namespace mozilla {

std::unique_ptr<webrtc::VideoDecoder>
WebrtcVideoDecoderFactory::CreateVideoDecoder(
    const webrtc::SdpVideoFormat& aFormat) {
  std::unique_ptr<webrtc::VideoDecoder> decoder;
  auto type = webrtc::PayloadStringToCodecType(aFormat.name);

  // Attempt to create a decoder using MediaDataDecoder.
  decoder.reset(MediaDataCodec::CreateDecoder(type, mTrackingId));
  if (decoder) {
    return decoder;
  }

  switch (type) {
    case webrtc::VideoCodecType::kVideoCodecH264: {
      // Get an external decoder
      auto gmpDecoder =
          WrapUnique(GmpVideoCodec::CreateDecoder(mPCHandle, mTrackingId));
      mCreatedGmpPluginEvent.Forward(*gmpDecoder->InitPluginEvent());
      mReleasedGmpPluginEvent.Forward(*gmpDecoder->ReleasePluginEvent());
      decoder.reset(gmpDecoder.release());
      break;
    }

    // Use libvpx decoders as fallbacks.
    case webrtc::VideoCodecType::kVideoCodecVP8:
      if (!decoder) {
        decoder = webrtc::VP8Decoder::Create();
      }
      break;
    case webrtc::VideoCodecType::kVideoCodecVP9:
      decoder = webrtc::VP9Decoder::Create();
      break;

    default:
      break;
  }

  return decoder;
}

std::unique_ptr<webrtc::VideoEncoder>
WebrtcVideoEncoderFactory::CreateVideoEncoder(
    const webrtc::SdpVideoFormat& aFormat) {
  if (!mInternalFactory->Supports(aFormat)) {
    return nullptr;
  }
  auto type = webrtc::PayloadStringToCodecType(aFormat.name);
  switch (type) {
    case webrtc::VideoCodecType::kVideoCodecVP8:
      // XXX We might be able to use the simulcast proxy for more codecs, but
      // that requires testing.
      return std::make_unique<webrtc::SimulcastEncoderAdapter>(
          mInternalFactory.get(), aFormat);
    default:
      return mInternalFactory->CreateVideoEncoder(aFormat);
  }
}

bool WebrtcVideoEncoderFactory::InternalFactory::Supports(
    const webrtc::SdpVideoFormat& aFormat) {
  switch (webrtc::PayloadStringToCodecType(aFormat.name)) {
    case webrtc::VideoCodecType::kVideoCodecVP8:
    case webrtc::VideoCodecType::kVideoCodecVP9:
    case webrtc::VideoCodecType::kVideoCodecH264:
      return true;
    default:
      return false;
  }
}

std::unique_ptr<webrtc::VideoEncoder>
WebrtcVideoEncoderFactory::InternalFactory::CreateVideoEncoder(
    const webrtc::SdpVideoFormat& aFormat) {
  MOZ_ASSERT(Supports(aFormat));

  std::unique_ptr<webrtc::VideoEncoder> platformEncoder;
  platformEncoder.reset(MediaDataCodec::CreateEncoder(aFormat));
  const bool fallback = StaticPrefs::media_webrtc_software_encoder_fallback();
  if (!fallback && platformEncoder) {
    return platformEncoder;
  }

  std::unique_ptr<webrtc::VideoEncoder> encoder;
  switch (webrtc::PayloadStringToCodecType(aFormat.name)) {
    case webrtc::VideoCodecType::kVideoCodecH264: {
      // get an external encoder
      auto gmpEncoder =
          WrapUnique(GmpVideoCodec::CreateEncoder(aFormat, mPCHandle));
      mCreatedGmpPluginEvent.Forward(*gmpEncoder->InitPluginEvent());
      mReleasedGmpPluginEvent.Forward(*gmpEncoder->ReleasePluginEvent());
      encoder.reset(gmpEncoder.release());
      break;
    }
    // libvpx fallbacks.
    case webrtc::VideoCodecType::kVideoCodecVP8:
      if (!encoder) {
        encoder = webrtc::VP8Encoder::Create();
      }
      break;
    case webrtc::VideoCodecType::kVideoCodecVP9:
      encoder = webrtc::VP9Encoder::Create();
      break;

    default:
      break;
  }
  if (fallback && encoder && platformEncoder) {
    return webrtc::CreateVideoEncoderSoftwareFallbackWrapper(
        std::move(encoder), std::move(platformEncoder), false);
  }
  if (platformEncoder) {
    return platformEncoder;
  }
  return encoder;
}

}  // namespace mozilla
