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