add simpler addFrame api (helps with threaded producers)

Change-Id: I458dc2fb59aa32e084b0b03945afd74ff5ee42ae
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217861
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
Auto-Submit: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2019-06-03 11:04:20 -04:00 committed by Skia Commit-Bot
parent fec9b902a6
commit ede7e99465
4 changed files with 101 additions and 37 deletions

View File

@ -110,7 +110,9 @@ static int64_t sk_seek_packet(void* ctx, int64_t pos, int whence) {
return pos;
}
SkVideoEncoder::SkVideoEncoder() {}
SkVideoEncoder::SkVideoEncoder() {
fInfo = SkImageInfo::MakeUnknown();
}
SkVideoEncoder::~SkVideoEncoder() {
this->reset();
@ -137,14 +139,11 @@ void SkVideoEncoder::reset() {
av_packet_free(&fPacket);
fPacket = nullptr;
fSurface.reset();
fWStream.reset();
}
bool SkVideoEncoder::init(const SkImageInfo& info, int fps) {
if ((info.width() & 1) || (info.height() & 1)) {
SkDebugf("dimensinos must be even (it appears)\n");
return false;
}
bool SkVideoEncoder::init(int fps) {
// only support this for now
AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
@ -182,8 +181,8 @@ bool SkVideoEncoder::init(const SkImageInfo& info, int fps) {
SkASSERT(fEncoderCtx);
fEncoderCtx->codec_id = output_format->video_codec;
fEncoderCtx->width = info.width();
fEncoderCtx->height = info.height();
fEncoderCtx->width = fInfo.width();
fEncoderCtx->height = fInfo.height();
fEncoderCtx->time_base = fStream->time_base;
fEncoderCtx->pix_fmt = pix_fmt;
@ -219,18 +218,26 @@ bool SkVideoEncoder::init(const SkImageInfo& info, int fps) {
#include "include/core/SkColorFilter.h"
#include "src/core/SkYUVMath.h"
bool SkVideoEncoder::beginRecording(SkISize dim, int fps) {
static bool is_valid(SkISize dim) {
if (dim.width() <= 0 || dim.height() <= 0) {
return false;
}
// need the dimensions to be even for YUV 420
return ((dim.width() | dim.height()) & 1) == 0;
}
bool SkVideoEncoder::beginRecording(SkISize dim, int fps) {
if (!is_valid(dim)) {
return false;
}
// need opaque and bgra to efficiently use libyuv / convert-to-yuv-420
auto info = SkImageInfo::Make(dim.width(), dim.height(),
kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
if (!this->init(info, fps)) {
SkAlphaType alphaType = kOpaque_SkAlphaType;
sk_sp<SkColorSpace> cs = nullptr; // should we use this?
fInfo = SkImageInfo::Make(dim.width(), dim.height(), kRGBA_8888_SkColorType, alphaType, cs);
if (!this->init(fps)) {
return false;
}
fSurface = SkSurface::MakeRaster(info);
fCurrentPTS = 0;
fDeltaPTS = 1;
@ -248,14 +255,23 @@ bool SkVideoEncoder::beginRecording(SkISize dim, int fps) {
return fSWScaleCtx != nullptr;
}
SkCanvas* SkVideoEncoder::beginFrame() {
if (!fSurface) {
return nullptr;
bool SkVideoEncoder::addFrame(const SkPixmap& pm) {
if (!is_valid(pm.dimensions())) {
return false;
}
SkCanvas* canvas = fSurface->getCanvas();
canvas->restoreToCount(1);
canvas->clear(0);
return canvas;
/* make sure the frame data is writable */
if (check_err(av_frame_make_writable(fFrame))) {
return false;
}
fFrame->pts = fCurrentPTS;
fCurrentPTS += fDeltaPTS;
const uint8_t* src[] = { (const uint8_t*)pm.addr() };
const int strides[] = { SkToInt(pm.rowBytes()) };
sws_scale(fSWScaleCtx, src, strides, 0, fInfo.height(), fFrame->data, fFrame->linesize);
return this->sendFrame(fFrame);
}
bool SkVideoEncoder::sendFrame(AVFrame* frame) {
@ -283,22 +299,25 @@ bool SkVideoEncoder::sendFrame(AVFrame* frame) {
return true;
}
SkCanvas* SkVideoEncoder::beginFrame() {
if (!fSurface) {
fSurface = SkSurface::MakeRaster(fInfo);
if (!fSurface) {
return nullptr;
}
}
SkCanvas* canvas = fSurface->getCanvas();
canvas->restoreToCount(1);
canvas->clear(0);
return canvas;
}
bool SkVideoEncoder::endFrame() {
/* make sure the frame data is writable */
if (check_err(av_frame_make_writable(fFrame))) {
if (!fSurface) {
return false;
}
fFrame->pts = fCurrentPTS;
fCurrentPTS += fDeltaPTS;
SkPixmap pm;
SkAssertResult(fSurface->peekPixels(&pm));
const uint8_t* src[] = { (const uint8_t*)pm.addr() };
const int strides[] = { SkToInt(pm.rowBytes()) };
sws_scale(fSWScaleCtx, src, strides, 0, fSurface->height(), fFrame->data, fFrame->linesize);
return this->sendFrame(fFrame);
return fSurface->peekPixels(&pm) && this->addFrame(pm);
}
sk_sp<SkData> SkVideoEncoder::endRecording() {

View File

@ -27,16 +27,45 @@ public:
SkVideoEncoder();
~SkVideoEncoder();
/**
* Begina a new recording. Balance this (after adding all of your frames) with a call
* to endRecording().
*/
bool beginRecording(SkISize, int fps);
/**
* Returns the preferred ImageInfo for this recording. Only valid if beginRecording() has
* been called, and endRecording has not been called (yet).
*/
SkImageInfo preferredInfo() const { return fInfo; }
/**
* If you have your own pixmap, call addFrame(). Note this may fail if it uses an unsupported
* ColorType or AlphaType, or the dimensions don't match those set in beginRecording.
* For best results, use the SkImageInfo returned by preferredInfo().
*/
bool addFrame(const SkPixmap&);
/**
* As an alternative to calling addFrame(), you can call beginFrame/endFrame, and the encoder
* will manage allocating a surface/canvas for you.
*
* SkCanvas* canvas = encoder.beginFrame();
* // your drawing code here, drawing into canvas
* encoder.endFrame();
*/
SkCanvas* beginFrame();
bool endFrame();
/**
* Call this after having added all of your frames. After calling this, no more frames can
* be added to this recording. To record a new video, call beginRecording().
*/
sk_sp<SkData> endRecording();
private:
void reset();
bool init(const SkImageInfo&, int fps);
bool init(int fps);
bool sendFrame(AVFrame*); // frame can be null
double computeTimeStamp(const AVFrame*) const;
@ -49,10 +78,13 @@ private:
AVFrame* fFrame = nullptr;
AVPacket* fPacket = nullptr;
sk_sp<SkSurface> fSurface;
SkImageInfo fInfo; // only defined between beginRecording() and endRecording()
std::unique_ptr<SkRandomAccessWStream> fWStream;
int64_t fCurrentPTS, fDeltaPTS;
// Lazily allocated, iff the client has called beginFrame() for a given recording session.
sk_sp<SkSurface> fSurface;
int64_t fCurrentPTS, fDeltaPTS;
};
#endif

View File

@ -149,6 +149,11 @@ public:
*/
int height() const { return fInfo.height(); }
/**
* Return the dimensions of the pixmap (from its ImageInfo)
*/
SkISize dimensions() const { return fInfo.dimensions(); }
/** Returns SkColorType, one of:
kUnknown_SkColorType, kAlpha_8_SkColorType, kRGB_565_SkColorType,
kARGB_4444_SkColorType, kRGBA_8888_SkColorType, kRGB_888x_SkColorType,

View File

@ -7,6 +7,8 @@
#include "modules/skottie/include/Skottie.h"
#include "modules/skottie/utils/SkottieUtils.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkSurface.h"
#include "include/core/SkStream.h"
#include "include/core/SkTime.h"
#include "src/utils/SkOSPath.h"
@ -79,14 +81,20 @@ int main(int argc, char** argv) {
encoder.beginRecording(dim, fps);
auto surf = SkSurface::MakeRaster(encoder.preferredInfo());
for (int i = 0; i <= frames; ++i) {
double ts = i * 1.0 / fps;
if (FLAGS_verbose) {
SkDebugf("rendering frame %d ts %g\n", i, ts);
}
animation->seek(i * 1.0 / frames); // normalized time
animation->render(encoder.beginFrame());
encoder.endFrame();
surf->getCanvas()->clear(SK_ColorWHITE);
animation->render(surf->getCanvas());
SkPixmap pm;
SkAssertResult(surf->peekPixels(&pm));
encoder.addFrame(pm);
}
if (FLAGS_output.count() == 0) {