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