1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
media / gpu / android / codec_output_buffer_renderer.cc [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/android/codec_output_buffer_renderer.h"
#include <string.h>
#include <optional>
#include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "base/functional/callback_helpers.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/scoped_make_current.h"
namespace media {
CodecOutputBufferRenderer::CodecOutputBufferRenderer(
std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
scoped_refptr<gpu::RefCountedLock> drdc_lock)
: RefCountedLockHelperDrDc(std::move(drdc_lock)),
output_buffer_(std::move(output_buffer)),
codec_buffer_wait_coordinator_(std::move(codec_buffer_wait_coordinator)) {
}
CodecOutputBufferRenderer::~CodecOutputBufferRenderer() {
Invalidate();
}
bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer() {
AssertAcquiredDrDcLock();
DCHECK_NE(phase_, Phase::kInFrontBuffer);
if (phase_ == Phase::kInBackBuffer)
return true;
if (phase_ == Phase::kInvalidated)
return false;
// Normally, we should have a wait coordinator if we're called. However, if
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false;
// Don't render frame if one is already pending.
// RenderToTextureOwnerFrontBuffer will wait before calling this.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
return false;
}
if (!output_buffer_->ReleaseToSurface()) {
Invalidate();
return false;
}
phase_ = Phase::kInBackBuffer;
codec_buffer_wait_coordinator_->SetReleaseTimeToNow();
return true;
}
bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer() {
AssertAcquiredDrDcLock();
// Normally, we should have a wait coordinator if we're called. However, if
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false;
if (phase_ == Phase::kInFrontBuffer) {
return true;
}
if (phase_ == Phase::kInvalidated)
return false;
// Render it to the back buffer if it's not already there.
if (phase_ != Phase::kInBackBuffer) {
// Wait for a previous frame available so we don't confuse it with the one
// we're about to render.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
// We must call update tex image if we did get OnFrameAvailable, otherwise
// we will stop receiving callbacks (see https://crbug.com/c/1113203)
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
}
if (!RenderToTextureOwnerBackBuffer()) {
// RenderTotextureOwnerBackBuffer can fail now only if ReleaseToSurface
// failed.
DCHECK_EQ(phase_, Phase::kInvalidated);
return false;
}
}
// The image is now in the back buffer, so promote it to the front buffer.
phase_ = Phase::kInFrontBuffer;
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
if (frame_info_callback_) {
gfx::Size coded_size;
gfx::Rect visible_rect;
if (texture_owner() && texture_owner()->GetCodedSizeAndVisibleRect(
size(), &coded_size, &visible_rect)) {
std::move(frame_info_callback_).Run(coded_size, visible_rect);
} else {
std::move(frame_info_callback_).Run(std::nullopt, std::nullopt);
}
}
return true;
}
bool CodecOutputBufferRenderer::RenderToOverlay() {
AssertAcquiredDrDcLock();
if (phase_ == Phase::kInFrontBuffer)
return true;
if (phase_ == Phase::kInvalidated)
return false;
if (!output_buffer_->ReleaseToSurface()) {
Invalidate();
return false;
}
phase_ = Phase::kInFrontBuffer;
return true;
}
bool CodecOutputBufferRenderer::RenderToFrontBuffer() {
AssertAcquiredDrDcLock();
// Trigger early rendering of the image before it is used for compositing.
return codec_buffer_wait_coordinator_ ? RenderToTextureOwnerFrontBuffer()
: RenderToOverlay();
}
void CodecOutputBufferRenderer::Invalidate() {
phase_ = Phase::kInvalidated;
if (frame_info_callback_) {
std::move(frame_info_callback_).Run(std::nullopt, std::nullopt);
}
}
} // namespace media