Shaka Player Embedded
sdl_frame_drawer.cc
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "shaka/sdl_frame_drawer.h"
16 
17 #include <SDL2/SDL.h>
18 #ifdef __APPLE__
19 # include <VideoToolbox/VideoToolbox.h>
20 #endif
21 
22 #include <list>
23 #include <unordered_set>
24 
25 #include "src/util/macros.h"
26 
27 namespace shaka {
28 
29 namespace {
30 
31 constexpr const size_t kMaxTextures = 8;
32 
33 struct TextureInfo {
34  TextureInfo(SDL_Texture* texture, uint32_t pixel_format, int width,
35  int height)
36  : texture(texture),
37  pixel_format(pixel_format),
38  width(width),
39  height(height) {}
40 
41  ~TextureInfo() {
42  SDL_DestroyTexture(texture);
43  }
44 
46 
47  SDL_Texture* texture;
48  uint32_t pixel_format;
49  int width;
50  int height;
51 };
52 
53 
54 Uint32 SdlPixelFormatFromPublic(
55  variant<media::PixelFormat, media::SampleFormat> format) {
56  switch (get<media::PixelFormat>(format)) {
57 #if SDL_VERSION_ATLEAST(2, 0, 4)
58 # ifdef __APPLE__
60 # endif
62  return SDL_PIXELFORMAT_NV12;
63 #endif
65  return SDL_PIXELFORMAT_IYUV;
67  return SDL_PIXELFORMAT_RGB24;
68 
69  default:
70  LOG(DFATAL) << "Unsupported pixel format: " << format;
71  return SDL_PIXELFORMAT_UNKNOWN;
72  }
73 }
74 
75 } // namespace
76 
78  public:
79  Impl() : renderer_(nullptr) {}
80  ~Impl() {}
81 
83 
84  void SetRenderer(SDL_Renderer* renderer) {
85  textures_.clear();
86  texture_formats_.clear();
87  renderer_ = renderer;
88 
89  if (renderer) {
90  SDL_RendererInfo info;
91  if (SDL_GetRendererInfo(renderer, &info) == 0) {
92  texture_formats_.insert(
93  info.texture_formats,
94  info.texture_formats + info.num_texture_formats);
95  }
96  if (texture_formats_.empty()) {
97  LOG(DFATAL) << "No supported texture formats";
98  }
99  }
100  }
101 
102  SDL_Texture* Draw(std::shared_ptr<media::DecodedFrame> frame) {
103  if (!frame)
104  return nullptr;
105 
106  auto sdl_pix_fmt = SdlPixelFormatFromPublic(frame->format);
107  if (sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ||
108  texture_formats_.count(sdl_pix_fmt) == 0) {
109  return nullptr;
110  }
111 
112  SDL_Texture* texture = GetTexture(sdl_pix_fmt, frame->stream_info->width,
113  frame->stream_info->height);
114  if (!texture)
115  return nullptr;
116 
117  if (!DrawOntoTexture(frame, texture, sdl_pix_fmt))
118  return nullptr;
119 
120  return texture;
121  }
122 
123  private:
124  bool DrawOntoTexture(std::shared_ptr<media::DecodedFrame> frame,
125  SDL_Texture* texture, Uint32 sdl_pix_fmt) {
126  const uint8_t* const* frame_data = frame->data.data();
127  const size_t* frame_linesize = frame->linesize.data();
128 
129  if (sdl_pix_fmt == SDL_PIXELFORMAT_IYUV) {
130  if (SDL_UpdateYUVTexture(
131  texture, nullptr, frame_data[0], frame_linesize[0], frame_data[1],
132  frame_linesize[1], frame_data[2], frame_linesize[2]) < 0) {
133  LOG(DFATAL) << "Error updating texture: " << SDL_GetError();
134  return false;
135  }
136 #if SDL_VERSION_ATLEAST(2, 0, 4)
137 # ifdef __APPLE__
138  } else if (get<media::PixelFormat>(frame->format) ==
140  auto* pix_buf = reinterpret_cast<CVPixelBufferRef>(
141  const_cast<uint8_t*>(frame->data[0]));
142  uint8_t* pixels;
143  int pitch;
144  if (SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels),
145  &pitch) < 0) {
146  LOG(DFATAL) << "Error locking texture: " << SDL_GetError();
147  return false;
148  }
149  if (static_cast<size_t>(pitch) !=
150  CVPixelBufferGetBytesPerRowOfPlane(pix_buf, 0) ||
151  !CVPixelBufferIsPlanar(pix_buf) ||
152  CVPixelBufferGetPlaneCount(pix_buf) != 2) {
153  LOG(DFATAL) << "Invalid pixel buffer";
154  SDL_UnlockTexture(texture);
155  return false;
156  }
157  if (CVPixelBufferLockBaseAddress(pix_buf, kCVPixelBufferLock_ReadOnly) !=
158  0) {
159  LOG(DFATAL) << "Error locking pixel_buffer";
160  SDL_UnlockTexture(texture);
161  return false;
162  }
163 
164  const size_t size0 = pitch * frame->stream_info->height;
165  memcpy(pixels, CVPixelBufferGetBaseAddressOfPlane(pix_buf, 0), size0);
166  memcpy(pixels + size0, CVPixelBufferGetBaseAddressOfPlane(pix_buf, 1),
167  size0 / 2);
168 
169  CVPixelBufferUnlockBaseAddress(pix_buf, kCVPixelBufferLock_ReadOnly);
170  SDL_UnlockTexture(texture);
171 # endif
172  } else if (sdl_pix_fmt == SDL_PIXELFORMAT_NV12 ||
173  sdl_pix_fmt == SDL_PIXELFORMAT_NV21) {
174  uint8_t* pixels;
175  int pitch;
176  if (SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels),
177  &pitch) < 0) {
178  LOG(DFATAL) << "Error locking texture: " << SDL_GetError();
179  return false;
180  }
181 
182  if (static_cast<size_t>(pitch) == frame_linesize[0]) {
183  // TODO: Sometimes there is a green bar at the bottom.
184  const size_t size0 = pitch * frame->stream_info->height;
185  memcpy(pixels, frame_data[0], size0);
186  memcpy(pixels + size0, frame_data[1], size0 / 2);
187  } else {
188  // FFmpeg may add padding to the rows, so we need to drop it by manually
189  // copying each line.
190  DCHECK_GE(frame_linesize[0], pitch);
191  const int min_width = std::min<size_t>(pitch, frame_linesize[0]);
192  for (uint32_t row = 0; row < frame->stream_info->height; row++) {
193  uint8_t* dest = pixels + pitch * row;
194  const uint8_t* src = frame_data[0] + frame_linesize[0] * row;
195  memcpy(dest, src, min_width);
196  }
197  for (uint32_t row = 0; row < frame->stream_info->height / 2; row++) {
198  uint8_t* dest = pixels + pitch * (row + frame->stream_info->height);
199  const uint8_t* src = frame_data[1] + frame_linesize[1] * row;
200  memcpy(dest, src, min_width);
201  }
202  }
203 
204  SDL_UnlockTexture(texture);
205 #endif
206  } else {
207  if (SDL_UpdateTexture(texture, nullptr, frame_data[0],
208  frame_linesize[0]) < 0) {
209  LOG(DFATAL) << "Error updating texture: " << SDL_GetError();
210  return false;
211  }
212  }
213 
214  return true;
215  }
216 
217  SDL_Texture* GetTexture(Uint32 pixel_format, int width, int height) {
218  if (!renderer_)
219  return nullptr;
220 
221  for (auto it = textures_.begin(); it != textures_.end(); it++) {
222  if (it->pixel_format == pixel_format && it->width == width &&
223  it->height == height) {
224  if (std::next(it) != textures_.end()) {
225  // Move the texture to the end so elements at the beginning are ones
226  // that were least-recently used.
227  textures_.splice(textures_.end(), textures_, it);
228  }
229 
230  return it->texture;
231  }
232  }
233 
234  while (!textures_.empty() && textures_.size() >= kMaxTextures) {
235  textures_.erase(textures_.begin());
236  }
237 
238  SDL_Texture* texture = SDL_CreateTexture(
239  renderer_, pixel_format, SDL_TEXTUREACCESS_STREAMING, width, height);
240  if (texture)
241  textures_.emplace_back(texture, pixel_format, width, height);
242  else
243  LOG(DFATAL) << "Error creating texture: " << SDL_GetError();
244 
245  return texture;
246  }
247 
248  std::list<TextureInfo> textures_;
249  std::unordered_set<Uint32> texture_formats_;
250  SDL_Renderer* renderer_;
251 };
252 
257 
258 void SdlFrameDrawer::SetRenderer(SDL_Renderer* renderer) {
259  impl_->SetRenderer(renderer);
260 }
261 
262 SDL_Texture* SdlFrameDrawer::Draw(std::shared_ptr<media::DecodedFrame> frame) {
263  return impl_->Draw(frame);
264 }
265 
266 } // namespace shaka
SDL_Texture * texture
const char * dest
Definition: media_utils.cc:31
SdlFrameDrawer & operator=(SdlFrameDrawer &&)
SDL_Texture * Draw(std::shared_ptr< media::DecodedFrame > frame)
std::shared_ptr< shaka::media::DecodedFrame > frame
void SetRenderer(SDL_Renderer *renderer)
#define SHAKA_NON_COPYABLE_OR_MOVABLE_TYPE(Type)
Definition: macros.h:51
int width
int height
uint32_t pixel_format
void SetRenderer(SDL_Renderer *renderer)
SDL_Texture * Draw(std::shared_ptr< media::DecodedFrame > frame)