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/SkVideoDecoder.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkYUVAPixmaps.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkImageGanesh.h"
15*c8dee2aaSAndroid Build Coastguard Worker
get_yuvspace(AVColorSpace space)16*c8dee2aaSAndroid Build Coastguard Worker static SkYUVColorSpace get_yuvspace(AVColorSpace space) {
17*c8dee2aaSAndroid Build Coastguard Worker // this is pretty incomplete -- TODO: look to convert more AVColorSpaces
18*c8dee2aaSAndroid Build Coastguard Worker switch (space) {
19*c8dee2aaSAndroid Build Coastguard Worker case AVCOL_SPC_RGB: return kIdentity_SkYUVColorSpace;
20*c8dee2aaSAndroid Build Coastguard Worker case AVCOL_SPC_BT709: return kRec709_SkYUVColorSpace;
21*c8dee2aaSAndroid Build Coastguard Worker case AVCOL_SPC_SMPTE170M:
22*c8dee2aaSAndroid Build Coastguard Worker case AVCOL_SPC_SMPTE240M:
23*c8dee2aaSAndroid Build Coastguard Worker case AVCOL_SPC_BT470BG: return kRec601_SkYUVColorSpace;
24*c8dee2aaSAndroid Build Coastguard Worker default: break;
25*c8dee2aaSAndroid Build Coastguard Worker }
26*c8dee2aaSAndroid Build Coastguard Worker return kRec709_SkYUVColorSpace;
27*c8dee2aaSAndroid Build Coastguard Worker }
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker struct av_transfer_characteristics {
30*c8dee2aaSAndroid Build Coastguard Worker // if x < beta delta * x
31*c8dee2aaSAndroid Build Coastguard Worker // else alpha * (x^gama)
32*c8dee2aaSAndroid Build Coastguard Worker float alpha, beta, gamma, delta;
33*c8dee2aaSAndroid Build Coastguard Worker };
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker // Tables extracted from vf_colorspace.c
36*c8dee2aaSAndroid Build Coastguard Worker
37*c8dee2aaSAndroid Build Coastguard Worker const av_transfer_characteristics gTransfer[AVCOL_TRC_NB] = {
38*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_BT709] = { 1.099, 0.018, 0.45, 4.5 },
39*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_GAMMA22] = { 1.0, 0.0, 1.0 / 2.2, 0.0 },
40*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_GAMMA28] = { 1.0, 0.0, 1.0 / 2.8, 0.0 },
41*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_SMPTE170M] = { 1.099, 0.018, 0.45, 4.5 },
42*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_SMPTE240M] = { 1.1115, 0.0228, 0.45, 4.0 },
43*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_IEC61966_2_1] = { 1.055, 0.0031308, 1.0 / 2.4, 12.92 },
44*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_IEC61966_2_4] = { 1.099, 0.018, 0.45, 4.5 },
45*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_BT2020_10] = { 1.099, 0.018, 0.45, 4.5 },
46*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_TRC_BT2020_12] = { 1.0993, 0.0181, 0.45, 4.5 },
47*c8dee2aaSAndroid Build Coastguard Worker };
48*c8dee2aaSAndroid Build Coastguard Worker
compute_transfer(AVColorTransferCharacteristic t)49*c8dee2aaSAndroid Build Coastguard Worker static skcms_TransferFunction compute_transfer(AVColorTransferCharacteristic t) {
50*c8dee2aaSAndroid Build Coastguard Worker const av_transfer_characteristics* av = &gTransfer[AVCOL_TRC_BT709];
51*c8dee2aaSAndroid Build Coastguard Worker if ((unsigned)t < AVCOL_TRC_NB) {
52*c8dee2aaSAndroid Build Coastguard Worker av = &gTransfer[t];
53*c8dee2aaSAndroid Build Coastguard Worker }
54*c8dee2aaSAndroid Build Coastguard Worker if (av->alpha == 0) {
55*c8dee2aaSAndroid Build Coastguard Worker av = &gTransfer[AVCOL_TRC_BT709];
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker skcms_TransferFunction linear_to_encoded = {
59*c8dee2aaSAndroid Build Coastguard Worker av->gamma, std::pow(av->alpha, 1/av->gamma), 0, av->delta, av->beta, 1 - av->alpha, 0,
60*c8dee2aaSAndroid Build Coastguard Worker };
61*c8dee2aaSAndroid Build Coastguard Worker skcms_TransferFunction encoded_to_linear;
62*c8dee2aaSAndroid Build Coastguard Worker bool success = skcms_TransferFunction_invert(&linear_to_encoded, &encoded_to_linear);
63*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(success);
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker return encoded_to_linear;
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker enum Whitepoint {
69*c8dee2aaSAndroid Build Coastguard Worker WP_D65,
70*c8dee2aaSAndroid Build Coastguard Worker WP_C,
71*c8dee2aaSAndroid Build Coastguard Worker WP_DCI,
72*c8dee2aaSAndroid Build Coastguard Worker WP_E,
73*c8dee2aaSAndroid Build Coastguard Worker WP_NB,
74*c8dee2aaSAndroid Build Coastguard Worker };
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker const SkPoint gWP[WP_NB] = {
77*c8dee2aaSAndroid Build Coastguard Worker [WP_D65] = { 0.3127f, 0.3290f },
78*c8dee2aaSAndroid Build Coastguard Worker [WP_C] = { 0.3100f, 0.3160f },
79*c8dee2aaSAndroid Build Coastguard Worker [WP_DCI] = { 0.3140f, 0.3510f },
80*c8dee2aaSAndroid Build Coastguard Worker [WP_E] = { 1/3.0f, 1/3.0f },
81*c8dee2aaSAndroid Build Coastguard Worker };
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker #define ExpandWP(index) gWP[index].fX, gWP[index].fY
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker const SkColorSpacePrimaries gPrimaries[AVCOL_PRI_NB] = {
86*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_BT709] = { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f, ExpandWP(WP_D65) },
87*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_BT470M] = { 0.670f, 0.330f, 0.210f, 0.710f, 0.140f, 0.080f, ExpandWP(WP_C) },
88*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_BT470BG] = { 0.640f, 0.330f, 0.290f, 0.600f, 0.150f, 0.060f, ExpandWP(WP_D65) },
89*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_SMPTE170M] = { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, ExpandWP(WP_D65) },
90*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_SMPTE240M] = { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, ExpandWP(WP_D65) },
91*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_SMPTE428] = { 0.735f, 0.265f, 0.274f, 0.718f, 0.167f, 0.009f, ExpandWP(WP_E) },
92*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_SMPTE431] = { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, ExpandWP(WP_DCI) },
93*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_SMPTE432] = { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, ExpandWP(WP_D65) },
94*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_FILM] = { 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, ExpandWP(WP_C) },
95*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_BT2020] = { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, ExpandWP(WP_D65) },
96*c8dee2aaSAndroid Build Coastguard Worker [AVCOL_PRI_JEDEC_P22] = { 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, ExpandWP(WP_D65) },
97*c8dee2aaSAndroid Build Coastguard Worker };
98*c8dee2aaSAndroid Build Coastguard Worker
make_colorspace(AVColorPrimaries primaries,AVColorTransferCharacteristic transfer)99*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> make_colorspace(AVColorPrimaries primaries,
100*c8dee2aaSAndroid Build Coastguard Worker AVColorTransferCharacteristic transfer) {
101*c8dee2aaSAndroid Build Coastguard Worker if (primaries == AVCOL_PRI_BT709 && transfer == AVCOL_TRC_BT709) {
102*c8dee2aaSAndroid Build Coastguard Worker return SkColorSpace::MakeSRGB();
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker const SkColorSpacePrimaries* p = &gPrimaries[0];
106*c8dee2aaSAndroid Build Coastguard Worker if ((unsigned)primaries < (unsigned)AVCOL_PRI_NB) {
107*c8dee2aaSAndroid Build Coastguard Worker p = &gPrimaries[primaries];
108*c8dee2aaSAndroid Build Coastguard Worker }
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker skcms_Matrix3x3 matrix;
111*c8dee2aaSAndroid Build Coastguard Worker p->toXYZD50(&matrix);
112*c8dee2aaSAndroid Build Coastguard Worker return SkColorSpace::MakeRGB(compute_transfer(transfer), matrix);
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker // returns true on error (and may dump the particular error message)
check_err(int err,const int silentList[]=nullptr)116*c8dee2aaSAndroid Build Coastguard Worker static bool check_err(int err, const int silentList[] = nullptr) {
117*c8dee2aaSAndroid Build Coastguard Worker if (err >= 0) {
118*c8dee2aaSAndroid Build Coastguard Worker return false;
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker if (silentList) {
122*c8dee2aaSAndroid Build Coastguard Worker for (; *silentList; ++silentList) {
123*c8dee2aaSAndroid Build Coastguard Worker if (*silentList == err) {
124*c8dee2aaSAndroid Build Coastguard Worker return true; // we still report the error, but we don't printf
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker }
127*c8dee2aaSAndroid Build Coastguard Worker }
128*c8dee2aaSAndroid Build Coastguard Worker
129*c8dee2aaSAndroid Build Coastguard Worker char errbuf[128];
130*c8dee2aaSAndroid Build Coastguard Worker const char *errbuf_ptr = errbuf;
131*c8dee2aaSAndroid Build Coastguard Worker
132*c8dee2aaSAndroid Build Coastguard Worker if (av_strerror(err, errbuf, sizeof(errbuf)) < 0) {
133*c8dee2aaSAndroid Build Coastguard Worker errbuf_ptr = strerror(AVUNERROR(err));
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("%s\n", errbuf_ptr);
136*c8dee2aaSAndroid Build Coastguard Worker return true;
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
skstream_read_packet(void * ctx,uint8_t * dstBuffer,int dstSize)139*c8dee2aaSAndroid Build Coastguard Worker static int skstream_read_packet(void* ctx, uint8_t* dstBuffer, int dstSize) {
140*c8dee2aaSAndroid Build Coastguard Worker SkStream* stream = (SkStream*)ctx;
141*c8dee2aaSAndroid Build Coastguard Worker int result = (int)stream->read(dstBuffer, dstSize);
142*c8dee2aaSAndroid Build Coastguard Worker if (result == 0) {
143*c8dee2aaSAndroid Build Coastguard Worker result = AVERROR_EOF;
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker return result;
146*c8dee2aaSAndroid Build Coastguard Worker }
147*c8dee2aaSAndroid Build Coastguard Worker
skstream_seek_packet(void * ctx,int64_t pos,int whence)148*c8dee2aaSAndroid Build Coastguard Worker static int64_t skstream_seek_packet(void* ctx, int64_t pos, int whence) {
149*c8dee2aaSAndroid Build Coastguard Worker SkStream* stream = (SkStream*)ctx;
150*c8dee2aaSAndroid Build Coastguard Worker switch (whence) {
151*c8dee2aaSAndroid Build Coastguard Worker case SEEK_SET:
152*c8dee2aaSAndroid Build Coastguard Worker break;
153*c8dee2aaSAndroid Build Coastguard Worker case SEEK_CUR:
154*c8dee2aaSAndroid Build Coastguard Worker pos = (int64_t)stream->getPosition() + pos;
155*c8dee2aaSAndroid Build Coastguard Worker break;
156*c8dee2aaSAndroid Build Coastguard Worker case SEEK_END:
157*c8dee2aaSAndroid Build Coastguard Worker pos = (int64_t)stream->getLength() + pos;
158*c8dee2aaSAndroid Build Coastguard Worker break;
159*c8dee2aaSAndroid Build Coastguard Worker default:
160*c8dee2aaSAndroid Build Coastguard Worker return -1;
161*c8dee2aaSAndroid Build Coastguard Worker }
162*c8dee2aaSAndroid Build Coastguard Worker return stream->seek(SkToSizeT(pos)) ? pos : -1;
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
make_yuv_420(GrRecordingContext * rContext,int w,int h,uint8_t * const data[],int const strides[],SkYUVColorSpace yuvSpace,sk_sp<SkColorSpace> cs)165*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_yuv_420(GrRecordingContext* rContext,
166*c8dee2aaSAndroid Build Coastguard Worker int w, int h,
167*c8dee2aaSAndroid Build Coastguard Worker uint8_t* const data[],
168*c8dee2aaSAndroid Build Coastguard Worker int const strides[],
169*c8dee2aaSAndroid Build Coastguard Worker SkYUVColorSpace yuvSpace,
170*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> cs) {
171*c8dee2aaSAndroid Build Coastguard Worker SkYUVAInfo yuvaInfo({w, h},
172*c8dee2aaSAndroid Build Coastguard Worker SkYUVAInfo::PlaneConfig::kY_U_V,
173*c8dee2aaSAndroid Build Coastguard Worker SkYUVAInfo::Subsampling::k420,
174*c8dee2aaSAndroid Build Coastguard Worker yuvSpace);
175*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pixmaps[3];
176*c8dee2aaSAndroid Build Coastguard Worker pixmaps[0].reset(SkImageInfo::MakeA8(w, h), data[0], strides[0]);
177*c8dee2aaSAndroid Build Coastguard Worker w = (w + 1)/2;
178*c8dee2aaSAndroid Build Coastguard Worker h = (h + 1)/2;
179*c8dee2aaSAndroid Build Coastguard Worker pixmaps[1].reset(SkImageInfo::MakeA8(w, h), data[1], strides[1]);
180*c8dee2aaSAndroid Build Coastguard Worker pixmaps[2].reset(SkImageInfo::MakeA8(w, h), data[2], strides[2]);
181*c8dee2aaSAndroid Build Coastguard Worker auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, pixmaps);
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker return SkImages::TextureFromYUVAPixmaps(
184*c8dee2aaSAndroid Build Coastguard Worker rContext, yuvaPixmaps, skgpu::Mipmapped::kNo, false, std::move(cs));
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard Worker // Init with illegal values, so our first compare will fail, forcing us to compute
188*c8dee2aaSAndroid Build Coastguard Worker // the skcolorspace.
ConvertedColorSpace()189*c8dee2aaSAndroid Build Coastguard Worker SkVideoDecoder::ConvertedColorSpace::ConvertedColorSpace()
190*c8dee2aaSAndroid Build Coastguard Worker : fPrimaries(AVCOL_PRI_NB), fTransfer(AVCOL_TRC_NB)
191*c8dee2aaSAndroid Build Coastguard Worker {}
192*c8dee2aaSAndroid Build Coastguard Worker
update(AVColorPrimaries primaries,AVColorTransferCharacteristic transfer)193*c8dee2aaSAndroid Build Coastguard Worker void SkVideoDecoder::ConvertedColorSpace::update(AVColorPrimaries primaries,
194*c8dee2aaSAndroid Build Coastguard Worker AVColorTransferCharacteristic transfer) {
195*c8dee2aaSAndroid Build Coastguard Worker if (fPrimaries != primaries || fTransfer != transfer) {
196*c8dee2aaSAndroid Build Coastguard Worker fPrimaries = primaries;
197*c8dee2aaSAndroid Build Coastguard Worker fTransfer = transfer;
198*c8dee2aaSAndroid Build Coastguard Worker fCS = make_colorspace(primaries, transfer);
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker }
201*c8dee2aaSAndroid Build Coastguard Worker
computeTimeStamp(const AVFrame * frame) const202*c8dee2aaSAndroid Build Coastguard Worker double SkVideoDecoder::computeTimeStamp(const AVFrame* frame) const {
203*c8dee2aaSAndroid Build Coastguard Worker AVRational base = fFormatCtx->streams[fStreamIndex]->time_base;
204*c8dee2aaSAndroid Build Coastguard Worker return 1.0 * frame->pts * base.num / base.den;
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker
convertFrame(const AVFrame * frame)207*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> SkVideoDecoder::convertFrame(const AVFrame* frame) {
208*c8dee2aaSAndroid Build Coastguard Worker auto yuv_space = get_yuvspace(frame->colorspace);
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker // we have a 1-entry cache for converting colorspaces
211*c8dee2aaSAndroid Build Coastguard Worker fCSCache.update(frame->color_primaries, frame->color_trc);
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker // Are these always true? If so, we don't need to check our "cache" on each frame...
214*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDecoderCtx->colorspace == frame->colorspace);
215*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDecoderCtx->color_primaries == frame->color_primaries);
216*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDecoderCtx->color_trc == frame->color_trc);
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker // Is this always true? If so, we might take advantage of it, knowing up-front if we support
219*c8dee2aaSAndroid Build Coastguard Worker // the format for the whole stream, in which case we might have to ask ffmpeg to convert it
220*c8dee2aaSAndroid Build Coastguard Worker // to something more reasonable (for us)...
221*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDecoderCtx->pix_fmt == frame->format);
222*c8dee2aaSAndroid Build Coastguard Worker
223*c8dee2aaSAndroid Build Coastguard Worker switch (frame->format) {
224*c8dee2aaSAndroid Build Coastguard Worker case AV_PIX_FMT_YUV420P:
225*c8dee2aaSAndroid Build Coastguard Worker if (auto image = make_yuv_420(fRecordingContext, frame->width, frame->height,
226*c8dee2aaSAndroid Build Coastguard Worker frame->data, frame->linesize, yuv_space, fCSCache.fCS)) {
227*c8dee2aaSAndroid Build Coastguard Worker return image;
228*c8dee2aaSAndroid Build Coastguard Worker }
229*c8dee2aaSAndroid Build Coastguard Worker break;
230*c8dee2aaSAndroid Build Coastguard Worker default:
231*c8dee2aaSAndroid Build Coastguard Worker break;
232*c8dee2aaSAndroid Build Coastguard Worker }
233*c8dee2aaSAndroid Build Coastguard Worker
234*c8dee2aaSAndroid Build Coastguard Worker // General N32 fallback.
235*c8dee2aaSAndroid Build Coastguard Worker const auto info = SkImageInfo::MakeN32(frame->width, frame->height,
236*c8dee2aaSAndroid Build Coastguard Worker SkAlphaType::kOpaque_SkAlphaType);
237*c8dee2aaSAndroid Build Coastguard Worker
238*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
239*c8dee2aaSAndroid Build Coastguard Worker bm.allocPixels(info, info.minRowBytes());
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker constexpr auto fmt = SK_PMCOLOR_BYTE_ORDER(R,G,B,A) ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA;
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker // TODO: should we cache these?
244*c8dee2aaSAndroid Build Coastguard Worker auto* ctx = sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format,
245*c8dee2aaSAndroid Build Coastguard Worker info.width(), info.height(), fmt,
246*c8dee2aaSAndroid Build Coastguard Worker SWS_BILINEAR, nullptr, nullptr, nullptr);
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker uint8_t* dst[] = { (uint8_t*)bm.pixmap().writable_addr() };
249*c8dee2aaSAndroid Build Coastguard Worker int dst_stride[] = { SkToInt(bm.pixmap().rowBytes()) };
250*c8dee2aaSAndroid Build Coastguard Worker
251*c8dee2aaSAndroid Build Coastguard Worker sws_scale(ctx, frame->data, frame->linesize, 0, frame->height, dst, dst_stride);
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker sws_freeContext(ctx);
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker bm.setImmutable();
256*c8dee2aaSAndroid Build Coastguard Worker
257*c8dee2aaSAndroid Build Coastguard Worker return SkImages::RasterFromBitmap(bm);
258*c8dee2aaSAndroid Build Coastguard Worker }
259*c8dee2aaSAndroid Build Coastguard Worker
nextImage(double * timeStamp)260*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> SkVideoDecoder::nextImage(double* timeStamp) {
261*c8dee2aaSAndroid Build Coastguard Worker double defaultTimeStampStorage = 0;
262*c8dee2aaSAndroid Build Coastguard Worker if (!timeStamp) {
263*c8dee2aaSAndroid Build Coastguard Worker timeStamp = &defaultTimeStampStorage;
264*c8dee2aaSAndroid Build Coastguard Worker }
265*c8dee2aaSAndroid Build Coastguard Worker
266*c8dee2aaSAndroid Build Coastguard Worker if (fFormatCtx == nullptr) {
267*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
268*c8dee2aaSAndroid Build Coastguard Worker }
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker if (fMode == kProcessing_Mode) {
271*c8dee2aaSAndroid Build Coastguard Worker // We sit in a loop, waiting for the codec to have received enough data (packets)
272*c8dee2aaSAndroid Build Coastguard Worker // to have at least one frame available.
273*c8dee2aaSAndroid Build Coastguard Worker // Treat non-zero return as EOF (or error, which we will decide is also EOF)
274*c8dee2aaSAndroid Build Coastguard Worker while (!av_read_frame(fFormatCtx, &fPacket)) {
275*c8dee2aaSAndroid Build Coastguard Worker if (fPacket.stream_index != fStreamIndex) {
276*c8dee2aaSAndroid Build Coastguard Worker // got a packet for a stream other than our (video) stream, so continue
277*c8dee2aaSAndroid Build Coastguard Worker continue;
278*c8dee2aaSAndroid Build Coastguard Worker }
279*c8dee2aaSAndroid Build Coastguard Worker
280*c8dee2aaSAndroid Build Coastguard Worker int ret = avcodec_send_packet(fDecoderCtx, &fPacket);
281*c8dee2aaSAndroid Build Coastguard Worker if (ret == AVERROR(EAGAIN)) {
282*c8dee2aaSAndroid Build Coastguard Worker // may signal that we have plenty already, encouraging us to call receive_frame
283*c8dee2aaSAndroid Build Coastguard Worker // so we don't treat this as an error.
284*c8dee2aaSAndroid Build Coastguard Worker ret = 0;
285*c8dee2aaSAndroid Build Coastguard Worker }
286*c8dee2aaSAndroid Build Coastguard Worker (void)check_err(ret); // we try to continue if there was an error
287*c8dee2aaSAndroid Build Coastguard Worker
288*c8dee2aaSAndroid Build Coastguard Worker int silentList[] = {
289*c8dee2aaSAndroid Build Coastguard Worker -35, // Resource temporarily unavailable (need more packets)
290*c8dee2aaSAndroid Build Coastguard Worker 0,
291*c8dee2aaSAndroid Build Coastguard Worker };
292*c8dee2aaSAndroid Build Coastguard Worker if (check_err(avcodec_receive_frame(fDecoderCtx, fFrame), silentList)) {
293*c8dee2aaSAndroid Build Coastguard Worker // this may be just "needs more input", so we try to continue
294*c8dee2aaSAndroid Build Coastguard Worker } else {
295*c8dee2aaSAndroid Build Coastguard Worker *timeStamp = this->computeTimeStamp(fFrame);
296*c8dee2aaSAndroid Build Coastguard Worker return this->convertFrame(fFrame);
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker }
299*c8dee2aaSAndroid Build Coastguard Worker
300*c8dee2aaSAndroid Build Coastguard Worker fMode = kDraining_Mode;
301*c8dee2aaSAndroid Build Coastguard Worker (void)avcodec_send_packet(fDecoderCtx, nullptr); // signal to start draining
302*c8dee2aaSAndroid Build Coastguard Worker }
303*c8dee2aaSAndroid Build Coastguard Worker if (fMode == kDraining_Mode) {
304*c8dee2aaSAndroid Build Coastguard Worker if (avcodec_receive_frame(fDecoderCtx, fFrame) >= 0) {
305*c8dee2aaSAndroid Build Coastguard Worker *timeStamp = this->computeTimeStamp(fFrame);
306*c8dee2aaSAndroid Build Coastguard Worker return this->convertFrame(fFrame);
307*c8dee2aaSAndroid Build Coastguard Worker }
308*c8dee2aaSAndroid Build Coastguard Worker // else we decide we're done
309*c8dee2aaSAndroid Build Coastguard Worker fMode = kDone_Mode;
310*c8dee2aaSAndroid Build Coastguard Worker }
311*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
312*c8dee2aaSAndroid Build Coastguard Worker }
313*c8dee2aaSAndroid Build Coastguard Worker
SkVideoDecoder(GrRecordingContext * rContext)314*c8dee2aaSAndroid Build Coastguard Worker SkVideoDecoder::SkVideoDecoder(GrRecordingContext* rContext) : fRecordingContext(rContext) {}
315*c8dee2aaSAndroid Build Coastguard Worker
~SkVideoDecoder()316*c8dee2aaSAndroid Build Coastguard Worker SkVideoDecoder::~SkVideoDecoder() {
317*c8dee2aaSAndroid Build Coastguard Worker this->reset();
318*c8dee2aaSAndroid Build Coastguard Worker }
319*c8dee2aaSAndroid Build Coastguard Worker
reset()320*c8dee2aaSAndroid Build Coastguard Worker void SkVideoDecoder::reset() {
321*c8dee2aaSAndroid Build Coastguard Worker if (fFrame) {
322*c8dee2aaSAndroid Build Coastguard Worker av_frame_free(&fFrame);
323*c8dee2aaSAndroid Build Coastguard Worker fFrame = nullptr;
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker if (fDecoderCtx) {
326*c8dee2aaSAndroid Build Coastguard Worker avcodec_free_context(&fDecoderCtx);
327*c8dee2aaSAndroid Build Coastguard Worker fDecoderCtx = nullptr;
328*c8dee2aaSAndroid Build Coastguard Worker }
329*c8dee2aaSAndroid Build Coastguard Worker if (fFormatCtx) {
330*c8dee2aaSAndroid Build Coastguard Worker avformat_close_input(&fFormatCtx);
331*c8dee2aaSAndroid Build Coastguard Worker fFormatCtx = nullptr;
332*c8dee2aaSAndroid Build Coastguard Worker }
333*c8dee2aaSAndroid Build Coastguard Worker if (fStreamCtx) {
334*c8dee2aaSAndroid Build Coastguard Worker av_freep(&fStreamCtx->buffer);
335*c8dee2aaSAndroid Build Coastguard Worker avio_context_free(&fStreamCtx);
336*c8dee2aaSAndroid Build Coastguard Worker fStreamCtx = nullptr;
337*c8dee2aaSAndroid Build Coastguard Worker }
338*c8dee2aaSAndroid Build Coastguard Worker
339*c8dee2aaSAndroid Build Coastguard Worker fStream.reset(nullptr);
340*c8dee2aaSAndroid Build Coastguard Worker fStreamIndex = -1;
341*c8dee2aaSAndroid Build Coastguard Worker fMode = kDone_Mode;
342*c8dee2aaSAndroid Build Coastguard Worker }
343*c8dee2aaSAndroid Build Coastguard Worker
loadStream(std::unique_ptr<SkStream> stream)344*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoDecoder::loadStream(std::unique_ptr<SkStream> stream) {
345*c8dee2aaSAndroid Build Coastguard Worker this->reset();
346*c8dee2aaSAndroid Build Coastguard Worker if (!stream) {
347*c8dee2aaSAndroid Build Coastguard Worker return false;
348*c8dee2aaSAndroid Build Coastguard Worker }
349*c8dee2aaSAndroid Build Coastguard Worker
350*c8dee2aaSAndroid Build Coastguard Worker int bufferSize = 4 * 1024;
351*c8dee2aaSAndroid Build Coastguard Worker uint8_t* buffer = (uint8_t*)av_malloc(bufferSize);
352*c8dee2aaSAndroid Build Coastguard Worker if (!buffer) {
353*c8dee2aaSAndroid Build Coastguard Worker return false;
354*c8dee2aaSAndroid Build Coastguard Worker }
355*c8dee2aaSAndroid Build Coastguard Worker
356*c8dee2aaSAndroid Build Coastguard Worker fStream = std::move(stream);
357*c8dee2aaSAndroid Build Coastguard Worker fStreamCtx = avio_alloc_context(buffer, bufferSize, 0, fStream.get(),
358*c8dee2aaSAndroid Build Coastguard Worker skstream_read_packet, nullptr, skstream_seek_packet);
359*c8dee2aaSAndroid Build Coastguard Worker if (!fStreamCtx) {
360*c8dee2aaSAndroid Build Coastguard Worker av_freep(buffer);
361*c8dee2aaSAndroid Build Coastguard Worker this->reset();
362*c8dee2aaSAndroid Build Coastguard Worker return false;
363*c8dee2aaSAndroid Build Coastguard Worker }
364*c8dee2aaSAndroid Build Coastguard Worker
365*c8dee2aaSAndroid Build Coastguard Worker fFormatCtx = avformat_alloc_context();
366*c8dee2aaSAndroid Build Coastguard Worker if (!fFormatCtx) {
367*c8dee2aaSAndroid Build Coastguard Worker this->reset();
368*c8dee2aaSAndroid Build Coastguard Worker return false;
369*c8dee2aaSAndroid Build Coastguard Worker }
370*c8dee2aaSAndroid Build Coastguard Worker fFormatCtx->pb = fStreamCtx;
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker int err = avformat_open_input(&fFormatCtx, nullptr, nullptr, nullptr);
373*c8dee2aaSAndroid Build Coastguard Worker if (err < 0) {
374*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("avformat_open_input failed %d\n", err);
375*c8dee2aaSAndroid Build Coastguard Worker return false;
376*c8dee2aaSAndroid Build Coastguard Worker }
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker const AVCodec* codec;
379*c8dee2aaSAndroid Build Coastguard Worker fStreamIndex = av_find_best_stream(fFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
380*c8dee2aaSAndroid Build Coastguard Worker if (fStreamIndex < 0) {
381*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("av_find_best_stream failed %d\n", fStreamIndex);
382*c8dee2aaSAndroid Build Coastguard Worker this->reset();
383*c8dee2aaSAndroid Build Coastguard Worker return false;
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker
386*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(codec);
387*c8dee2aaSAndroid Build Coastguard Worker fDecoderCtx = avcodec_alloc_context3(codec);
388*c8dee2aaSAndroid Build Coastguard Worker
389*c8dee2aaSAndroid Build Coastguard Worker AVStream* strm = fFormatCtx->streams[fStreamIndex];
390*c8dee2aaSAndroid Build Coastguard Worker if ((err = avcodec_parameters_to_context(fDecoderCtx, strm->codecpar)) < 0) {
391*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("avcodec_parameters_to_context failed %d\n", err);
392*c8dee2aaSAndroid Build Coastguard Worker this->reset();
393*c8dee2aaSAndroid Build Coastguard Worker return false;
394*c8dee2aaSAndroid Build Coastguard Worker }
395*c8dee2aaSAndroid Build Coastguard Worker
396*c8dee2aaSAndroid Build Coastguard Worker if ((err = avcodec_open2(fDecoderCtx, codec, nullptr)) < 0) {
397*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("avcodec_open2 failed %d\n", err);
398*c8dee2aaSAndroid Build Coastguard Worker this->reset();
399*c8dee2aaSAndroid Build Coastguard Worker return false;
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker
402*c8dee2aaSAndroid Build Coastguard Worker fFrame = av_frame_alloc();
403*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fFrame);
404*c8dee2aaSAndroid Build Coastguard Worker
405*c8dee2aaSAndroid Build Coastguard Worker av_init_packet(&fPacket); // is there a "free" call?
406*c8dee2aaSAndroid Build Coastguard Worker
407*c8dee2aaSAndroid Build Coastguard Worker fMode = kProcessing_Mode;
408*c8dee2aaSAndroid Build Coastguard Worker
409*c8dee2aaSAndroid Build Coastguard Worker return true;
410*c8dee2aaSAndroid Build Coastguard Worker }
411*c8dee2aaSAndroid Build Coastguard Worker
dimensions() const412*c8dee2aaSAndroid Build Coastguard Worker SkISize SkVideoDecoder::dimensions() const {
413*c8dee2aaSAndroid Build Coastguard Worker if (!fFormatCtx) {
414*c8dee2aaSAndroid Build Coastguard Worker return {0, 0};
415*c8dee2aaSAndroid Build Coastguard Worker }
416*c8dee2aaSAndroid Build Coastguard Worker
417*c8dee2aaSAndroid Build Coastguard Worker AVStream* strm = fFormatCtx->streams[fStreamIndex];
418*c8dee2aaSAndroid Build Coastguard Worker return {strm->codecpar->width, strm->codecpar->height};
419*c8dee2aaSAndroid Build Coastguard Worker }
420*c8dee2aaSAndroid Build Coastguard Worker
duration() const421*c8dee2aaSAndroid Build Coastguard Worker double SkVideoDecoder::duration() const {
422*c8dee2aaSAndroid Build Coastguard Worker if (!fFormatCtx) {
423*c8dee2aaSAndroid Build Coastguard Worker return 0;
424*c8dee2aaSAndroid Build Coastguard Worker }
425*c8dee2aaSAndroid Build Coastguard Worker
426*c8dee2aaSAndroid Build Coastguard Worker AVStream* strm = fFormatCtx->streams[fStreamIndex];
427*c8dee2aaSAndroid Build Coastguard Worker AVRational base = strm->time_base;
428*c8dee2aaSAndroid Build Coastguard Worker return 1.0 * strm->duration * base.num / base.den;
429*c8dee2aaSAndroid Build Coastguard Worker }
430*c8dee2aaSAndroid Build Coastguard Worker
rewind()431*c8dee2aaSAndroid Build Coastguard Worker bool SkVideoDecoder::rewind() {
432*c8dee2aaSAndroid Build Coastguard Worker auto stream = std::move(fStream);
433*c8dee2aaSAndroid Build Coastguard Worker this->reset();
434*c8dee2aaSAndroid Build Coastguard Worker if (stream) {
435*c8dee2aaSAndroid Build Coastguard Worker stream->rewind();
436*c8dee2aaSAndroid Build Coastguard Worker }
437*c8dee2aaSAndroid Build Coastguard Worker return this->loadStream(std::move(stream));
438*c8dee2aaSAndroid Build Coastguard Worker }
439