19 # include <VideoToolbox/VideoToolbox.h> 23 #include <unordered_set> 31 constexpr
const size_t kMaxTextures = 8;
37 pixel_format(pixel_format),
54 Uint32 SdlPixelFormatFromPublic(
55 variant<media::PixelFormat, media::SampleFormat> format) {
56 switch (get<media::PixelFormat>(format)) {
57 #if SDL_VERSION_ATLEAST(2, 0, 4) 62 return SDL_PIXELFORMAT_NV12;
65 return SDL_PIXELFORMAT_IYUV;
67 return SDL_PIXELFORMAT_RGB24;
70 LOG(DFATAL) <<
"Unsupported pixel format: " << format;
71 return SDL_PIXELFORMAT_UNKNOWN;
79 Impl() : renderer_(nullptr) {}
86 texture_formats_.clear();
90 SDL_RendererInfo info;
91 if (SDL_GetRendererInfo(renderer, &info) == 0) {
92 texture_formats_.insert(
94 info.texture_formats + info.num_texture_formats);
96 if (texture_formats_.empty()) {
97 LOG(DFATAL) <<
"No supported texture formats";
102 SDL_Texture*
Draw(std::shared_ptr<media::DecodedFrame>
frame) {
106 auto sdl_pix_fmt = SdlPixelFormatFromPublic(frame->format);
107 if (sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ||
108 texture_formats_.count(sdl_pix_fmt) == 0) {
112 SDL_Texture*
texture = GetTexture(sdl_pix_fmt, frame->stream_info->width,
113 frame->stream_info->height);
117 if (!DrawOntoTexture(frame, texture, sdl_pix_fmt))
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();
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();
136 #if SDL_VERSION_ATLEAST(2, 0, 4) 138 }
else if (get<media::PixelFormat>(frame->format) ==
140 auto* pix_buf =
reinterpret_cast<CVPixelBufferRef
>(
141 const_cast<uint8_t*
>(frame->data[0]));
144 if (SDL_LockTexture(texture,
nullptr, reinterpret_cast<void**>(&pixels),
146 LOG(DFATAL) <<
"Error locking texture: " << SDL_GetError();
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);
157 if (CVPixelBufferLockBaseAddress(pix_buf, kCVPixelBufferLock_ReadOnly) !=
159 LOG(DFATAL) <<
"Error locking pixel_buffer";
160 SDL_UnlockTexture(texture);
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),
169 CVPixelBufferUnlockBaseAddress(pix_buf, kCVPixelBufferLock_ReadOnly);
170 SDL_UnlockTexture(texture);
172 }
else if (sdl_pix_fmt == SDL_PIXELFORMAT_NV12 ||
173 sdl_pix_fmt == SDL_PIXELFORMAT_NV21) {
176 if (SDL_LockTexture(texture,
nullptr, reinterpret_cast<void**>(&pixels),
178 LOG(DFATAL) <<
"Error locking texture: " << SDL_GetError();
182 if (static_cast<size_t>(pitch) == frame_linesize[0]) {
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);
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);
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);
204 SDL_UnlockTexture(texture);
207 if (SDL_UpdateTexture(texture,
nullptr, frame_data[0],
208 frame_linesize[0]) < 0) {
209 LOG(DFATAL) <<
"Error updating texture: " << SDL_GetError();
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()) {
227 textures_.splice(textures_.end(), textures_, it);
234 while (!textures_.empty() && textures_.size() >= kMaxTextures) {
235 textures_.erase(textures_.begin());
238 SDL_Texture* texture = SDL_CreateTexture(
239 renderer_, pixel_format, SDL_TEXTUREACCESS_STREAMING, width, height);
241 textures_.emplace_back(texture, pixel_format, width, height);
243 LOG(DFATAL) <<
"Error creating texture: " << SDL_GetError();
248 std::list<TextureInfo> textures_;
249 std::unordered_set<Uint32> texture_formats_;
250 SDL_Renderer* renderer_;
259 impl_->SetRenderer(renderer);
263 return impl_->Draw(frame);
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)
void SetRenderer(SDL_Renderer *renderer)
SDL_Texture * Draw(std::shared_ptr< media::DecodedFrame > frame)