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:
parent
fec9b902a6
commit
ede7e99465
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user