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

#include "media/video/fake_video_encode_accelerator.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"

namespace media {

static const unsigned int kMinimumInputCount = 1;

FakeVideoEncodeAccelerator::FakeVideoEncodeAccelerator(
    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
    : task_runner_(task_runner),
      will_initialization_succeed_(true),
      client_(NULL),
      next_frame_is_first_frame_(true),
      weak_this_factory_(this) {}

FakeVideoEncodeAccelerator::~FakeVideoEncodeAccelerator() {
  weak_this_factory_.InvalidateWeakPtrs();
}

VideoEncodeAccelerator::SupportedProfiles
FakeVideoEncodeAccelerator::GetSupportedProfiles() {
  SupportedProfiles profiles;
  SupportedProfile profile;
  profile.max_resolution.SetSize(1920, 1088);
  profile.max_framerate_numerator = 30;
  profile.max_framerate_denominator = 1;

  profile.profile = media::H264PROFILE_MAIN;
  profiles.push_back(profile);
  profile.profile = media::VP8PROFILE_ANY;
  profiles.push_back(profile);
  return profiles;
}

bool FakeVideoEncodeAccelerator::Initialize(VideoPixelFormat input_format,
                                            const gfx::Size& input_visible_size,
                                            VideoCodecProfile output_profile,
                                            uint32_t initial_bitrate,
                                            Client* client) {
  if (!will_initialization_succeed_) {
    return false;
  }
  if (output_profile == VIDEO_CODEC_PROFILE_UNKNOWN ||
      output_profile > VIDEO_CODEC_PROFILE_MAX) {
    return false;
  }
  client_ = client;
  task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&FakeVideoEncodeAccelerator::DoRequireBitstreamBuffers,
                 weak_this_factory_.GetWeakPtr(),
                 kMinimumInputCount,
                 input_visible_size,
                 kMinimumOutputBufferSize));
  return true;
}

void FakeVideoEncodeAccelerator::Encode(
    const scoped_refptr<VideoFrame>& frame,
    bool force_keyframe) {
  DCHECK(client_);
  queued_frames_.push(force_keyframe);
  EncodeTask();
}

void FakeVideoEncodeAccelerator::UseOutputBitstreamBuffer(
    const BitstreamBuffer& buffer) {
  available_buffers_.push_back(buffer);
  EncodeTask();
}

void FakeVideoEncodeAccelerator::RequestEncodingParametersChange(
    uint32_t bitrate,
    uint32_t framerate) {
  stored_bitrates_.push_back(bitrate);
}

void FakeVideoEncodeAccelerator::Destroy() { delete this; }

void FakeVideoEncodeAccelerator::SendDummyFrameForTesting(bool key_frame) {
  task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&FakeVideoEncodeAccelerator::DoBitstreamBufferReady,
                   weak_this_factory_.GetWeakPtr(),
                   0,
                   23,
                   key_frame));
}

void FakeVideoEncodeAccelerator::SetWillInitializationSucceed(
    bool will_initialization_succeed) {
  will_initialization_succeed_ = will_initialization_succeed;
}

void FakeVideoEncodeAccelerator::DoRequireBitstreamBuffers(
    unsigned int input_count,
    const gfx::Size& input_coded_size,
    size_t output_buffer_size) const {
  client_->RequireBitstreamBuffers(
      input_count, input_coded_size, output_buffer_size);
}

void FakeVideoEncodeAccelerator::EncodeTask() {
  while (!queued_frames_.empty() && !available_buffers_.empty()) {
    bool force_key_frame = queued_frames_.front();
    queued_frames_.pop();
    int32_t bitstream_buffer_id = available_buffers_.front().id();
    available_buffers_.pop_front();
    bool key_frame = next_frame_is_first_frame_ || force_key_frame;
    next_frame_is_first_frame_ = false;
    task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&FakeVideoEncodeAccelerator::DoBitstreamBufferReady,
                   weak_this_factory_.GetWeakPtr(),
                   bitstream_buffer_id,
                   kMinimumOutputBufferSize,
                   key_frame));
  }
}

void FakeVideoEncodeAccelerator::DoBitstreamBufferReady(
    int32_t bitstream_buffer_id,
    size_t payload_size,
    bool key_frame) const {
  client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame,
                                base::Time::Now() - base::Time());
}

}  // namespace media
