• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "cast/standalone_receiver/sdl_video_player.h"
6 
7 #include <sstream>
8 #include <utility>
9 
10 #include "cast/standalone_receiver/avcodec_glue.h"
11 #include "util/enum_name_table.h"
12 #include "util/osp_logging.h"
13 #include "util/trace_logging.h"
14 
15 namespace openscreen {
16 namespace cast {
17 
18 namespace {
19 constexpr char kVideoMediaType[] = "video";
20 }  // namespace
21 
22 constexpr EnumNameTable<VideoCodec, 6> kFfmpegCodecDescriptors{
23     {{"h264", VideoCodec::kH264},
24      {"vp8", VideoCodec::kVp8},
25      {"hevc", VideoCodec::kHevc},
26      {"vp9", VideoCodec::kVp9},
27      {"libaom-av1", VideoCodec::kAv1}}};
28 
SDLVideoPlayer(ClockNowFunctionPtr now_function,TaskRunner * task_runner,Receiver * receiver,VideoCodec codec,SDL_Renderer * renderer,std::function<void ()> error_callback)29 SDLVideoPlayer::SDLVideoPlayer(ClockNowFunctionPtr now_function,
30                                TaskRunner* task_runner,
31                                Receiver* receiver,
32                                VideoCodec codec,
33                                SDL_Renderer* renderer,
34                                std::function<void()> error_callback)
35     : SDLPlayerBase(now_function,
36                     task_runner,
37                     receiver,
38                     GetEnumName(kFfmpegCodecDescriptors, codec).value(),
39                     std::move(error_callback),
40                     kVideoMediaType),
41       renderer_(renderer) {
42   OSP_DCHECK(renderer_);
43 }
44 
45 SDLVideoPlayer::~SDLVideoPlayer() = default;
46 
RenderWhileIdle(const SDLPlayerBase::PresentableFrame * frame)47 bool SDLVideoPlayer::RenderWhileIdle(
48     const SDLPlayerBase::PresentableFrame* frame) {
49   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
50   // Attempt to re-render the same content.
51   if (state() == kPresented && frame) {
52     const auto result = RenderNextFrame(*frame);
53     if (result) {
54       return true;
55     }
56     OnFatalError(result.error().message());
57     // Fall-through to the "red splash" rendering below.
58   }
59 
60   if (state() == kError) {
61     // Paint "red splash" to indicate an error state.
62     constexpr struct { int r = 128, g = 0, b = 0, a = 255; } kRedSplashColor;
63     SDL_SetRenderDrawColor(renderer_, kRedSplashColor.r, kRedSplashColor.g,
64                            kRedSplashColor.b, kRedSplashColor.a);
65     SDL_RenderClear(renderer_);
66   } else if (state() == kWaitingForFirstFrame || !frame) {
67     // Paint "blue splash" to indicate the "waiting for first frame" state.
68     constexpr struct { int r = 0, g = 0, b = 128, a = 255; } kBlueSplashColor;
69     SDL_SetRenderDrawColor(renderer_, kBlueSplashColor.r, kBlueSplashColor.g,
70                            kBlueSplashColor.b, kBlueSplashColor.a);
71     SDL_RenderClear(renderer_);
72   }
73 
74   return state() != kScheduledToPresent;
75 }
76 
RenderNextFrame(const SDLPlayerBase::PresentableFrame & frame)77 ErrorOr<Clock::time_point> SDLVideoPlayer::RenderNextFrame(
78     const SDLPlayerBase::PresentableFrame& frame) {
79   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
80   OSP_DCHECK(frame.decoded_frame);
81   const AVFrame& picture = *frame.decoded_frame;
82 
83   // Punt if the |picture| format is not compatible with those supported by SDL.
84   const uint32_t sdl_format = GetSDLPixelFormat(picture);
85   if (sdl_format == SDL_PIXELFORMAT_UNKNOWN) {
86     std::ostringstream error;
87     error << "SDL does not support AVPixelFormat " << picture.format;
88     return Error(Error::Code::kUnknownError, error.str());
89   }
90 
91   // If there is already a SDL texture, check that its format and size matches
92   // that of |picture|. If not, release the existing texture.
93   if (texture_) {
94     uint32_t texture_format = SDL_PIXELFORMAT_UNKNOWN;
95     int texture_width = -1;
96     int texture_height = -1;
97     SDL_QueryTexture(texture_.get(), &texture_format, nullptr, &texture_width,
98                      &texture_height);
99     if (texture_format != sdl_format || texture_width != picture.width ||
100         texture_height != picture.height) {
101       texture_.reset();
102     }
103   }
104 
105   // If necessary, recreate a SDL texture having the same format and size as
106   // that of |picture|.
107   if (!texture_) {
108     const auto EvalDescriptionString = [&] {
109       std::ostringstream error;
110       error << SDL_GetPixelFormatName(sdl_format) << " at " << picture.width
111             << "×" << picture.height;
112       return error.str();
113     };
114     OSP_LOG_INFO << "Creating SDL texture for " << EvalDescriptionString();
115     texture_ =
116         MakeUniqueSDLTexture(renderer_, sdl_format, SDL_TEXTUREACCESS_STREAMING,
117                              picture.width, picture.height);
118     if (!texture_) {
119       std::ostringstream error;
120       error << "Unable to (re)create SDL texture for format: "
121             << EvalDescriptionString();
122       return Error(Error::Code::kUnknownError, error.str());
123     }
124   }
125 
126   // Upload the |picture_| to the SDL texture.
127   void* pixels = nullptr;
128   int stride = 0;
129   SDL_LockTexture(texture_.get(), nullptr, &pixels, &stride);
130   const auto picture_format = static_cast<AVPixelFormat>(picture.format);
131   const int pixels_size = av_image_get_buffer_size(
132       picture_format, picture.width, picture.height, stride);
133   constexpr int kSDLTextureRowAlignment = 1;  // SDL doesn't use word-alignment.
134   av_image_copy_to_buffer(static_cast<uint8_t*>(pixels), pixels_size,
135                           picture.data, picture.linesize, picture_format,
136                           picture.width, picture.height,
137                           kSDLTextureRowAlignment);
138   SDL_UnlockTexture(texture_.get());
139 
140   // Render the SDL texture to the render target. Quality-related issues that a
141   // production-worthy player should account for that are not being done here:
142   //
143   // 1. Need to account for AVPicture's sample_aspect_ratio property. Otherwise,
144   //    content may appear "squashed" in one direction to the user.
145   //
146   // 2. SDL has no concept of color space, and so the color information provided
147   //    with the AVPicture might not match the assumptions being made within
148   //    SDL. Content may appear with washed-out colors, not-entirely-black
149   //    blacks, striped gradients, etc.
150   const SDL_Rect src_rect = {
151       static_cast<int>(picture.crop_left), static_cast<int>(picture.crop_top),
152       picture.width - static_cast<int>(picture.crop_left + picture.crop_right),
153       picture.height -
154           static_cast<int>(picture.crop_top + picture.crop_bottom)};
155   SDL_Rect dst_rect = {0, 0, 0, 0};
156   SDL_RenderGetLogicalSize(renderer_, &dst_rect.w, &dst_rect.h);
157   if (src_rect.w != dst_rect.w || src_rect.h != dst_rect.h) {
158     // Make the SDL rendering size the same as the frame's visible size. This
159     // lets SDL automatically handle letterboxing and scaling details, so that
160     // the video fits within the on-screen window.
161     dst_rect.w = src_rect.w;
162     dst_rect.h = src_rect.h;
163     SDL_RenderSetLogicalSize(renderer_, dst_rect.w, dst_rect.h);
164   }
165   // Clear with black, for the "letterboxing" borders.
166   SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
167   SDL_RenderClear(renderer_);
168   SDL_RenderCopy(renderer_, texture_.get(), &src_rect, &dst_rect);
169 
170   return frame.presentation_time;
171 }
172 
Present()173 void SDLVideoPlayer::Present() {
174   TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
175   SDL_RenderPresent(renderer_);
176 }
177 
178 // static
GetSDLPixelFormat(const AVFrame & picture)179 uint32_t SDLVideoPlayer::GetSDLPixelFormat(const AVFrame& picture) {
180   switch (picture.format) {
181     case AV_PIX_FMT_NONE:
182       break;
183     case AV_PIX_FMT_YUV420P:
184       return SDL_PIXELFORMAT_IYUV;
185     case AV_PIX_FMT_YUYV422:
186       return SDL_PIXELFORMAT_YUY2;
187     case AV_PIX_FMT_UYVY422:
188       return SDL_PIXELFORMAT_UYVY;
189     case AV_PIX_FMT_YVYU422:
190       return SDL_PIXELFORMAT_YVYU;
191     case AV_PIX_FMT_NV12:
192       return SDL_PIXELFORMAT_NV12;
193     case AV_PIX_FMT_NV21:
194       return SDL_PIXELFORMAT_NV21;
195     case AV_PIX_FMT_RGB24:
196       return SDL_PIXELFORMAT_RGB24;
197     case AV_PIX_FMT_BGR24:
198       return SDL_PIXELFORMAT_BGR24;
199     case AV_PIX_FMT_ARGB:
200       return SDL_PIXELFORMAT_ARGB32;
201     case AV_PIX_FMT_RGBA:
202       return SDL_PIXELFORMAT_RGBA32;
203     case AV_PIX_FMT_ABGR:
204       return SDL_PIXELFORMAT_ABGR32;
205     case AV_PIX_FMT_BGRA:
206       return SDL_PIXELFORMAT_BGRA32;
207     default:
208       break;
209   }
210   return SDL_PIXELFORMAT_UNKNOWN;
211 }
212 
213 }  // namespace cast
214 }  // namespace openscreen
215