blob: 79bbb8a7b67732e9226f4740f0d73d6cfb0a2d30 [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/common_video/interface/incoming_video_stream.h"
#include <assert.h>
#if defined(_WIN32)
#include <windows.h>
#elif defined(WEBRTC_LINUX)
#include <sys/time.h>
#include <time.h>
#else
#include <sys/time.h>
#endif
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/common_video/video_render_frames.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
#include "webrtc/system_wrappers/include/event_wrapper.h"
#include "webrtc/system_wrappers/include/thread_wrapper.h"
#include "webrtc/system_wrappers/include/tick_util.h"
#include "webrtc/system_wrappers/include/trace.h"
namespace webrtc {
IncomingVideoStream::IncomingVideoStream(uint32_t stream_id)
: stream_id_(stream_id),
stream_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
thread_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
buffer_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
incoming_render_thread_(),
deliver_buffer_event_(EventTimerWrapper::Create()),
running_(false),
external_callback_(nullptr),
render_callback_(nullptr),
render_buffers_(new VideoRenderFrames()),
incoming_rate_(0),
last_rate_calculation_time_ms_(0),
num_frames_since_last_calculation_(0),
last_render_time_ms_(0),
temp_frame_(),
start_image_(),
timeout_image_(),
timeout_time_() {
}
IncomingVideoStream::~IncomingVideoStream() {
Stop();
}
VideoRenderCallback* IncomingVideoStream::ModuleCallback() {
CriticalSectionScoped cs(stream_critsect_.get());
return this;
}
int32_t IncomingVideoStream::RenderFrame(const uint32_t stream_id,
const VideoFrame& video_frame) {
CriticalSectionScoped csS(stream_critsect_.get());
if (!running_) {
return -1;
}
// Rate statistics.
num_frames_since_last_calculation_++;
int64_t now_ms = TickTime::MillisecondTimestamp();
if (now_ms >= last_rate_calculation_time_ms_ + kFrameRatePeriodMs) {
incoming_rate_ =
static_cast<uint32_t>(1000 * num_frames_since_last_calculation_ /
(now_ms - last_rate_calculation_time_ms_));
num_frames_since_last_calculation_ = 0;
last_rate_calculation_time_ms_ = now_ms;
}
// Insert frame.
CriticalSectionScoped csB(buffer_critsect_.get());
if (render_buffers_->AddFrame(video_frame) == 1)
deliver_buffer_event_->Set();
return 0;
}
int32_t IncomingVideoStream::SetStartImage(const VideoFrame& video_frame) {
CriticalSectionScoped csS(thread_critsect_.get());
return start_image_.CopyFrame(video_frame);
}
int32_t IncomingVideoStream::SetTimeoutImage(const VideoFrame& video_frame,
const uint32_t timeout) {
CriticalSectionScoped csS(thread_critsect_.get());
timeout_time_ = timeout;
return timeout_image_.CopyFrame(video_frame);
}
void IncomingVideoStream::SetRenderCallback(
VideoRenderCallback* render_callback) {
CriticalSectionScoped cs(thread_critsect_.get());
render_callback_ = render_callback;
}
int32_t IncomingVideoStream::SetExpectedRenderDelay(
int32_t delay_ms) {
CriticalSectionScoped csS(stream_critsect_.get());
if (running_) {
return -1;
}
CriticalSectionScoped cs(buffer_critsect_.get());
return render_buffers_->SetRenderDelay(delay_ms);
}
void IncomingVideoStream::SetExternalCallback(
VideoRenderCallback* external_callback) {
CriticalSectionScoped cs(thread_critsect_.get());
external_callback_ = external_callback;
}
int32_t IncomingVideoStream::Start() {
CriticalSectionScoped csS(stream_critsect_.get());
if (running_) {
return 0;
}
CriticalSectionScoped csT(thread_critsect_.get());
assert(incoming_render_thread_ == NULL);
incoming_render_thread_ = ThreadWrapper::CreateThread(
IncomingVideoStreamThreadFun, this, "IncomingVideoStreamThread");
if (!incoming_render_thread_) {
return -1;
}
if (incoming_render_thread_->Start()) {
} else {
return -1;
}
incoming_render_thread_->SetPriority(kRealtimePriority);
deliver_buffer_event_->StartTimer(false, kEventStartupTimeMs);
running_ = true;
return 0;
}
int32_t IncomingVideoStream::Stop() {
CriticalSectionScoped cs_stream(stream_critsect_.get());
if (!running_) {
return 0;
}
ThreadWrapper* thread = NULL;
{
CriticalSectionScoped cs_thread(thread_critsect_.get());
if (incoming_render_thread_) {
// Setting the incoming render thread to NULL marks that we're performing
// a shutdown and will make IncomingVideoStreamProcess abort after wakeup.
thread = incoming_render_thread_.release();
deliver_buffer_event_->StopTimer();
// Set the event to allow the thread to wake up and shut down without
// waiting for a timeout.
deliver_buffer_event_->Set();
}
}
if (thread) {
if (thread->Stop()) {
delete thread;
} else {
assert(false);
}
}
running_ = false;
return 0;
}
int32_t IncomingVideoStream::Reset() {
CriticalSectionScoped cs_buffer(buffer_critsect_.get());
render_buffers_->ReleaseAllFrames();
return 0;
}
uint32_t IncomingVideoStream::StreamId() const {
return stream_id_;
}
uint32_t IncomingVideoStream::IncomingRate() const {
CriticalSectionScoped cs(stream_critsect_.get());
return incoming_rate_;
}
bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) {
return static_cast<IncomingVideoStream*>(obj)->IncomingVideoStreamProcess();
}
bool IncomingVideoStream::IncomingVideoStreamProcess() {
if (kEventError != deliver_buffer_event_->Wait(kEventMaxWaitTimeMs)) {
CriticalSectionScoped cs(thread_critsect_.get());
if (incoming_render_thread_ == NULL) {
// Terminating
return false;
}
// Get a new frame to render and the time for the frame after this one.
VideoFrame frame_to_render;
uint32_t wait_time;
{
CriticalSectionScoped cs(buffer_critsect_.get());
frame_to_render = render_buffers_->FrameToRender();
wait_time = render_buffers_->TimeToNextFrameRelease();
}
// Set timer for next frame to render.
if (wait_time > kEventMaxWaitTimeMs) {
wait_time = kEventMaxWaitTimeMs;
}
deliver_buffer_event_->StartTimer(false, wait_time);
if (frame_to_render.IsZeroSize()) {
if (render_callback_) {
if (last_render_time_ms_ == 0 && !start_image_.IsZeroSize()) {
// We have not rendered anything and have a start image.
temp_frame_.CopyFrame(start_image_);
render_callback_->RenderFrame(stream_id_, temp_frame_);
} else if (!timeout_image_.IsZeroSize() &&
last_render_time_ms_ + timeout_time_ <
TickTime::MillisecondTimestamp()) {
// Render a timeout image.
temp_frame_.CopyFrame(timeout_image_);
render_callback_->RenderFrame(stream_id_, temp_frame_);
}
}
// No frame.
return true;
}
// Send frame for rendering.
if (external_callback_) {
external_callback_->RenderFrame(stream_id_, frame_to_render);
} else if (render_callback_) {
render_callback_->RenderFrame(stream_id_, frame_to_render);
}
// We're done with this frame.
if (!frame_to_render.IsZeroSize())
last_render_time_ms_ = frame_to_render.render_time_ms();
}
return true;
}
} // namespace webrtc