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