// Copyright 2015 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 "content/common/gpu/media/vp8_decoder.h"
#include "media/base/limits.h"

namespace content {

VP8Decoder::VP8Accelerator::VP8Accelerator() {
}

VP8Decoder::VP8Accelerator::~VP8Accelerator() {
}

VP8Decoder::VP8Decoder(VP8Accelerator* accelerator)
    : state_(kNeedStreamMetadata),
      curr_frame_start_(nullptr),
      frame_size_(0),
      accelerator_(accelerator) {
  DCHECK(accelerator_);
}

VP8Decoder::~VP8Decoder() {
}

bool VP8Decoder::Flush() {
  DVLOG(2) << "Decoder flush";
  Reset();
  return true;
}

void VP8Decoder::SetStream(const uint8_t* ptr, size_t size) {
  DCHECK(ptr);
  DCHECK(size);

  curr_frame_start_ = ptr;
  frame_size_ = size;
  DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
}

void VP8Decoder::Reset() {
  curr_pic_ = nullptr;
  curr_frame_hdr_ = nullptr;
  curr_frame_start_ = nullptr;
  frame_size_ = 0;

  last_frame_ = nullptr;
  golden_frame_ = nullptr;
  alt_frame_ = nullptr;

  if (state_ == kDecoding)
    state_ = kAfterReset;
}

VP8Decoder::DecodeResult VP8Decoder::Decode() {
  if (!curr_frame_start_ || frame_size_ == 0)
    return kRanOutOfStreamData;

  if (!curr_frame_hdr_) {
    curr_frame_hdr_.reset(new media::Vp8FrameHeader());
    if (!parser_.ParseFrame(curr_frame_start_, frame_size_,
                            curr_frame_hdr_.get())) {
      DVLOG(1) << "Error during decode";
      state_ = kError;
      return kDecodeError;
    }
  }

  if (curr_frame_hdr_->IsKeyframe()) {
    gfx::Size new_pic_size(curr_frame_hdr_->width, curr_frame_hdr_->height);
    if (new_pic_size.IsEmpty())
      return kDecodeError;

    if (new_pic_size != pic_size_) {
      DVLOG(2) << "New resolution: " << new_pic_size.ToString();
      pic_size_ = new_pic_size;

      DCHECK(!curr_pic_);
      last_frame_ = nullptr;
      golden_frame_ = nullptr;
      alt_frame_ = nullptr;

      return kAllocateNewSurfaces;
    }

    state_ = kDecoding;
  } else {
    if (state_ != kDecoding) {
      // Need a resume point.
      curr_frame_hdr_.reset();
      return kRanOutOfStreamData;
    }
  }

  curr_pic_ = accelerator_->CreateVP8Picture();
  if (!curr_pic_)
    return kRanOutOfSurfaces;

  if (!DecodeAndOutputCurrentFrame())
    return kDecodeError;

  return kRanOutOfStreamData;
}

void VP8Decoder::RefreshReferenceFrames() {
  if (curr_frame_hdr_->IsKeyframe()) {
    last_frame_ = curr_pic_;
    golden_frame_ = curr_pic_;
    alt_frame_ = curr_pic_;
    return;
  }

  // Save current golden since we overwrite it here,
  // but may have to use it to update alt below.
  scoped_refptr<VP8Picture> curr_golden = golden_frame_;

  if (curr_frame_hdr_->refresh_golden_frame) {
    golden_frame_ = curr_pic_;
  } else {
    switch (curr_frame_hdr_->copy_buffer_to_golden) {
      case media::Vp8FrameHeader::COPY_LAST_TO_GOLDEN:
        DCHECK(last_frame_);
        golden_frame_ = last_frame_;
        break;

      case media::Vp8FrameHeader::COPY_ALT_TO_GOLDEN:
        DCHECK(alt_frame_);
        golden_frame_ = alt_frame_;
        break;
    }
  }

  if (curr_frame_hdr_->refresh_alternate_frame) {
    alt_frame_ = curr_pic_;
  } else {
    switch (curr_frame_hdr_->copy_buffer_to_alternate) {
      case media::Vp8FrameHeader::COPY_LAST_TO_ALT:
        DCHECK(last_frame_);
        alt_frame_ = last_frame_;
        break;

      case media::Vp8FrameHeader::COPY_GOLDEN_TO_ALT:
        DCHECK(curr_golden);
        alt_frame_ = curr_golden;
        break;
    }
  }

  if (curr_frame_hdr_->refresh_last)
    last_frame_ = curr_pic_;
}

bool VP8Decoder::DecodeAndOutputCurrentFrame() {
  DCHECK(!pic_size_.IsEmpty());
  DCHECK(curr_pic_);
  DCHECK(curr_frame_hdr_);

  if (curr_frame_hdr_->IsKeyframe()) {
    horizontal_scale_ = curr_frame_hdr_->horizontal_scale;
    vertical_scale_ = curr_frame_hdr_->vertical_scale;
  } else {
    // Populate fields from decoder state instead.
    curr_frame_hdr_->width = pic_size_.width();
    curr_frame_hdr_->height = pic_size_.height();
    curr_frame_hdr_->horizontal_scale = horizontal_scale_;
    curr_frame_hdr_->vertical_scale = vertical_scale_;
  }

  if (!accelerator_->SubmitDecode(curr_pic_, curr_frame_hdr_.get(), last_frame_,
                                  golden_frame_, alt_frame_))
    return false;

  if (curr_frame_hdr_->show_frame)
    if (!accelerator_->OutputPicture(curr_pic_))
      return false;

  RefreshReferenceFrames();

  curr_pic_ = nullptr;
  curr_frame_hdr_ = nullptr;
  curr_frame_start_ = nullptr;
  frame_size_ = 0;
  return true;
}

gfx::Size VP8Decoder::GetPicSize() const {
  return pic_size_;
}

size_t VP8Decoder::GetRequiredNumOfPictures() const {
  const size_t kVP8NumFramesActive = 4;
  const size_t kPicsInPipeline = media::limits::kMaxVideoFrames + 2;
  return kVP8NumFramesActive + kPicsInPipeline;
}

}  // namespace content
