Shaka Player Embedded
apple_video_renderer.cc
Go to the documentation of this file.
1 // Copyright 2019 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 
16 
17 #include <glog/logging.h>
18 
20 
21 namespace shaka {
22 namespace media {
23 
24 namespace {
25 
26 struct FrameInfo {
27  std::shared_ptr<shaka::media::DecodedFrame> frame;
28  size_t widths[4];
29  size_t heights[4];
30 };
31 
32 void FreeFrameBytes(void* info, const void*, size_t) {
33  auto* frame = reinterpret_cast<FrameInfo*>(info);
34  delete frame;
35 }
36 
37 void FreeFramePlanar(void* info, const void*, size_t, size_t, const void**) {
38  auto* frame = reinterpret_cast<FrameInfo*>(info);
39  delete frame;
40 }
41 
42 } // namespace
43 
45  public:
46  CGImageRef Render(double* delay, Rational<uint32_t>* sample_aspect_ratio);
47 
48  private:
49  CGImageRef RenderPackedFrame(std::shared_ptr<DecodedFrame> frame);
50  CGImageRef RenderPlanarFrame(std::shared_ptr<DecodedFrame> frame);
51 
52  std::shared_ptr<DecodedFrame> prev_frame_;
53 };
54 
56  double* delay, Rational<uint32_t>* sample_aspect_ratio) {
57  std::shared_ptr<DecodedFrame> frame;
58  const double loc_delay = GetCurrentFrame(&frame);
59  if (delay)
60  *delay = loc_delay;
61 
62  if (!frame || frame == prev_frame_)
63  return nullptr;
64 
65  if (sample_aspect_ratio)
66  *sample_aspect_ratio = frame->stream_info->sample_aspect_ratio;
67  prev_frame_ = frame;
68  switch (get<PixelFormat>(frame->format)) {
69  case PixelFormat::RGB24:
70  return RenderPackedFrame(frame);
71 
74  return RenderPlanarFrame(frame);
75 
76  default:
77  LOG(DFATAL) << "Unsupported pixel format: " << frame->format;
78  return nullptr;
79  }
80 }
81 
82 CGImageRef AppleVideoRenderer::Impl::RenderPackedFrame(
83  std::shared_ptr<DecodedFrame> frame) {
84  const uint32_t width = frame->stream_info->width;
85  const uint32_t height = frame->stream_info->height;
86  const size_t bytes_per_row = frame->linesize[0];
87  CFIndex size = bytes_per_row * height;
88 
89  // TODO: Handle padding.
90 
91  // Make a CGDataProvider object to distribute the data to the CGImage.
92  // This takes ownership of the frame and calls the given callback when the
93  // CGImage is destroyed.
94  const uint8_t* data = frame->data[0];
95  auto* info = new FrameInfo;
96  info->frame = frame;
97  CGDataProviderRef provider =
98  CGDataProviderCreateWithData(info, data, size, &FreeFrameBytes);
99 
100  // CGColorSpaceCreateDeviceRGB makes a device-specific colorSpace, so use a
101  // standardized one instead.
102  CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
103 
104  // Create a CGImage.
105  const size_t bits_per_pixel = 24;
106  const size_t bits_per_component = 8;
107  const bool should_interpolate = false;
108  CGImage* image = CGImageCreate(width, height, bits_per_component,
109  bits_per_pixel, bytes_per_row, color_space,
110  kCGBitmapByteOrderDefault, provider, nullptr,
111  should_interpolate, kCGRenderingIntentDefault);
112 
113  // Dispose of temporary data.
114  CGColorSpaceRelease(color_space);
115  CGDataProviderRelease(provider);
116 
117  return image;
118 }
119 
120 CGImageRef AppleVideoRenderer::Impl::RenderPlanarFrame(
121  std::shared_ptr<DecodedFrame> frame) {
122  CVPixelBufferRef pixel_buffer;
123  bool free_pixel_buffer;
124  auto pix_fmt = get<PixelFormat>(frame->format);
126  uint8_t* data = const_cast<uint8_t*>(frame->data[0]);
127  pixel_buffer = reinterpret_cast<CVPixelBufferRef>(data);
128  free_pixel_buffer = false;
129  } else {
130  if (pix_fmt != PixelFormat::YUV420P)
131  return nullptr;
132 
133  const OSType cv_pix_fmt = kCVPixelFormatType_420YpCbCr8Planar;
134  auto* info = new FrameInfo;
135  info->frame = frame;
136  info->widths[0] = frame->stream_info->width;
137  info->widths[1] = info->widths[2] = frame->stream_info->width / 2;
138  info->heights[0] = frame->stream_info->height;
139  info->heights[1] = info->heights[2] = frame->stream_info->height / 2;
140 
141  const auto status = CVPixelBufferCreateWithPlanarBytes(
142  nullptr, frame->stream_info->width, frame->stream_info->height,
143  cv_pix_fmt, nullptr, 0, frame->data.size(),
144  reinterpret_cast<void**>(const_cast<uint8_t**>(frame->data.data())),
145  info->widths, info->heights,
146  const_cast<size_t*>(frame->linesize.data()), &FreeFramePlanar, info,
147  nullptr, &pixel_buffer);
148  if (status != 0) {
149  LOG(ERROR) << "CVPixelBufferCreateWithPlanarBytes error " << status;
150  delete info;
151  return nullptr;
152  }
153 
154  free_pixel_buffer = true;
155  }
156 
157  CGImage* ret;
158  // This retains the buffer, so the Frame is free to be deleted.
159  const auto status =
160  VTCreateCGImageFromCVPixelBuffer(pixel_buffer, nullptr, &ret);
161  if (free_pixel_buffer)
162  CVPixelBufferRelease(pixel_buffer);
163 
164  if (status != 0) {
165  LOG(ERROR) << "VTCreateCGImageFromCVPixelBuffer error " << status;
166  return nullptr;
167  }
168  return ret;
169 }
170 
171 
174 
176  return impl_->fill_mode();
177 }
178 
179 CGImageRef AppleVideoRenderer::Render(double* delay,
180  Rational<uint32_t>* sample_aspect_ratio) {
181  return impl_->Render(delay, sample_aspect_ratio);
182 }
183 
185  impl_->SetPlayer(player);
186 }
187 
189  impl_->Attach(stream);
190 }
191 
193  impl_->Detach();
194 }
195 
197  return impl_->VideoPlaybackQuality();
198 }
199 
201  return impl_->SetVideoFillMode(mode);
202 }
203 
204 } // namespace media
205 } // namespace shaka
bool SetVideoFillMode(VideoFillMode mode) override
VideoFillMode
Definition: utils.h:41
struct VideoPlaybackQuality VideoPlaybackQuality() const override
std::shared_ptr< shaka::media::DecodedFrame > frame
size_t heights[4]
int width
void SetPlayer(const MediaPlayer *player) override
size_t widths[4]
int height
CGImageRef Render(double *delay, Rational< uint32_t > *sample_aspect_ratio)
void Attach(const DecodedStream *stream) override
CGImageRef Render(double *delay=nullptr, Rational< uint32_t > *sample_aspect_ratio=nullptr)