// Copyright (c) 2013 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/renderer/media/video_track_to_pepper_adapter.h"

#include <string>

#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/trace_event/trace_event.h"
#include "content/public/renderer/media_stream_video_sink.h"
#include "content/renderer/media/media_stream.h"
#include "content/renderer/media/media_stream_registry_interface.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_capture_types.h"
#include "third_party/WebKit/public/platform/WebMediaStream.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"
#include "url/gurl.h"

namespace content {

// PpFrameReceiver implements MediaStreamVideoSink so that it can be attached
// to video track to receive captured frames.
// It can be attached to a FrameReaderInterface to output the received frame.
class PpFrameReceiver : public MediaStreamVideoSink {
 public:
  PpFrameReceiver(blink::WebMediaStreamTrack track)
    : track_(track),
      reader_(NULL),
      weak_factory_(this) {
  }

  ~PpFrameReceiver() override {}

  void SetReader(FrameReaderInterface* reader) {
    DCHECK((reader_ && !reader) || (!reader_ && reader))
        << " |reader| = " << reader << ", |reader_| = " << reader_;
    if (reader) {
      AddToVideoTrack(this,
                      media::BindToCurrentLoop(
                          base::Bind(&PpFrameReceiver::OnVideoFrame,
                                     weak_factory_.GetWeakPtr())),
                      track_);
    } else {
      RemoveFromVideoTrack(this, track_);
      weak_factory_.InvalidateWeakPtrs();
    }
    reader_ = reader;
  }

  void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
                    base::TimeTicks estimated_capture_time) {
    TRACE_EVENT0("video", "PpFrameReceiver::OnVideoFrame");
    if (reader_)
      reader_->GotFrame(frame);
  }

 private:
  const blink::WebMediaStreamTrack track_;
  FrameReaderInterface* reader_;
  base::WeakPtrFactory<PpFrameReceiver> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(PpFrameReceiver);
};

VideoTrackToPepperAdapter::VideoTrackToPepperAdapter(
    MediaStreamRegistryInterface* registry)
    : registry_(registry) {}

VideoTrackToPepperAdapter::~VideoTrackToPepperAdapter() {
  for (const auto& reader_and_receiver : reader_to_receiver_)
    delete reader_and_receiver.second;
}

bool VideoTrackToPepperAdapter::Open(const std::string& url,
                              FrameReaderInterface* reader) {
  DCHECK(thread_checker_.CalledOnValidThread());
  const blink::WebMediaStreamTrack& track = GetFirstVideoTrack(url);
  if (track.isNull())
    return false;
  reader_to_receiver_[reader] = new SourceInfo(track, reader);
  return true;
}

bool VideoTrackToPepperAdapter::Close(FrameReaderInterface* reader) {
  DCHECK(thread_checker_. CalledOnValidThread());
  SourceInfoMap::iterator it = reader_to_receiver_.find(reader);
  if (it == reader_to_receiver_.end())
    return false;
  delete it->second;
  reader_to_receiver_.erase(it);
  return true;
}

blink::WebMediaStreamTrack VideoTrackToPepperAdapter::GetFirstVideoTrack(
    const std::string& url) {
  DCHECK(thread_checker_.CalledOnValidThread());
  const blink::WebMediaStream stream = registry_
      ? registry_->GetMediaStream(url)
      : blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url));

  if (stream.isNull()) {
    LOG(ERROR) << "GetFirstVideoSource - invalid url: " << url;
    return blink::WebMediaStreamTrack();
  }

  // Get the first video track from the stream.
  blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
  stream.videoTracks(video_tracks);
  if (video_tracks.isEmpty()) {
    LOG(ERROR) << "GetFirstVideoSource - no video tracks. url: " << url;
    return blink::WebMediaStreamTrack();
  }

  return video_tracks[0];
}

void VideoTrackToPepperAdapter::DeliverFrameForTesting(
    FrameReaderInterface* reader,
    const scoped_refptr<media::VideoFrame>& frame) {
  SourceInfoMap::const_iterator it = reader_to_receiver_.find(reader);
  if (it == reader_to_receiver_.end())
    return;
  PpFrameReceiver* receiver = it->second->receiver_.get();
  receiver->OnVideoFrame(frame, base::TimeTicks());
}

VideoTrackToPepperAdapter::SourceInfo::SourceInfo(
    const blink::WebMediaStreamTrack& blink_track,
    FrameReaderInterface* reader)
    : receiver_(new PpFrameReceiver(blink_track)) {
  receiver_->SetReader(reader);
}

VideoTrackToPepperAdapter::SourceInfo::~SourceInfo() {
  receiver_->SetReader(NULL);
}

}  // namespace content
