| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/common/gpu/media/fake_video_decode_accelerator.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/location.h" | |
| 12 #include "base/thread_task_runner_handle.h" | |
| 13 #include "media/base/bitstream_buffer.h" | |
| 14 #include "media/base/limits.h" | |
| 15 #include "ui/gl/gl_context.h" | |
| 16 #include "ui/gl/gl_implementation.h" | |
| 17 #include "ui/gl/gl_surface.h" | |
| 18 #include "ui/gl/gl_surface_egl.h" | |
| 19 #include "ui/gl/gl_surface_glx.h" | |
| 20 | |
| 21 namespace content { | |
| 22 | |
| 23 static const uint32_t kDefaultTextureTarget = GL_TEXTURE_2D; | |
| 24 // Must be at least 2 since the rendering helper will switch between textures | |
| 25 // and if there is only one, it will wait for the next one that will never come. | |
| 26 // Must also be an even number as otherwise there won't be the same amount of | |
| 27 // white and black frames. | |
| 28 static const unsigned int kNumBuffers = media::limits::kMaxVideoFrames + | |
| 29 (media::limits::kMaxVideoFrames & 1u); | |
| 30 | |
| 31 FakeVideoDecodeAccelerator::FakeVideoDecodeAccelerator( | |
| 32 const gfx::Size& size, | |
| 33 const MakeGLContextCurrentCallback& make_context_current_cb) | |
| 34 : child_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
| 35 client_(NULL), | |
| 36 make_context_current_cb_(make_context_current_cb), | |
| 37 frame_buffer_size_(size), | |
| 38 flushing_(false), | |
| 39 weak_this_factory_(this) {} | |
| 40 | |
| 41 FakeVideoDecodeAccelerator::~FakeVideoDecodeAccelerator() { | |
| 42 } | |
| 43 | |
| 44 bool FakeVideoDecodeAccelerator::Initialize(const Config& config, | |
| 45 Client* client) { | |
| 46 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
| 47 if (config.profile == media::VIDEO_CODEC_PROFILE_UNKNOWN) { | |
| 48 LOG(ERROR) << "unknown codec profile"; | |
| 49 return false; | |
| 50 } | |
| 51 if (config.is_encrypted) { | |
| 52 NOTREACHED() << "encrypted streams are not supported"; | |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 // V4L2VideoDecodeAccelerator waits until first decode call to ask for buffers | |
| 57 // This class asks for it on initialization instead. | |
| 58 client_ = client; | |
| 59 client_->ProvidePictureBuffers(kNumBuffers, 1, frame_buffer_size_, | |
| 60 kDefaultTextureTarget); | |
| 61 return true; | |
| 62 } | |
| 63 | |
| 64 void FakeVideoDecodeAccelerator::Decode( | |
| 65 const media::BitstreamBuffer& bitstream_buffer) { | |
| 66 // We won't really read from the bitstream_buffer, close the handle. | |
| 67 if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) | |
| 68 base::SharedMemory::CloseHandle(bitstream_buffer.handle()); | |
| 69 | |
| 70 if (bitstream_buffer.id() < 0) { | |
| 71 LOG(ERROR) << "Invalid bitstream: id=" << bitstream_buffer.id(); | |
| 72 client_->NotifyError(INVALID_ARGUMENT); | |
| 73 return; | |
| 74 } | |
| 75 | |
| 76 int bitstream_buffer_id = bitstream_buffer.id(); | |
| 77 queued_bitstream_ids_.push(bitstream_buffer_id); | |
| 78 child_task_runner_->PostTask( | |
| 79 FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, | |
| 80 weak_this_factory_.GetWeakPtr())); | |
| 81 } | |
| 82 | |
| 83 // Similar to UseOutputBitstreamBuffer for the encode accelerator. | |
| 84 void FakeVideoDecodeAccelerator::AssignPictureBuffers( | |
| 85 const std::vector<media::PictureBuffer>& buffers) { | |
| 86 DCHECK(buffers.size() == kNumBuffers); | |
| 87 DCHECK(!(buffers.size()%2)); | |
| 88 | |
| 89 // Save buffers and mark all buffers as ready for use. | |
| 90 std::unique_ptr<uint8_t[]> white_data( | |
| 91 new uint8_t[frame_buffer_size_.width() * frame_buffer_size_.height() * | |
| 92 4]); | |
| 93 memset(white_data.get(), | |
| 94 UINT8_MAX, | |
| 95 frame_buffer_size_.width() * frame_buffer_size_.height() * 4); | |
| 96 std::unique_ptr<uint8_t[]> black_data( | |
| 97 new uint8_t[frame_buffer_size_.width() * frame_buffer_size_.height() * | |
| 98 4]); | |
| 99 memset(black_data.get(), | |
| 100 0, | |
| 101 frame_buffer_size_.width() * frame_buffer_size_.height() * 4); | |
| 102 if (!make_context_current_cb_.Run()) { | |
| 103 LOG(ERROR) << "ReusePictureBuffer(): could not make context current"; | |
| 104 return; | |
| 105 } | |
| 106 for (size_t index = 0; index < buffers.size(); ++index) { | |
| 107 DCHECK_LE(1u, buffers[index].texture_ids().size()); | |
| 108 glBindTexture(GL_TEXTURE_2D, buffers[index].texture_ids()[0]); | |
| 109 // Every other frame white and the rest black. | |
| 110 uint8_t* data = index % 2 ? white_data.get() : black_data.get(); | |
| 111 glTexImage2D(GL_TEXTURE_2D, | |
| 112 0, | |
| 113 GL_RGBA, | |
| 114 frame_buffer_size_.width(), | |
| 115 frame_buffer_size_.height(), | |
| 116 0, | |
| 117 GL_RGBA, | |
| 118 GL_UNSIGNED_BYTE, | |
| 119 data); | |
| 120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 124 glBindTexture(GL_TEXTURE_2D, 0); | |
| 125 free_output_buffers_.push(buffers[index].id()); | |
| 126 } | |
| 127 child_task_runner_->PostTask( | |
| 128 FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, | |
| 129 weak_this_factory_.GetWeakPtr())); | |
| 130 } | |
| 131 | |
| 132 void FakeVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_buffer_id) { | |
| 133 free_output_buffers_.push(picture_buffer_id); | |
| 134 child_task_runner_->PostTask( | |
| 135 FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, | |
| 136 weak_this_factory_.GetWeakPtr())); | |
| 137 } | |
| 138 | |
| 139 void FakeVideoDecodeAccelerator::Flush() { | |
| 140 flushing_ = true; | |
| 141 child_task_runner_->PostTask( | |
| 142 FROM_HERE, base::Bind(&FakeVideoDecodeAccelerator::DoPictureReady, | |
| 143 weak_this_factory_.GetWeakPtr())); | |
| 144 } | |
| 145 | |
| 146 void FakeVideoDecodeAccelerator::Reset() { | |
| 147 while (!queued_bitstream_ids_.empty()) { | |
| 148 client_->NotifyEndOfBitstreamBuffer(queued_bitstream_ids_.front()); | |
| 149 queued_bitstream_ids_.pop(); | |
| 150 } | |
| 151 client_->NotifyResetDone(); | |
| 152 } | |
| 153 | |
| 154 void FakeVideoDecodeAccelerator::Destroy() { | |
| 155 while (!queued_bitstream_ids_.empty()) { | |
| 156 client_->NotifyEndOfBitstreamBuffer(queued_bitstream_ids_.front()); | |
| 157 queued_bitstream_ids_.pop(); | |
| 158 } | |
| 159 delete this; | |
| 160 } | |
| 161 | |
| 162 bool FakeVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( | |
| 163 const base::WeakPtr<Client>& decode_client, | |
| 164 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { | |
| 165 return false; | |
| 166 } | |
| 167 | |
| 168 void FakeVideoDecodeAccelerator::DoPictureReady() { | |
| 169 if (flushing_ && queued_bitstream_ids_.empty()) { | |
| 170 flushing_ = false; | |
| 171 client_->NotifyFlushDone(); | |
| 172 } | |
| 173 while (!free_output_buffers_.empty() && !queued_bitstream_ids_.empty()) { | |
| 174 int bitstream_id = queued_bitstream_ids_.front(); | |
| 175 queued_bitstream_ids_.pop(); | |
| 176 int buffer_id = free_output_buffers_.front(); | |
| 177 free_output_buffers_.pop(); | |
| 178 | |
| 179 const media::Picture picture = | |
| 180 media::Picture(buffer_id, | |
| 181 bitstream_id, | |
| 182 gfx::Rect(frame_buffer_size_), | |
| 183 false); | |
| 184 client_->PictureReady(picture); | |
| 185 // Bitstream no longer needed. | |
| 186 client_->NotifyEndOfBitstreamBuffer(bitstream_id); | |
| 187 if (flushing_ && queued_bitstream_ids_.empty()) { | |
| 188 flushing_ = false; | |
| 189 client_->NotifyFlushDone(); | |
| 190 } | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 } // namespace content | |
| OLD | NEW |