xref: /aosp_15_r20/external/skia/experimental/ffmpeg/SkVideoEncoder.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "experimental/ffmpeg/SkVideoEncoder.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker extern "C" {
14*c8dee2aaSAndroid Build Coastguard Worker #include "libswscale/swscale.h"
15*c8dee2aaSAndroid Build Coastguard Worker }
16*c8dee2aaSAndroid Build Coastguard Worker 
17*c8dee2aaSAndroid Build Coastguard Worker class SkRandomAccessWStream {
18*c8dee2aaSAndroid Build Coastguard Worker     SkTDArray<char> fStorage;
19*c8dee2aaSAndroid Build Coastguard Worker     size_t          fPos = 0;
20*c8dee2aaSAndroid Build Coastguard Worker 
21*c8dee2aaSAndroid Build Coastguard Worker public:
SkRandomAccessWStream()22*c8dee2aaSAndroid Build Coastguard Worker     SkRandomAccessWStream() {}
23*c8dee2aaSAndroid Build Coastguard Worker 
pos() const24*c8dee2aaSAndroid Build Coastguard Worker     size_t pos() const { return fPos; }
25*c8dee2aaSAndroid Build Coastguard Worker 
size() const26*c8dee2aaSAndroid Build Coastguard Worker     size_t size() const { return fStorage.size(); }
27*c8dee2aaSAndroid Build Coastguard Worker 
write(const void * src,size_t bytes)28*c8dee2aaSAndroid Build Coastguard Worker     void write(const void* src, size_t bytes) {
29*c8dee2aaSAndroid Build Coastguard Worker         size_t len = fStorage.size();
30*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fPos <= len);
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker         size_t overwrite = std::min(len - fPos, bytes);
33*c8dee2aaSAndroid Build Coastguard Worker         if (overwrite) {
34*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("overwrite %zu bytes at %zu offset with %zu remaining\n", overwrite, fPos, bytes - overwrite);
35*c8dee2aaSAndroid Build Coastguard Worker             memcpy(&fStorage[fPos], src, overwrite);
36*c8dee2aaSAndroid Build Coastguard Worker             fPos += overwrite;
37*c8dee2aaSAndroid Build Coastguard Worker             src = (const char*)src + overwrite;
38*c8dee2aaSAndroid Build Coastguard Worker             bytes -= overwrite;
39*c8dee2aaSAndroid Build Coastguard Worker         }
40*c8dee2aaSAndroid Build Coastguard Worker         // bytes now represents the amount to append
41*c8dee2aaSAndroid Build Coastguard Worker         if (bytes) {
42*c8dee2aaSAndroid Build Coastguard Worker             fStorage.append(bytes, (const char*)src);
43*c8dee2aaSAndroid Build Coastguard Worker             fPos += bytes;
44*c8dee2aaSAndroid Build Coastguard Worker         }
45*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fPos <= fStorage.size());
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker 
seek(size_t pos)48*c8dee2aaSAndroid Build Coastguard Worker     void seek(size_t pos) {
49*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(pos <= fStorage.size());
50*c8dee2aaSAndroid Build Coastguard Worker         fPos = pos;
51*c8dee2aaSAndroid Build Coastguard Worker     }
52*c8dee2aaSAndroid Build Coastguard Worker 
detachAsData()53*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> detachAsData() {
54*c8dee2aaSAndroid Build Coastguard Worker         // TODO: could add an efficient detach to SkTDArray if we wanted, w/o copy
55*c8dee2aaSAndroid Build Coastguard Worker         return SkData::MakeWithCopy(fStorage.begin(), fStorage.size());
56*c8dee2aaSAndroid Build Coastguard Worker     }
57*c8dee2aaSAndroid Build Coastguard Worker };
58*c8dee2aaSAndroid Build Coastguard Worker 
59*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////////////////
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker // returns true on error (and may dump the particular error message)
check_err(int err,const int silentList[]=nullptr)62*c8dee2aaSAndroid Build Coastguard Worker static bool check_err(int err, const int silentList[] = nullptr) {
63*c8dee2aaSAndroid Build Coastguard Worker     if (err >= 0) {
64*c8dee2aaSAndroid Build Coastguard Worker         return false;
65*c8dee2aaSAndroid Build Coastguard Worker     }
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker     if (silentList) {
68*c8dee2aaSAndroid Build Coastguard Worker         for (; *silentList; ++silentList) {
69*c8dee2aaSAndroid Build Coastguard Worker             if (*silentList == err) {
70*c8dee2aaSAndroid Build Coastguard Worker                 return true;    // we still report the error, but we don't printf
71*c8dee2aaSAndroid Build Coastguard Worker             }
72*c8dee2aaSAndroid Build Coastguard Worker         }
73*c8dee2aaSAndroid Build Coastguard Worker     }
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker     char errbuf[128];
76*c8dee2aaSAndroid Build Coastguard Worker     const char *errbuf_ptr = errbuf;
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker     if (av_strerror(err, errbuf, sizeof(errbuf)) < 0) {
79*c8dee2aaSAndroid Build Coastguard Worker         errbuf_ptr = strerror(AVUNERROR(err));
80*c8dee2aaSAndroid Build Coastguard Worker     }
81*c8dee2aaSAndroid Build Coastguard Worker     SkDebugf("%s\n", errbuf_ptr);
82*c8dee2aaSAndroid Build Coastguard Worker     return true;
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker 
sk_write_packet(void * ctx,const uint8_t * buffer,int size)85*c8dee2aaSAndroid Build Coastguard Worker static int sk_write_packet(void* ctx, const uint8_t* buffer, int size) {
86*c8dee2aaSAndroid Build Coastguard Worker     SkRandomAccessWStream* stream = (SkRandomAccessWStream*)ctx;
87*c8dee2aaSAndroid Build Coastguard Worker     stream->write(buffer, size);
88*c8dee2aaSAndroid Build Coastguard Worker     return size;
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker 
sk_seek_packet(void * ctx,int64_t pos,int whence)91*c8dee2aaSAndroid Build Coastguard Worker static int64_t sk_seek_packet(void* ctx, int64_t pos, int whence) {
92*c8dee2aaSAndroid Build Coastguard Worker     SkRandomAccessWStream* stream = (SkRandomAccessWStream*)ctx;
93*c8dee2aaSAndroid Build Coastguard Worker     switch (whence) {
94*c8dee2aaSAndroid Build Coastguard Worker         case SEEK_SET:
95*c8dee2aaSAndroid Build Coastguard Worker             break;
96*c8dee2aaSAndroid Build Coastguard Worker         case SEEK_CUR:
97*c8dee2aaSAndroid Build Coastguard Worker             pos = (int64_t)stream->pos() + pos;
98*c8dee2aaSAndroid Build Coastguard Worker             break;
99*c8dee2aaSAndroid Build Coastguard Worker         case SEEK_END:
100*c8dee2aaSAndroid Build Coastguard Worker             pos = (int64_t)stream->size() + pos;
101*c8dee2aaSAndroid Build Coastguard Worker             break;
102*c8dee2aaSAndroid Build Coastguard Worker         default:
103*c8dee2aaSAndroid Build Coastguard Worker             return -1;
104*c8dee2aaSAndroid Build Coastguard Worker     }
105*c8dee2aaSAndroid Build Coastguard Worker     if (pos < 0 || pos > (int64_t)stream->size()) {
106*c8dee2aaSAndroid Build Coastguard Worker         return -1;
107*c8dee2aaSAndroid Build Coastguard Worker     }
108*c8dee2aaSAndroid Build Coastguard Worker     stream->seek(SkToSizeT(pos));
109*c8dee2aaSAndroid Build Coastguard Worker     return pos;
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker 
SkVideoEncoder()112*c8dee2aaSAndroid Build Coastguard Worker SkVideoEncoder::SkVideoEncoder() {
113*c8dee2aaSAndroid Build Coastguard Worker     fInfo = SkImageInfo::MakeUnknown();
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker 
~SkVideoEncoder()116*c8dee2aaSAndroid Build Coastguard Worker SkVideoEncoder::~SkVideoEncoder() {
117*c8dee2aaSAndroid Build Coastguard Worker     this->reset();
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker     if (fSWScaleCtx) {
120*c8dee2aaSAndroid Build Coastguard Worker         sws_freeContext(fSWScaleCtx);
121*c8dee2aaSAndroid Build Coastguard Worker     }
122*c8dee2aaSAndroid Build Coastguard Worker }
123*c8dee2aaSAndroid Build Coastguard Worker 
reset()124*c8dee2aaSAndroid Build Coastguard Worker void SkVideoEncoder::reset() {
125*c8dee2aaSAndroid Build Coastguard Worker     if (fFrame) {
126*c8dee2aaSAndroid Build Coastguard Worker         av_frame_free(&fFrame);
127*c8dee2aaSAndroid Build Coastguard Worker         fFrame = nullptr;
128*c8dee2aaSAndroid Build Coastguard Worker     }
129*c8dee2aaSAndroid Build Coastguard Worker     if (fEncoderCtx) {
130*c8dee2aaSAndroid Build Coastguard Worker         avcodec_free_context(&fEncoderCtx);
131*c8dee2aaSAndroid Build Coastguard Worker         fEncoderCtx = nullptr;
132*c8dee2aaSAndroid Build Coastguard Worker     }
133*c8dee2aaSAndroid Build Coastguard Worker     if (fFormatCtx) {
134*c8dee2aaSAndroid Build Coastguard Worker         avformat_free_context(fFormatCtx);
135*c8dee2aaSAndroid Build Coastguard Worker         fFormatCtx = nullptr;
136*c8dee2aaSAndroid Build Coastguard Worker     }
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker     av_packet_free(&fPacket);
139*c8dee2aaSAndroid Build Coastguard Worker     fPacket = nullptr;
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker     fSurface.reset();
142*c8dee2aaSAndroid Build Coastguard Worker     fWStream.reset();
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker 
init(int fps)145*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoEncoder::init(int fps) {
146*c8dee2aaSAndroid Build Coastguard Worker     // only support this for now
147*c8dee2aaSAndroid Build Coastguard Worker     AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker     this->reset();
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     fWStream.reset(new SkRandomAccessWStream);
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker     int bufferSize = 4 * 1024;
154*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* buffer = (uint8_t*)av_malloc(bufferSize);
155*c8dee2aaSAndroid Build Coastguard Worker     if (!buffer) {
156*c8dee2aaSAndroid Build Coastguard Worker         return false;
157*c8dee2aaSAndroid Build Coastguard Worker     }
158*c8dee2aaSAndroid Build Coastguard Worker     fStreamCtx = avio_alloc_context(buffer, bufferSize, AVIO_FLAG_WRITE, fWStream.get(),
159*c8dee2aaSAndroid Build Coastguard Worker                                     nullptr, sk_write_packet, sk_seek_packet);
160*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fStreamCtx);
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker     avformat_alloc_output_context2(&fFormatCtx, nullptr, "mp4", nullptr);
163*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fFormatCtx);
164*c8dee2aaSAndroid Build Coastguard Worker     fFormatCtx->pb = fStreamCtx;
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     const auto* output_format = fFormatCtx->oformat;
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     if (output_format->video_codec == AV_CODEC_ID_NONE) {
169*c8dee2aaSAndroid Build Coastguard Worker         return false;
170*c8dee2aaSAndroid Build Coastguard Worker     }
171*c8dee2aaSAndroid Build Coastguard Worker     const auto* codec = avcodec_find_encoder(output_format->video_codec);
172*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(codec);
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker     fStream = avformat_new_stream(fFormatCtx, codec);
175*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fStream);
176*c8dee2aaSAndroid Build Coastguard Worker     fStream->id = fFormatCtx->nb_streams-1;
177*c8dee2aaSAndroid Build Coastguard Worker     fStream->time_base = (AVRational){ 1, fps };
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx = avcodec_alloc_context3(codec);
180*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fEncoderCtx);
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx->codec_id = output_format->video_codec;
183*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx->width    = fInfo.width();
184*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx->height   = fInfo.height();
185*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx->time_base = fStream->time_base;
186*c8dee2aaSAndroid Build Coastguard Worker     fEncoderCtx->pix_fmt  = pix_fmt;
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker     /* Some formats want stream headers to be separate. */
189*c8dee2aaSAndroid Build Coastguard Worker     if (output_format->flags & AVFMT_GLOBALHEADER) {
190*c8dee2aaSAndroid Build Coastguard Worker         fEncoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
191*c8dee2aaSAndroid Build Coastguard Worker     }
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(avcodec_open2(fEncoderCtx, codec, nullptr))) {
194*c8dee2aaSAndroid Build Coastguard Worker         return false;
195*c8dee2aaSAndroid Build Coastguard Worker     }
196*c8dee2aaSAndroid Build Coastguard Worker     fFrame = av_frame_alloc();
197*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fFrame);
198*c8dee2aaSAndroid Build Coastguard Worker     fFrame->format = pix_fmt;
199*c8dee2aaSAndroid Build Coastguard Worker     fFrame->width = fEncoderCtx->width;
200*c8dee2aaSAndroid Build Coastguard Worker     fFrame->height = fEncoderCtx->height;
201*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(av_frame_get_buffer(fFrame, 32))) {
202*c8dee2aaSAndroid Build Coastguard Worker         return false;
203*c8dee2aaSAndroid Build Coastguard Worker     }
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(avcodec_parameters_from_context(fStream->codecpar, fEncoderCtx))) {
206*c8dee2aaSAndroid Build Coastguard Worker         return false;
207*c8dee2aaSAndroid Build Coastguard Worker     }
208*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(avformat_write_header(fFormatCtx, nullptr))) {
209*c8dee2aaSAndroid Build Coastguard Worker         return false;
210*c8dee2aaSAndroid Build Coastguard Worker     }
211*c8dee2aaSAndroid Build Coastguard Worker     fPacket = av_packet_alloc();
212*c8dee2aaSAndroid Build Coastguard Worker     return true;
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
216*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorFilter.h"
217*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
218*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkYUVMath.h"
219*c8dee2aaSAndroid Build Coastguard Worker 
is_valid(SkISize dim)220*c8dee2aaSAndroid Build Coastguard Worker static bool is_valid(SkISize dim) {
221*c8dee2aaSAndroid Build Coastguard Worker     if (dim.width() <= 0 || dim.height() <= 0) {
222*c8dee2aaSAndroid Build Coastguard Worker         return false;
223*c8dee2aaSAndroid Build Coastguard Worker     }
224*c8dee2aaSAndroid Build Coastguard Worker     // need the dimensions to be even for YUV 420
225*c8dee2aaSAndroid Build Coastguard Worker     return ((dim.width() | dim.height()) & 1) == 0;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker 
beginRecording(SkISize dim,int fps)228*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoEncoder::beginRecording(SkISize dim, int fps) {
229*c8dee2aaSAndroid Build Coastguard Worker     if (!is_valid(dim)) {
230*c8dee2aaSAndroid Build Coastguard Worker         return false;
231*c8dee2aaSAndroid Build Coastguard Worker     }
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker     SkAlphaType alphaType = kOpaque_SkAlphaType;
234*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkColorSpace> cs = nullptr;   // should we use this?
235*c8dee2aaSAndroid Build Coastguard Worker     fInfo = SkImageInfo::MakeN32(dim.width(), dim.height(), alphaType, cs);
236*c8dee2aaSAndroid Build Coastguard Worker     if (!this->init(fps)) {
237*c8dee2aaSAndroid Build Coastguard Worker         return false;
238*c8dee2aaSAndroid Build Coastguard Worker     }
239*c8dee2aaSAndroid Build Coastguard Worker 
240*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPTS = 0;
241*c8dee2aaSAndroid Build Coastguard Worker     fDeltaPTS = 1;
242*c8dee2aaSAndroid Build Coastguard Worker 
243*c8dee2aaSAndroid Build Coastguard Worker     const auto fmt = kN32_SkColorType == kRGBA_8888_SkColorType ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA;
244*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(sws_isSupportedInput(fmt) > 0);
245*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(sws_isSupportedOutput(AV_PIX_FMT_YUV420P) > 0);
246*c8dee2aaSAndroid Build Coastguard Worker     // sws_getCachedContext takes in either null or a previous ctx. It returns either a new ctx,
247*c8dee2aaSAndroid Build Coastguard Worker     // or the same as the input if it is compatible with the inputs. Thus we never have to
248*c8dee2aaSAndroid Build Coastguard Worker     // explicitly release our ctx until the destructor, since sws_getCachedContext takes care
249*c8dee2aaSAndroid Build Coastguard Worker     // of freeing the old as needed if/when it returns a new one.
250*c8dee2aaSAndroid Build Coastguard Worker     fSWScaleCtx = sws_getCachedContext(fSWScaleCtx,
251*c8dee2aaSAndroid Build Coastguard Worker                                        dim.width(), dim.height(), fmt,
252*c8dee2aaSAndroid Build Coastguard Worker                                        dim.width(), dim.height(), AV_PIX_FMT_YUV420P,
253*c8dee2aaSAndroid Build Coastguard Worker                                        SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
254*c8dee2aaSAndroid Build Coastguard Worker     return fSWScaleCtx != nullptr;
255*c8dee2aaSAndroid Build Coastguard Worker }
256*c8dee2aaSAndroid Build Coastguard Worker 
addFrame(const SkPixmap & pm)257*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoEncoder::addFrame(const SkPixmap& pm) {
258*c8dee2aaSAndroid Build Coastguard Worker     if (!is_valid(pm.dimensions())) {
259*c8dee2aaSAndroid Build Coastguard Worker         return false;
260*c8dee2aaSAndroid Build Coastguard Worker     }
261*c8dee2aaSAndroid Build Coastguard Worker     if (pm.info().colorType() != fInfo.colorType()) {
262*c8dee2aaSAndroid Build Coastguard Worker         return false;
263*c8dee2aaSAndroid Build Coastguard Worker     }
264*c8dee2aaSAndroid Build Coastguard Worker     /* make sure the frame data is writable */
265*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(av_frame_make_writable(fFrame))) {
266*c8dee2aaSAndroid Build Coastguard Worker         return false;
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     fFrame->pts = fCurrentPTS;
270*c8dee2aaSAndroid Build Coastguard Worker     fCurrentPTS += fDeltaPTS;
271*c8dee2aaSAndroid Build Coastguard Worker 
272*c8dee2aaSAndroid Build Coastguard Worker     const uint8_t* src[] = { (const uint8_t*)pm.addr() };
273*c8dee2aaSAndroid Build Coastguard Worker     const int strides[] = { SkToInt(pm.rowBytes()) };
274*c8dee2aaSAndroid Build Coastguard Worker     sws_scale(fSWScaleCtx, src, strides, 0, fInfo.height(), fFrame->data, fFrame->linesize);
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker     return this->sendFrame(fFrame);
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker 
sendFrame(AVFrame * frame)279*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoEncoder::sendFrame(AVFrame* frame) {
280*c8dee2aaSAndroid Build Coastguard Worker     if (check_err(avcodec_send_frame(fEncoderCtx, frame))) {
281*c8dee2aaSAndroid Build Coastguard Worker         return false;
282*c8dee2aaSAndroid Build Coastguard Worker     }
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker     int ret = 0;
285*c8dee2aaSAndroid Build Coastguard Worker     while (ret >= 0) {
286*c8dee2aaSAndroid Build Coastguard Worker         ret = avcodec_receive_packet(fEncoderCtx, fPacket);
287*c8dee2aaSAndroid Build Coastguard Worker         if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
288*c8dee2aaSAndroid Build Coastguard Worker             break;
289*c8dee2aaSAndroid Build Coastguard Worker         }
290*c8dee2aaSAndroid Build Coastguard Worker         if (check_err(ret)) {
291*c8dee2aaSAndroid Build Coastguard Worker             return false;
292*c8dee2aaSAndroid Build Coastguard Worker         }
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker         av_packet_rescale_ts(fPacket, fEncoderCtx->time_base, fStream->time_base);
295*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fPacket->stream_index == fStream->index);
296*c8dee2aaSAndroid Build Coastguard Worker 
297*c8dee2aaSAndroid Build Coastguard Worker         if (check_err(av_interleaved_write_frame(fFormatCtx, fPacket))) {
298*c8dee2aaSAndroid Build Coastguard Worker             return false;
299*c8dee2aaSAndroid Build Coastguard Worker         }
300*c8dee2aaSAndroid Build Coastguard Worker     }
301*c8dee2aaSAndroid Build Coastguard Worker     return true;
302*c8dee2aaSAndroid Build Coastguard Worker }
303*c8dee2aaSAndroid Build Coastguard Worker 
beginFrame()304*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* SkVideoEncoder::beginFrame() {
305*c8dee2aaSAndroid Build Coastguard Worker     if (!fSurface) {
306*c8dee2aaSAndroid Build Coastguard Worker         fSurface = SkSurfaces::Raster(fInfo);
307*c8dee2aaSAndroid Build Coastguard Worker         if (!fSurface) {
308*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
309*c8dee2aaSAndroid Build Coastguard Worker         }
310*c8dee2aaSAndroid Build Coastguard Worker     }
311*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas* canvas = fSurface->getCanvas();
312*c8dee2aaSAndroid Build Coastguard Worker     canvas->restoreToCount(1);
313*c8dee2aaSAndroid Build Coastguard Worker     canvas->clear(0);
314*c8dee2aaSAndroid Build Coastguard Worker     return canvas;
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker 
endFrame()317*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoEncoder::endFrame() {
318*c8dee2aaSAndroid Build Coastguard Worker     if (!fSurface) {
319*c8dee2aaSAndroid Build Coastguard Worker         return false;
320*c8dee2aaSAndroid Build Coastguard Worker     }
321*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pm;
322*c8dee2aaSAndroid Build Coastguard Worker     return fSurface->peekPixels(&pm) && this->addFrame(pm);
323*c8dee2aaSAndroid Build Coastguard Worker }
324*c8dee2aaSAndroid Build Coastguard Worker 
endRecording()325*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkVideoEncoder::endRecording() {
326*c8dee2aaSAndroid Build Coastguard Worker     if (!fFormatCtx) {
327*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
328*c8dee2aaSAndroid Build Coastguard Worker     }
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker     this->sendFrame(nullptr);
331*c8dee2aaSAndroid Build Coastguard Worker     av_write_trailer(fFormatCtx);
332*c8dee2aaSAndroid Build Coastguard Worker 
333*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> data = fWStream->detachAsData();
334*c8dee2aaSAndroid Build Coastguard Worker     this->reset();
335*c8dee2aaSAndroid Build Coastguard Worker     return data;
336*c8dee2aaSAndroid Build Coastguard Worker }
337