Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(461)

Side by Side Diff: services/media/framework_ffmpeg/ffmpeg_video_decoder.cc

Issue 1923763002: Motown: Ffmpeg video decoder (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <algorithm>
6
5 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "services/media/framework_ffmpeg/ffmpeg_formatting.h"
6 #include "services/media/framework_ffmpeg/ffmpeg_video_decoder.h" 9 #include "services/media/framework_ffmpeg/ffmpeg_video_decoder.h"
10 extern "C" {
11 #include "third_party/ffmpeg/libavutil/imgutils.h"
12 }
7 13
8 namespace mojo { 14 namespace mojo {
9 namespace media { 15 namespace media {
10 16
11 FfmpegVideoDecoder::FfmpegVideoDecoder(AvCodecContextPtr av_codec_context) 17 FfmpegVideoDecoder::FfmpegVideoDecoder(AvCodecContextPtr av_codec_context)
12 : FfmpegDecoderBase(std::move(av_codec_context)) { 18 : FfmpegDecoderBase(std::move(av_codec_context)) {
13 DCHECK(context()); 19 DCHECK(context());
20
21 context()->opaque = this;
22 context()->get_buffer2 = AllocateBufferForAvFrame;
23 context()->refcounted_frames = 1;
14 } 24 }
15 25
16 FfmpegVideoDecoder::~FfmpegVideoDecoder() {} 26 FfmpegVideoDecoder::~FfmpegVideoDecoder() {}
17 27
18 int FfmpegVideoDecoder::Decode(const AVPacket& av_packet, 28 int FfmpegVideoDecoder::Decode(const AVPacket& av_packet,
19 const ffmpeg::AvFramePtr& av_frame_ptr, 29 const ffmpeg::AvFramePtr& av_frame_ptr,
20 PayloadAllocator* allocator, 30 PayloadAllocator* allocator,
21 bool* frame_decoded_out) { 31 bool* frame_decoded_out) {
22 DCHECK(av_frame_ptr);
23 DCHECK(allocator); 32 DCHECK(allocator);
24 DCHECK(frame_decoded_out); 33 DCHECK(frame_decoded_out);
25 DCHECK(context()); 34 DCHECK(context());
35 DCHECK(av_frame_ptr);
36
37 DCHECK(av_packet.pts != AV_NOPTS_VALUE);
38
39 // Use the provided allocator (for allocations in AllocateBufferForAvFrame).
40 allocator_ = allocator;
41
42 // We put the pts here so it can be recovered later in CreateOutputPacket.
43 // Ffmpeg deals with the frame ordering issues.
44 context()->reordered_opaque = av_packet.pts;
26 45
27 int frame_decoded = 0; 46 int frame_decoded = 0;
28 int input_bytes_used = avcodec_decode_video2( 47 int input_bytes_used = avcodec_decode_video2(
29 context().get(), av_frame_ptr.get(), &frame_decoded, &av_packet); 48 context().get(), av_frame_ptr.get(), &frame_decoded, &av_packet);
30 *frame_decoded_out = frame_decoded != 0; 49 *frame_decoded_out = frame_decoded != 0;
50
51 // We're done with this allocator.
52 allocator_ = nullptr;
53
31 return input_bytes_used; 54 return input_bytes_used;
32 } 55 }
33 56
57 void FfmpegVideoDecoder::Flush() {
58 FfmpegDecoderBase::Flush();
59 next_pts_ = Packet::kUnknownPts;
60 }
61
34 PacketPtr FfmpegVideoDecoder::CreateOutputPacket(const AVFrame& av_frame, 62 PacketPtr FfmpegVideoDecoder::CreateOutputPacket(const AVFrame& av_frame,
35 PayloadAllocator* allocator) { 63 PayloadAllocator* allocator) {
36 DCHECK(allocator); 64 DCHECK(allocator);
37 65
38 // End of stream is indicated when we're draining and produce no packet. 66 // Recover the pts deposited in Decode.
39 // TODO(dalesat): This is just a copy of the audio version. 67 next_pts_ = av_frame.reordered_opaque;
40 return Packet::Create(av_frame.pts, false, packet_size_, av_frame.data[0], 68
41 allocator); 69 AvBufferContext* av_buffer_context =
70 reinterpret_cast<AvBufferContext*>(av_buffer_get_opaque(av_frame.buf[0]));
71
72 return Packet::Create(
73 next_pts_,
74 false, // The base class is responsible for end-of-stream.
75 av_buffer_context->size(), av_buffer_context->Release(), allocator);
42 } 76 }
43 77
44 PacketPtr FfmpegVideoDecoder::CreateOutputEndOfStreamPacket() { 78 PacketPtr FfmpegVideoDecoder::CreateOutputEndOfStreamPacket() {
45 // TODO(dalesat): Presentation time for this packet. 79 return Packet::CreateEndOfStream(next_pts_);
46 return Packet::CreateEndOfStream(0);
47 } 80 }
48 81
49 int FfmpegVideoDecoder::AllocateBufferForAvFrame( 82 int FfmpegVideoDecoder::AllocateBufferForAvFrame(
50 AVCodecContext* av_codec_context, 83 AVCodecContext* av_codec_context,
51 AVFrame* av_frame, 84 AVFrame* av_frame,
52 int flags) { 85 int flags) {
53 // It's important to use av_codec_context here rather than context(), 86 // It's important to use av_codec_context here rather than context(),
54 // because av_codec_context is different for different threads when we're 87 // because av_codec_context is different for different threads when we're
55 // decoding on multiple threads. If this code is moved to an instance method, 88 // decoding on multiple threads. Be sure to avoid using self->context().
56 // be sure to avoid using context().
57 89
58 // TODO(dalesat): Not sure why/if this is needed. 90 // CODEC_CAP_DR1 is required in order to do allocation this way.
59 // int result = av_image_check_size( 91 DCHECK(av_codec_context->codec->capabilities & CODEC_CAP_DR1);
60 // av_codec_context->width,
61 // av_codec_context->height,
62 // 0,
63 // NULL);
64 // if (result < 0) {
65 // DCHECK(false) << "av_image_check_size failed";
66 // return result;
67 //}
68 92
69 // TODO(dalesat): Not sure why this is needed. 93 FfmpegVideoDecoder* self =
70 int coded_width = 94 reinterpret_cast<FfmpegVideoDecoder*>(av_codec_context->opaque);
71 std::max(av_codec_context->width, av_codec_context->coded_width); 95 DCHECK(self);
72 int coded_height = 96 DCHECK(self->allocator_);
73 std::max(av_codec_context->height, av_codec_context->coded_height);
74 DCHECK_EQ(coded_width, av_codec_context->coded_width)
75 << "coded width is less than width";
76 DCHECK_EQ(coded_height, av_codec_context->coded_height)
77 << "coded height is less than height";
78 97
79 // TODO(dalesat): Fill in av_frame->data and av_frame->data for each plane. 98 Extent visible_size(av_codec_context->width, av_codec_context->height);
99 const int result =
100 av_image_check_size(visible_size.width(), visible_size.height(), 0, NULL);
101 if (result < 0) {
102 return result;
103 }
80 104
81 av_frame->width = coded_width; 105 // FFmpeg has specific requirements on the allocation size of the frame. The
82 av_frame->height = coded_height; 106 // following logic replicates FFmpeg's allocation strategy to ensure buffers
107 // are not overread / overwritten. See ff_init_buffer_info() for details.
108
109 // When lowres is non-zero, dimensions should be divided by 2^(lowres), but
110 // since we don't use this, just DCHECK that it's zero.
111 DCHECK_EQ(av_codec_context->lowres, 0);
112 Extent coded_size(
113 std::max(visible_size.width(),
114 static_cast<size_t>(av_codec_context->coded_width)),
115 std::max(visible_size.height(),
116 static_cast<size_t>(av_codec_context->coded_height)));
117
118 VideoStreamType::FrameLayout frame_layout;
119
120 VideoStreamType::InfoForPixelFormat(
121 PixelFormatFromAVPixelFormat(av_codec_context->pix_fmt))
122 .BuildFrameLayout(coded_size, &frame_layout);
123
124 AvBufferContext* av_buffer_context =
125 new AvBufferContext(frame_layout.size, self->allocator_);
126 uint8_t* buffer = av_buffer_context->buffer();
127
128 // TODO(dalesat): For investigation purposes only...remove one day.
129 if (self->first_frame_) {
130 self->first_frame_ = false;
131 self->colorspace_ = av_codec_context->colorspace;
132 self->coded_size_ = coded_size;
133 } else {
134 if (av_codec_context->colorspace != self->colorspace_) {
135 LOG(WARNING) << " colorspace changed to " << av_codec_context->colorspace
136 << std::endl;
137 }
138 if (coded_size.width() != self->coded_size_.width()) {
139 LOG(WARNING) << " coded_size width changed to " << coded_size.width()
140 << std::endl;
141 }
142 if (coded_size.height() != self->coded_size_.height()) {
143 LOG(WARNING) << " coded_size height changed to " << coded_size.height()
144 << std::endl;
145 }
146 self->colorspace_ = av_codec_context->colorspace;
147 self->coded_size_ = coded_size;
148 }
149
150 if (buffer == nullptr) {
151 LOG(ERROR) << "failed to allocate buffer of size " << frame_layout.size;
152 return -1;
153 }
154
155 // Decoders require a zeroed buffer.
156 std::memset(buffer, 0, frame_layout.size);
157
158 for (size_t plane = 0; plane < frame_layout.plane_count; ++plane) {
159 av_frame->data[plane] = buffer + frame_layout.plane_offset_for_plane(plane);
160 av_frame->linesize[plane] = frame_layout.line_stride_for_plane(plane);
161 }
162
163 // TODO(dalesat): Do we need to attach colorspace info to the packet?
164
165 av_frame->width = coded_size.width();
166 av_frame->height = coded_size.height();
83 av_frame->format = av_codec_context->pix_fmt; 167 av_frame->format = av_codec_context->pix_fmt;
84 av_frame->reordered_opaque = av_codec_context->reordered_opaque; 168 av_frame->reordered_opaque = av_codec_context->reordered_opaque;
85 169
86 av_frame->buf[0] = av_buffer_create( 170 DCHECK(av_frame->data[0] == buffer);
87 av_frame->data[0], // Because this is the first chunk in the buffer. 171 av_frame->buf[0] =
88 0, // TODO(dalesat): Provide this. 172 av_buffer_create(buffer, av_buffer_context->size(),
89 ReleaseBufferForAvFrame, 173 ReleaseBufferForAvFrame, av_buffer_context,
90 nullptr, // opaque 174 0); // flags
91 0); // flags
92 175
93 return 0; 176 return 0;
94 } 177 }
95 178
96 void FfmpegVideoDecoder::ReleaseBufferForAvFrame(void* opaque, 179 void FfmpegVideoDecoder::ReleaseBufferForAvFrame(void* opaque,
97 uint8_t* buffer) { 180 uint8_t* buffer) {
98 // Nothing to do. 181 AvBufferContext* av_buffer_context =
99 // TODO(dalesat): Can we get rid of this method altogether? 182 reinterpret_cast<AvBufferContext*>(opaque);
183 DCHECK(av_buffer_context);
184 // Either this buffer has already been released to someone else's ownership,
185 // or it's the same as the buffer parameter.
186 DCHECK(av_buffer_context->buffer() == nullptr ||
187 av_buffer_context->buffer() == buffer);
188 delete av_buffer_context;
100 } 189 }
101 190
102 } // namespace media 191 } // namespace media
103 } // namespace mojo 192 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698