// 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 "content/common/gpu/media/fake_video_decode_accelerator.h" #include "base/bind.h" #include "base/location.h" #include "base/thread_task_runner_handle.h" #include "media/base/bitstream_buffer.h" #include "media/base/limits.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_glx.h" namespace content { static const uint32 kDefaultTextureTarget = GL_TEXTURE_2D; // Must be at least 2 since the rendering helper will switch between textures // and if there is only one, it will wait for the next one that will never come. // Must also be an even number as otherwise there won't be the same amount of // white and black frames. static const unsigned int kNumBuffers = media::limits::kMaxVideoFrames + (media::limits::kMaxVideoFrames & 1u); FakeVideoDecodeAccelerator::FakeVideoDecodeAccelerator( gfx::GLContext* gl, gfx::Size size, const base::Callback& make_context_current) : child_task_runner_(base::ThreadTaskRunnerHandle::Get()), client_(NULL), make_context_current_(make_context_current), gl_(gl), frame_buffer_size_(size), flushing_(false), weak_this_factory_(this) { } FakeVideoDecodeAccelerator::~FakeVideoDecodeAccelerator() { } bool FakeVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, Client* client) { DCHECK(child_task_runner_->BelongsToCurrentThread()); if (profile == media::VIDEO_CODEC_PROFILE_UNKNOWN) { LOG(ERROR) << "unknown codec profile"; return false; } // V4L2VideoDecodeAccelerator waits until first decode call to ask for buffers // This class asks for it on initialization instead. client_ = client; client_->ProvidePictureBuffers(kNumBuffers, frame_buffer_size_, kDefaultTextureTarget); return true; } void FakeVideoDecodeAccelerator::Decode( const media::BitstreamBuffer& bitstream_buffer) { int bitstream_buffer_id = bitstream_buffer.id(); queued_bitstream_ids_.push(bitstream_buffer_id); child_task_runner_->PostTask( FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, weak_this_factory_.GetWeakPtr())); } // Similar to UseOutputBitstreamBuffer for the encode accelerator. void FakeVideoDecodeAccelerator::AssignPictureBuffers( const std::vector& buffers) { DCHECK(buffers.size() == kNumBuffers); DCHECK(!(buffers.size()%2)); // Save buffers and mark all buffers as ready for use. scoped_ptr white_data( new uint8[frame_buffer_size_.width() * frame_buffer_size_.height() * 4]); memset(white_data.get(), UINT8_MAX, frame_buffer_size_.width() * frame_buffer_size_.height() * 4); scoped_ptr black_data( new uint8[frame_buffer_size_.width() * frame_buffer_size_.height() * 4]); memset(black_data.get(), 0, frame_buffer_size_.width() * frame_buffer_size_.height() * 4); if (!make_context_current_.Run()) { LOG(ERROR) << "ReusePictureBuffer(): could not make context current"; return; } for (size_t index = 0; index < buffers.size(); ++index) { glBindTexture(GL_TEXTURE_2D, buffers[index].texture_id()); // Every other frame white and the rest black. uint8* data = index%2 ? white_data.get():black_data.get(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, frame_buffer_size_.width(), frame_buffer_size_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); free_output_buffers_.push(buffers[index].id()); } child_task_runner_->PostTask( FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, weak_this_factory_.GetWeakPtr())); } void FakeVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { free_output_buffers_.push(picture_buffer_id); child_task_runner_->PostTask( FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, weak_this_factory_.GetWeakPtr())); } void FakeVideoDecodeAccelerator::Flush() { flushing_ = true; child_task_runner_->PostTask( FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, weak_this_factory_.GetWeakPtr())); } void FakeVideoDecodeAccelerator::Reset() { while (!queued_bitstream_ids_.empty()) { client_->NotifyEndOfBitstreamBuffer(queued_bitstream_ids_.front()); queued_bitstream_ids_.pop(); } client_->NotifyResetDone(); } void FakeVideoDecodeAccelerator::Destroy() { while (!queued_bitstream_ids_.empty()) { client_->NotifyEndOfBitstreamBuffer(queued_bitstream_ids_.front()); queued_bitstream_ids_.pop(); } delete this; } bool FakeVideoDecodeAccelerator::CanDecodeOnIOThread() { return true; } void FakeVideoDecodeAccelerator::DoPictureReady() { if (flushing_ && queued_bitstream_ids_.empty()) { flushing_ = false; client_->NotifyFlushDone(); } while (!free_output_buffers_.empty() && !queued_bitstream_ids_.empty()) { int bitstream_id = queued_bitstream_ids_.front(); queued_bitstream_ids_.pop(); int buffer_id = free_output_buffers_.front(); free_output_buffers_.pop(); const media::Picture picture = media::Picture(buffer_id, bitstream_id, gfx::Rect(frame_buffer_size_), false); client_->PictureReady(picture); // Bitstream no longer needed. client_->NotifyEndOfBitstreamBuffer(bitstream_id); if (flushing_ && queued_bitstream_ids_.empty()) { flushing_ = false; client_->NotifyFlushDone(); } } } } // namespace content