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