| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/shared/win32/video_decoder_thread.h" |
| |
| #include <deque> |
| |
| namespace starboard { |
| namespace shared { |
| namespace win32 { |
| |
| namespace { |
| const size_t kMaxSize = 16; |
| |
| size_t WriteAsMuchAsPossible( |
| std::deque<scoped_refptr<InputBuffer> >* data_queue, |
| AbstractWin32VideoDecoder* video_decoder, |
| bool* is_end_of_stream_reached) { |
| const size_t original_size = data_queue->size(); |
| while (!data_queue->empty()) { |
| scoped_refptr<InputBuffer> buff = data_queue->front(); |
| data_queue->pop_front(); |
| |
| if (buff) { |
| const bool write_ok = video_decoder->TryWrite(buff); |
| |
| if (!write_ok) { |
| data_queue->push_front(buff); |
| break; |
| } |
| } else { |
| video_decoder->WriteEndOfStream(); |
| *is_end_of_stream_reached = true; |
| } |
| } |
| return original_size - data_queue->size(); |
| } |
| |
| } // namespace. |
| |
| VideoDecoderThread::VideoDecoderThread(AbstractWin32VideoDecoder* decoder_impl, |
| VideoDecodedCallback* callback) |
| : SimpleThread("VideoDecoderThread"), |
| win32_video_decoder_(decoder_impl), |
| callback_(callback) { |
| Start(); |
| } |
| |
| VideoDecoderThread::~VideoDecoderThread() { |
| Join(); |
| SB_DCHECK(join_called()); |
| } |
| |
| bool VideoDecoderThread::QueueInput(const scoped_refptr<InputBuffer>& buffer) { |
| { |
| ::starboard::ScopedLock lock(input_buffer_queue_mutex_); |
| input_buffer_queue_.push_back(buffer); |
| } |
| |
| // increment() returns the prev value. |
| size_t proc_size = processing_elements_.increment() + 1; |
| semaphore_.Put(); |
| return proc_size < kMaxSize; |
| } |
| |
| void VideoDecoderThread::QueueEndOfStream() { |
| scoped_refptr<InputBuffer> empty; |
| QueueInput(empty); |
| } |
| |
| void VideoDecoderThread::TransferPendingInputTo( |
| std::deque<scoped_refptr<InputBuffer> >* output) { |
| // Transfer input buffer to local thread. |
| ::starboard::ScopedLock lock(input_buffer_queue_mutex_); |
| while (!input_buffer_queue_.empty()) { |
| output->push_back(input_buffer_queue_.front()); |
| input_buffer_queue_.pop_front(); |
| } |
| } |
| |
| void VideoDecoderThread::Run() { |
| std::deque<scoped_refptr<InputBuffer> > local_queue; |
| while (!join_called()) { |
| if (local_queue.empty()) { |
| TransferPendingInputTo(&local_queue); |
| } |
| bool work_done = false; |
| bool is_end_of_stream = false; |
| const size_t number_written = |
| WriteAsMuchAsPossible(&local_queue, win32_video_decoder_, |
| &is_end_of_stream); |
| if (number_written > 0) { |
| processing_elements_.fetch_sub(static_cast<int32_t>(number_written)); |
| work_done = true; |
| callback_->OnVideoDecoded(NULL); |
| } |
| |
| bool too_many_outstanding_frames; |
| while (VideoFramePtr decoded_datum = win32_video_decoder_->ProcessAndRead( |
| &too_many_outstanding_frames)) { |
| if (decoded_datum.get()) { |
| callback_->OnVideoDecoded(decoded_datum); |
| } |
| work_done = true; |
| } |
| |
| if (is_end_of_stream) { |
| callback_->OnVideoDecoded(VideoFrame::CreateEOSFrame()); |
| work_done = true; |
| } |
| |
| if (too_many_outstanding_frames) { |
| SbThreadSleep(10 * kSbTimeMillisecond); |
| } |
| |
| if (!work_done) { |
| semaphore_.TakeWait(kSbTimeMillisecond); |
| } |
| } |
| } |
| |
| } // namespace win32 |
| } // namespace shared |
| } // namespace starboard |