blob: 877869ec9223b98af5b7ffa36af60d226896f3c1 [file] [log] [blame]
Andrew Top2a796462018-06-29 09:04:04 -07001// Copyright 2018 The Cobalt Authors. All Rights Reserved.
Mike Fleming3933d922018-04-02 10:53:08 -07002//
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// http://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// This file contains the explicit specialization of the VideoDecoderImpl class
16// for the value 'FFMPEG'.
17
18#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h"
19
Chad Duffinac9ac062019-07-23 10:06:45 -070020#include "starboard/common/string.h"
Mike Fleming3933d922018-04-02 10:53:08 -070021#include "starboard/linux/shared/decode_target_internal.h"
22#include "starboard/memory.h"
23#include "starboard/thread.h"
24
25namespace starboard {
26namespace shared {
27namespace ffmpeg {
28
29namespace {
30
31// FFmpeg requires its decoding buffers to align with platform alignment. It
32// mentions inside
33// http://ffmpeg.org/doxygen/trunk/structAVFrame.html#aa52bfc6605f6a3059a0c3226cc0f6567
34// that the alignment on most modern desktop systems are 16 or 32.
35static const int kAlignment = 32;
36
37size_t AlignUp(size_t size, int alignment) {
38 SB_DCHECK((alignment & (alignment - 1)) == 0);
39 return (size + alignment - 1) & ~(alignment - 1);
40}
41
42size_t GetYV12SizeInBytes(int32_t width, int32_t height) {
43 return width * height * 3 / 2;
44}
45
Kaido Kert612c0202020-01-22 10:28:42 -080046#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -070047
48void ReleaseBuffer(void* opaque, uint8_t* data) {
Kaido Kertecb76eb2020-11-03 16:50:30 -080049 SbMemoryDeallocateAligned(data);
Mike Fleming3933d922018-04-02 10:53:08 -070050}
51
52int AllocateBufferCallback(AVCodecContext* codec_context,
53 AVFrame* frame,
54 int flags) {
55 VideoDecoderImpl<FFMPEG>* video_decoder =
56 static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque);
57 return video_decoder->AllocateBuffer(codec_context, frame, flags);
58}
59
Kaido Kert612c0202020-01-22 10:28:42 -080060#else // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -070061
62int AllocateBufferCallback(AVCodecContext* codec_context, AVFrame* frame) {
63 VideoDecoderImpl<FFMPEG>* video_decoder =
64 static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque);
65 return video_decoder->AllocateBuffer(codec_context, frame);
66}
67
68void ReleaseBuffer(AVCodecContext*, AVFrame* frame) {
Kaido Kertecb76eb2020-11-03 16:50:30 -080069 SbMemoryDeallocateAligned(frame->opaque);
Mike Fleming3933d922018-04-02 10:53:08 -070070 frame->opaque = NULL;
71
72 // The FFmpeg API expects us to zero the data pointers in this callback.
Kaido Kert6f3fc442021-06-25 11:58:59 -070073 memset(frame->data, 0, sizeof(frame->data));
Mike Fleming3933d922018-04-02 10:53:08 -070074}
Kaido Kert612c0202020-01-22 10:28:42 -080075#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -070076
Andrew Savage9708d322022-07-15 14:00:51 -070077AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaVideoCodec video_codec) {
78 // Note: although SbMediaVideoCodec values exist for them, MPEG2VIDEO, THEORA,
79 // and VC1 are not included here since we do not officially support them.
80 // Also, note that VP9 and HEVC use different decoders (not FFmpeg).
81 switch (video_codec) {
82 case kSbMediaVideoCodecH264:
83 return AV_CODEC_ID_H264;
84 case kSbMediaVideoCodecVp8:
85 return AV_CODEC_ID_VP8;
86 default:
87 SB_DLOG(WARNING) << "FFmpeg decoder does not support SbMediaVideoCodec "
88 << video_codec;
89 }
90 return AV_CODEC_ID_NONE;
91}
92
Mike Fleming3933d922018-04-02 10:53:08 -070093const bool g_registered =
94 FFMPEGDispatch::RegisterSpecialization(FFMPEG,
95 LIBAVCODEC_VERSION_MAJOR,
96 LIBAVFORMAT_VERSION_MAJOR,
97 LIBAVUTIL_VERSION_MAJOR);
98
99} // namespace
100
101VideoDecoderImpl<FFMPEG>::VideoDecoderImpl(
102 SbMediaVideoCodec video_codec,
103 SbPlayerOutputMode output_mode,
104 SbDecodeTargetGraphicsContextProvider*
105 decode_target_graphics_context_provider)
106 : video_codec_(video_codec),
107 codec_context_(NULL),
108 av_frame_(NULL),
109 stream_ended_(false),
Kaido Kert72bde072021-03-12 15:55:15 -0800110 error_occurred_(false),
Mike Fleming3933d922018-04-02 10:53:08 -0700111 decoder_thread_(kSbThreadInvalid),
112 output_mode_(output_mode),
113 decode_target_graphics_context_provider_(
114 decode_target_graphics_context_provider),
115 decode_target_(kSbDecodeTargetInvalid) {
116 SB_DCHECK(g_registered) << "Decoder Specialization registration failed.";
117 ffmpeg_ = FFMPEGDispatch::GetInstance();
118 SB_DCHECK(ffmpeg_);
119 if ((ffmpeg_->specialization_version()) == FFMPEG) {
120 InitializeCodec();
121 }
122}
123
124VideoDecoderImpl<FFMPEG>::~VideoDecoderImpl() {
125 Reset();
126 TeardownCodec();
127}
128
129// static
130VideoDecoder* VideoDecoderImpl<FFMPEG>::Create(
131 SbMediaVideoCodec video_codec,
132 SbPlayerOutputMode output_mode,
133 SbDecodeTargetGraphicsContextProvider*
134 decode_target_graphics_context_provider) {
135 return new VideoDecoderImpl<FFMPEG>(video_codec, output_mode,
136 decode_target_graphics_context_provider);
137}
138
139void VideoDecoderImpl<FFMPEG>::Initialize(
140 const DecoderStatusCB& decoder_status_cb,
141 const ErrorCB& error_cb) {
142 SB_DCHECK(decoder_status_cb);
143 SB_DCHECK(!decoder_status_cb_);
144 SB_DCHECK(error_cb);
145 SB_DCHECK(!error_cb_);
146
147 decoder_status_cb_ = decoder_status_cb;
148 error_cb_ = error_cb;
149}
150
Kaido Kert52711092023-02-14 18:06:26 -0800151void VideoDecoderImpl<FFMPEG>::WriteInputBuffers(
152 const InputBuffers& input_buffers) {
153 SB_DCHECK(input_buffers.size() == 1);
154 SB_DCHECK(input_buffers[0]);
Mike Fleming3933d922018-04-02 10:53:08 -0700155 SB_DCHECK(queue_.Poll().type == kInvalid);
156 SB_DCHECK(decoder_status_cb_);
157
Kaido Kert52711092023-02-14 18:06:26 -0800158 const auto& input_buffer = input_buffers[0];
159
Mike Fleming3933d922018-04-02 10:53:08 -0700160 if (stream_ended_) {
161 SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream().";
162 return;
163 }
164
165 if (!SbThreadIsValid(decoder_thread_)) {
166 decoder_thread_ = SbThreadCreate(
167 0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true, "ff_video_dec",
168 &VideoDecoderImpl<FFMPEG>::ThreadEntryPoint, this);
169 SB_DCHECK(SbThreadIsValid(decoder_thread_));
170 }
Mike Fleming3933d922018-04-02 10:53:08 -0700171 queue_.Put(Event(input_buffer));
172}
173
174void VideoDecoderImpl<FFMPEG>::WriteEndOfStream() {
175 SB_DCHECK(decoder_status_cb_);
176
177 // We have to flush the decoder to decode the rest frames and to ensure that
178 // Decode() is not called when the stream is ended.
179 stream_ended_ = true;
180
181 if (!SbThreadIsValid(decoder_thread_)) {
Kaido Kert52711092023-02-14 18:06:26 -0800182 // In case there is no WriteInputBuffers() call before WriteEndOfStream(),
Mike Fleming3933d922018-04-02 10:53:08 -0700183 // don't create the decoder thread and send the EOS frame directly.
184 decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
185 return;
186 }
187
188 queue_.Put(Event(kWriteEndOfStream));
189}
190
191void VideoDecoderImpl<FFMPEG>::Reset() {
192 // Join the thread to ensure that all callbacks in process are finished.
193 if (SbThreadIsValid(decoder_thread_)) {
194 queue_.Put(Event(kReset));
195 SbThreadJoin(decoder_thread_, NULL);
196 }
197
198 if (codec_context_ != NULL) {
199 ffmpeg_->avcodec_flush_buffers(codec_context_);
200 }
201
202 decoder_thread_ = kSbThreadInvalid;
203 stream_ended_ = false;
204
205 if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
206 TeardownCodec();
207 InitializeCodec();
208 }
Andrew Top8a7c8792018-10-26 08:54:41 -0700209
Kaido Kertb48d5d32023-06-30 09:59:25 -0700210 ScopedLock lock(decode_target_and_frames_mutex_);
Andrew Top8a7c8792018-10-26 08:54:41 -0700211 decltype(frames_) frames;
212 frames_ = std::queue<scoped_refptr<CpuVideoFrame>>();
Mike Fleming3933d922018-04-02 10:53:08 -0700213}
214
215bool VideoDecoderImpl<FFMPEG>::is_valid() const {
216 return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL);
217}
218
219// static
220void* VideoDecoderImpl<FFMPEG>::ThreadEntryPoint(void* context) {
221 SB_DCHECK(context);
222 VideoDecoderImpl<FFMPEG>* decoder =
223 reinterpret_cast<VideoDecoderImpl<FFMPEG>*>(context);
224 decoder->DecoderThreadFunc();
225 return NULL;
226}
227
228void VideoDecoderImpl<FFMPEG>::DecoderThreadFunc() {
229 for (;;) {
230 Event event = queue_.Get();
231 if (event.type == kReset) {
232 return;
233 }
Kaido Kert72bde072021-03-12 15:55:15 -0800234 if (error_occurred_) {
Mike Fleming3933d922018-04-02 10:53:08 -0700235 continue;
236 }
237 if (event.type == kWriteInputBuffer) {
238 // Send |input_buffer| to ffmpeg and try to decode one frame.
239 AVPacket packet;
240 ffmpeg_->av_init_packet(&packet);
241 packet.data = const_cast<uint8_t*>(event.input_buffer->data());
242 packet.size = event.input_buffer->size();
243 packet.pts = event.input_buffer->timestamp();
244 codec_context_->reordered_opaque = packet.pts;
245
246 DecodePacket(&packet);
247 decoder_status_cb_(kNeedMoreInput, NULL);
248 } else {
249 SB_DCHECK(event.type == kWriteEndOfStream);
250 // Stream has ended, try to decode any frames left in ffmpeg.
251 AVPacket packet;
252 do {
253 ffmpeg_->av_init_packet(&packet);
254 packet.data = NULL;
255 packet.size = 0;
256 packet.pts = 0;
257 } while (DecodePacket(&packet));
258
259 decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
260 }
261 }
262}
263
264bool VideoDecoderImpl<FFMPEG>::DecodePacket(AVPacket* packet) {
265 SB_DCHECK(packet != NULL);
266
Kaido Kert788710a2023-06-05 07:50:22 -0700267 if (ffmpeg_->avcodec_version() > kAVCodecSupportsAvFrameAlloc) {
268 ffmpeg_->av_frame_unref(av_frame_);
269 } else {
270 ffmpeg_->avcodec_get_frame_defaults(av_frame_);
271 }
Mike Fleming3933d922018-04-02 10:53:08 -0700272 int frame_decoded = 0;
273 int decode_result = ffmpeg_->avcodec_decode_video2(codec_context_, av_frame_,
274 &frame_decoded, packet);
275 if (decode_result < 0) {
276 SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result "
277 << decode_result;
Andrew Top8b6b16e2018-07-25 17:44:41 -0700278 error_cb_(kSbPlayerErrorDecode,
279 FormatString("avcodec_decode_video2() failed with result %d.",
280 decode_result));
Kaido Kert72bde072021-03-12 15:55:15 -0800281 error_occurred_ = true;
Mike Fleming3933d922018-04-02 10:53:08 -0700282 return false;
283 }
284 if (frame_decoded == 0) {
285 return false;
286 }
287
288 if (av_frame_->opaque == NULL) {
289 SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data.";
Andrew Top8b6b16e2018-07-25 17:44:41 -0700290 error_cb_(kSbPlayerErrorDecode,
291 "Video frame was produced yet has invalid frame data.");
Kaido Kert72bde072021-03-12 15:55:15 -0800292 error_occurred_ = true;
Mike Fleming3933d922018-04-02 10:53:08 -0700293 return false;
294 }
295
Andrew Top286dd782018-10-02 16:52:45 -0700296 int codec_aligned_width = av_frame_->width;
297 int codec_aligned_height = av_frame_->height;
298 int codec_linesize_align[AV_NUM_DATA_POINTERS];
Kaido Kertecb76eb2020-11-03 16:50:30 -0800299 ffmpeg_->avcodec_align_dimensions2(codec_context_, &codec_aligned_width,
300 &codec_aligned_height,
301 codec_linesize_align);
Andrew Top286dd782018-10-02 16:52:45 -0700302
Andrew Savagedb232fd2022-06-23 12:28:40 -0700303 int y_pitch = AlignUp(av_frame_->width, codec_linesize_align[0] * 2);
304 int uv_pitch = av_frame_->linesize[1];
Mike Fleming3933d922018-04-02 10:53:08 -0700305
Chad Duffinac9ac062019-07-23 10:06:45 -0700306 const int kBitDepth = 8;
Mike Fleming3933d922018-04-02 10:53:08 -0700307 scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame(
Andrew Savagedb232fd2022-06-23 12:28:40 -0700308 kBitDepth, av_frame_->width, av_frame_->height, y_pitch, uv_pitch,
Chad Duffinac9ac062019-07-23 10:06:45 -0700309 av_frame_->reordered_opaque, av_frame_->data[0], av_frame_->data[1],
310 av_frame_->data[2]);
Mike Fleming3933d922018-04-02 10:53:08 -0700311
312 bool result = true;
313 if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
Kaido Kertb48d5d32023-06-30 09:59:25 -0700314 ScopedLock lock(decode_target_and_frames_mutex_);
Andrew Top8a7c8792018-10-26 08:54:41 -0700315 frames_.push(frame);
Mike Fleming3933d922018-04-02 10:53:08 -0700316 }
317
318 decoder_status_cb_(kBufferFull, frame);
319
Andrew Top8a7c8792018-10-26 08:54:41 -0700320 return true;
Mike Fleming3933d922018-04-02 10:53:08 -0700321}
322
Andrew Top8a7c8792018-10-26 08:54:41 -0700323void VideoDecoderImpl<FFMPEG>::UpdateDecodeTarget_Locked(
Mike Fleming3933d922018-04-02 10:53:08 -0700324 const scoped_refptr<CpuVideoFrame>& frame) {
325 SbDecodeTarget decode_target = DecodeTargetCreate(
326 decode_target_graphics_context_provider_, frame, decode_target_);
327
328 // Lock only after the post to the renderer thread, to prevent deadlock.
Mike Fleming3933d922018-04-02 10:53:08 -0700329 decode_target_ = decode_target;
330
331 if (!SbDecodeTargetIsValid(decode_target)) {
332 SB_LOG(ERROR) << "Could not acquire a decode target from provider.";
Mike Fleming3933d922018-04-02 10:53:08 -0700333 }
Mike Fleming3933d922018-04-02 10:53:08 -0700334}
335
336void VideoDecoderImpl<FFMPEG>::InitializeCodec() {
337 codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL);
338
339 if (codec_context_ == NULL) {
340 SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
341 return;
342 }
343
344 codec_context_->codec_type = AVMEDIA_TYPE_VIDEO;
Andrew Savage9708d322022-07-15 14:00:51 -0700345 codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(video_codec_);
Mike Fleming3933d922018-04-02 10:53:08 -0700346 codec_context_->profile = FF_PROFILE_UNKNOWN;
347 codec_context_->coded_width = 0;
348 codec_context_->coded_height = 0;
349 codec_context_->pix_fmt = PIX_FMT_NONE;
350
351 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
352 codec_context_->thread_count = 2;
353 codec_context_->opaque = this;
Andrew Top3afedff2019-08-29 16:48:11 -0700354#if defined(CODEC_FLAG_EMU_EDGE)
Mike Fleming3933d922018-04-02 10:53:08 -0700355 codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
Andrew Top3afedff2019-08-29 16:48:11 -0700356#endif
Kaido Kert612c0202020-01-22 10:28:42 -0800357#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700358 codec_context_->get_buffer2 = AllocateBufferCallback;
Kaido Kert612c0202020-01-22 10:28:42 -0800359#else // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700360 codec_context_->get_buffer = AllocateBufferCallback;
361 codec_context_->release_buffer = ReleaseBuffer;
Kaido Kert612c0202020-01-22 10:28:42 -0800362#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700363
364 codec_context_->extradata = NULL;
365 codec_context_->extradata_size = 0;
366
367 AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id);
368
369 if (codec == NULL) {
370 SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context";
371 TeardownCodec();
372 return;
373 }
374
375 int rv = ffmpeg_->OpenCodec(codec_context_, codec);
376 if (rv < 0) {
377 SB_LOG(ERROR) << "Unable to open codec";
378 TeardownCodec();
379 return;
380 }
381
Kaido Kert612c0202020-01-22 10:28:42 -0800382#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700383 av_frame_ = ffmpeg_->av_frame_alloc();
Kaido Kert612c0202020-01-22 10:28:42 -0800384#else // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700385 av_frame_ = ffmpeg_->avcodec_alloc_frame();
Kaido Kert612c0202020-01-22 10:28:42 -0800386#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700387 if (av_frame_ == NULL) {
388 SB_LOG(ERROR) << "Unable to allocate audio frame";
389 TeardownCodec();
390 }
391}
392
393void VideoDecoderImpl<FFMPEG>::TeardownCodec() {
394 if (codec_context_) {
395 ffmpeg_->CloseCodec(codec_context_);
Kaido Kert612c0202020-01-22 10:28:42 -0800396 ffmpeg_->FreeContext(&codec_context_);
Mike Fleming3933d922018-04-02 10:53:08 -0700397 }
Kaido Kert612c0202020-01-22 10:28:42 -0800398 ffmpeg_->FreeFrame(&av_frame_);
Mike Fleming3933d922018-04-02 10:53:08 -0700399
400 if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
Kaido Kertb48d5d32023-06-30 09:59:25 -0700401 ScopedLock lock(decode_target_and_frames_mutex_);
Mike Fleming3933d922018-04-02 10:53:08 -0700402 if (SbDecodeTargetIsValid(decode_target_)) {
403 DecodeTargetRelease(decode_target_graphics_context_provider_,
404 decode_target_);
405 decode_target_ = kSbDecodeTargetInvalid;
406 }
407 }
408}
409
410// When in decode-to-texture mode, this returns the current decoded video frame.
411SbDecodeTarget VideoDecoderImpl<FFMPEG>::GetCurrentDecodeTarget() {
412 SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
413
414 // We must take a lock here since this function can be called from a
415 // separate thread.
Kaido Kertb48d5d32023-06-30 09:59:25 -0700416 ScopedLock lock(decode_target_and_frames_mutex_);
Andrew Top8a7c8792018-10-26 08:54:41 -0700417 while (frames_.size() > 1 && frames_.front()->HasOneRef()) {
418 frames_.pop();
419 }
420 if (!frames_.empty()) {
421 UpdateDecodeTarget_Locked(frames_.front());
422 }
Mike Fleming3933d922018-04-02 10:53:08 -0700423 if (SbDecodeTargetIsValid(decode_target_)) {
424 // Make a disposable copy, since the state is internally reused by this
425 // class (to avoid recreating GL objects).
426 return DecodeTargetCopy(decode_target_);
427 } else {
428 return kSbDecodeTargetInvalid;
429 }
430}
431
Kaido Kert612c0202020-01-22 10:28:42 -0800432#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700433
434int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context,
435 AVFrame* frame,
436 int flags) {
437 if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
438 codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
439 SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
440 return AVERROR(EINVAL);
441 }
442
443 int ret = ffmpeg_->av_image_check_size(codec_context->width,
444 codec_context->height, 0, NULL);
445 if (ret < 0) {
446 return ret;
447 }
448
Andrew Top286dd782018-10-02 16:52:45 -0700449 int codec_aligned_width = codec_context->width;
450 int codec_aligned_height = codec_context->height;
451 int codec_linesize_align[AV_NUM_DATA_POINTERS];
Kaido Kertecb76eb2020-11-03 16:50:30 -0800452 ffmpeg_->avcodec_align_dimensions2(codec_context, &codec_aligned_width,
453 &codec_aligned_height,
454 codec_linesize_align);
Andrew Top286dd782018-10-02 16:52:45 -0700455
456 // Align to linesize alignment * 2 as we will divide y_stride by 2 for
457 // u and v planes.
458 size_t y_stride = AlignUp(codec_context->width, codec_linesize_align[0] * 2);
Mike Fleming3933d922018-04-02 10:53:08 -0700459 size_t uv_stride = y_stride / 2;
Andrew Top286dd782018-10-02 16:52:45 -0700460 size_t aligned_height = codec_aligned_height;
Mike Fleming3933d922018-04-02 10:53:08 -0700461
462 uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned(
463 kAlignment, GetYV12SizeInBytes(y_stride, aligned_height)));
464
465 frame->data[0] = frame_buffer;
466 frame->linesize[0] = y_stride;
467
468 frame->data[1] = frame_buffer + y_stride * aligned_height;
469 frame->linesize[1] = uv_stride;
470
471 frame->data[2] = frame->data[1] + uv_stride * aligned_height / 2;
472 frame->linesize[2] = uv_stride;
473
474 frame->opaque = frame;
475 frame->width = codec_context->width;
476 frame->height = codec_context->height;
477 frame->format = codec_context->pix_fmt;
478
479 frame->reordered_opaque = codec_context->reordered_opaque;
480
481 frame->buf[0] = static_cast<AVBufferRef*>(ffmpeg_->av_buffer_create(
482 frame_buffer, GetYV12SizeInBytes(y_stride, aligned_height),
483 &ReleaseBuffer, frame->opaque, 0));
484 return 0;
485}
486
Kaido Kert612c0202020-01-22 10:28:42 -0800487#else // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700488
489int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context,
490 AVFrame* frame) {
491 if (codec_context->pix_fmt != PIX_FMT_YUV420P &&
492 codec_context->pix_fmt != PIX_FMT_YUVJ420P) {
493 SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt;
494 return AVERROR(EINVAL);
495 }
496
497 int ret = ffmpeg_->av_image_check_size(codec_context->width,
498 codec_context->height, 0, NULL);
499 if (ret < 0) {
500 return ret;
501 }
502
Andrew Top286dd782018-10-02 16:52:45 -0700503 int codec_aligned_width = codec_context->width;
504 int codec_aligned_height = codec_context->height;
505 int codec_linesize_align[AV_NUM_DATA_POINTERS];
Kaido Kertecb76eb2020-11-03 16:50:30 -0800506 ffmpeg_->avcodec_align_dimensions2(codec_context, &codec_aligned_width,
507 &codec_aligned_height,
508 codec_linesize_align);
Andrew Top286dd782018-10-02 16:52:45 -0700509
510 // Align to linesize alignment * 2 as we will divide y_stride by 2 for
511 // u and v planes.
512 size_t y_stride = AlignUp(codec_context->width, codec_linesize_align[0] * 2);
Mike Fleming3933d922018-04-02 10:53:08 -0700513 size_t uv_stride = y_stride / 2;
Andrew Top286dd782018-10-02 16:52:45 -0700514 size_t aligned_height = codec_aligned_height;
Mike Fleming3933d922018-04-02 10:53:08 -0700515 uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned(
516 kAlignment, GetYV12SizeInBytes(y_stride, aligned_height)));
517
518 // y plane
519 frame->base[0] = frame_buffer;
520 frame->data[0] = frame->base[0];
521 frame->linesize[0] = y_stride;
522 // u plane
523 frame->base[1] = frame_buffer + y_stride * aligned_height;
524 frame->data[1] = frame->base[1];
525 frame->linesize[1] = uv_stride;
526 // v plane
527 frame->base[2] = frame->base[1] + uv_stride * aligned_height / 2;
528 frame->data[2] = frame->base[2];
529 frame->linesize[2] = uv_stride;
530
531 frame->opaque = frame_buffer;
532 frame->type = FF_BUFFER_TYPE_USER;
533 frame->pkt_pts =
534 codec_context->pkt ? codec_context->pkt->pts : AV_NOPTS_VALUE;
535 frame->width = codec_context->width;
536 frame->height = codec_context->height;
537 frame->format = codec_context->pix_fmt;
538
539 frame->reordered_opaque = codec_context->reordered_opaque;
540
541 return 0;
542}
Kaido Kert612c0202020-01-22 10:28:42 -0800543#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
Mike Fleming3933d922018-04-02 10:53:08 -0700544
545} // namespace ffmpeg
546} // namespace shared
547} // namespace starboard