libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

thumbnailffmpeg_extractor.c (44693B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2008, 2012, 2026 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/thumbnailffmpeg_extractor.c
     22  * @author Christian Grothoff
     23  * @brief this extractor produces a binary encoded thumbnail of a frame
     24  *        taken from a video, using FFmpeg for demuxing, decoding and
     25  *        scaling
     26  *
     27  * Unlike the gtk thumbnailer, which can only re-scale a still image, this
     28  * plugin seeks about 30s into a video and encodes the frame it finds
     29  * there as a small JPEG (or PNG).  A previous incarnation of this plugin
     30  * was removed in 2021 because it had accumulated compatibility code for
     31  * every libav and FFmpeg API since 2008; this one deliberately requires
     32  * FFmpeg 5.1 or newer and uses only the send/receive APIs, so that there
     33  * is nothing to keep in sync with upstream churn.
     34  *
     35  * Besides the thumbnail, the plugin always reports the language of every
     36  * subtitle stream.  Two options add more expensive output:
     37  *
     38  * - "srt:LANG" dumps the first text subtitle stream in language LANG as
     39  *   a complete SubRip file.  This demuxes the entire file (without
     40  *   decoding the video), and is done first, on a fresh demuxer.
     41  * - "gallery:N" replaces the single thumbnail by a contact sheet of N
     42  *   frames sampled evenly over the whole duration.  Frames that show
     43  *   nothing (black, white, a fade) are replaced by a nearby frame of the
     44  *   same slice of the video.  If the deadline expires while the sheet is
     45  *   being assembled, the sheet is emitted with the tiles obtained so far
     46  *   and black cells for the rest: a partial overview is more useful than
     47  *   a single frame, and trying to get a single frame once the budget is
     48  *   used up would not work anyway.  Without a known duration there is
     49  *   nothing to spread the samples over, and the plugin falls back to the
     50  *   single thumbnail.
     51  */
     52 #include "platform.h"
     53 #include "extractor.h"
     54 #include "mediautil.h"
     55 #include "mediaffmpeg.h"
     56 
     57 #include <libavutil/imgutils.h>
     58 #include <libswscale/swscale.h>
     59 
     60 /**
     61  * Give up after this many decoded frames, no matter what the timestamps
     62  * say.  A malformed file can otherwise keep us decoding forever.
     63  */
     64 #define MAX_FRAMES 2048
     65 
     66 /**
     67  * Mean absolute deviation below which we consider a frame to be a flat
     68  * surface (a fade-in, a black leader) and not worth showing.
     69  */
     70 #define FLAT_FRAME_MAD 2
     71 
     72 /**
     73  * How far to jump ahead when we hit a flat frame, in seconds.
     74  */
     75 #define FLAT_FRAME_SKIP 5
     76 
     77 /**
     78  * How often we are willing to do that.
     79  */
     80 #define MAX_FLAT_RETRIES 2
     81 
     82 /**
     83  * JPEG quantizer scale; smaller is better and bigger.
     84  */
     85 #define JPEG_QSCALE 5
     86 
     87 /**
     88  * Give up on reaching a gallery sample after decoding this many frames
     89  * past the keyframe we seeked to.
     90  */
     91 #define GALLERY_MAX_FRAMES 512
     92 
     93 /**
     94  * Largest contact sheet we assemble, in pixels.  Far below
     95  * #EXTRACTOR_MEDIA_MAX_PIXELS, because the sheet has to be encoded in
     96  * one go, without any I/O that would tell the parent process we are
     97  * still alive, and a sheet this size already cannot be squeezed into
     98  * #EXTRACTOR_MEDIA_MAX_GALLERY_BYTES as a PNG unless it is very simple.
     99  */
    100 #define GALLERY_MAX_SHEET_PIXELS EXTRACTOR_MEDIA_MAX_GALLERY_PIXELS
    101 
    102 /**
    103  * zlib level for PNG galleries; 9 costs a lot of time on a big sheet
    104  * for a few percent.
    105  */
    106 #define GALLERY_PNG_LEVEL 6
    107 
    108 /**
    109  * Most distinct subtitle languages we report.
    110  */
    111 #define MAX_LANGUAGES 64
    112 
    113 /**
    114  * Longest language tag we report (e.g. "zh-Hant-TW").
    115  */
    116 #define MAX_LANGUAGE_LEN 15
    117 
    118 /**
    119  * Most subtitle cues we collect for an SRT dump.
    120  */
    121 #define MAX_CUES 100000
    122 
    123 /**
    124  * How long a cue whose end is unknown and that has no successor stays
    125  * on screen, in milliseconds.
    126  */
    127 #define DEFAULT_CUE_MS 5000
    128 
    129 /**
    130  * Cap for any time stamp in an SRT dump (1000 hours), in milliseconds.
    131  */
    132 #define MAX_CUE_TIME_MS (1000ULL * 3600 * 1000)
    133 
    134 
    135 /**
    136  * Is @a frame a flat surface?  Cheap heuristic over the first plane; we
    137  * only want to catch the completely uniform black frame of a fade-in.
    138  *
    139  * @param frame frame to inspect
    140  * @return 1 if the frame carries no discernible detail
    141  */
    142 static int
    143 is_flat (const AVFrame *frame)
    144 {
    145   const uint8_t *p = frame->data[0];
    146   int stride = frame->linesize[0];
    147   uint64_t sum = 0;
    148   uint64_t dev = 0;
    149   unsigned int n = 0;
    150   int x;
    151   int y;
    152 
    153   if ( (NULL == p) ||
    154        (stride <= 0) ||
    155        (frame->height <= 0) )
    156     return 0;
    157   for (y = 0; y < frame->height; y += 4)
    158     for (x = 0; x < stride; x += 4)
    159     {
    160       sum += p[y * stride + x];
    161       n++;
    162     }
    163   if (0 == n)
    164     return 0;
    165   sum /= n;
    166   for (y = 0; y < frame->height; y += 4)
    167     for (x = 0; x < stride; x += 4)
    168     {
    169       uint8_t v = p[y * stride + x];
    170 
    171       dev += (v > sum) ? (v - sum) : (sum - v);
    172     }
    173   return (dev / n) < FLAT_FRAME_MAD;
    174 }
    175 
    176 
    177 /**
    178  * Scale @a frame to @a width x @a height and encode it as JPEG or PNG.
    179  *
    180  * @param frame the image to convert (any pixel format swscale accepts)
    181  * @param width width of the result
    182  * @param height height of the result
    183  * @param png 1 for PNG, 0 for JPEG
    184  * @param qscale JPEG quantizer scale (ignored for PNG)
    185  * @param level zlib level for PNG (ignored for JPEG)
    186  * @param max_bytes largest acceptable result
    187  * @param[out] output set to the encoded image, to be freed with av_free()
    188  * @param[out] output_size set to the number of bytes in @a output
    189  * @return 0 on success, -2 if the result would exceed @a max_bytes,
    190  *         -1 on other errors
    191  */
    192 static int
    193 encode_image (const AVFrame *frame,
    194               unsigned int width,
    195               unsigned int height,
    196               int png,
    197               int qscale,
    198               int level,
    199               size_t max_bytes,
    200               uint8_t **output,
    201               size_t *output_size)
    202 {
    203   const AVCodec *codec;
    204   AVCodecContext *ctx = NULL;
    205   struct SwsContext *sws = NULL;
    206   AVFrame *dst = NULL;
    207   AVPacket *pkt = NULL;
    208   enum AVPixelFormat dst_fmt;
    209   int ret = -1;
    210   int r;
    211 
    212   if ( (0 == width) ||
    213        (0 == height) ||
    214        (width > EXTRACTOR_MEDIA_MAX_DIMENSION) ||
    215        (height > EXTRACTOR_MEDIA_MAX_DIMENSION) )
    216     return -1;
    217   if (! sws_isSupportedInput (frame->format))
    218     return -1;
    219   if (png)
    220   {
    221     dst_fmt = AV_PIX_FMT_RGB24;
    222     codec = avcodec_find_encoder (AV_CODEC_ID_PNG);
    223   }
    224   else
    225   {
    226     dst_fmt = AV_PIX_FMT_YUVJ420P;
    227     codec = avcodec_find_encoder (AV_CODEC_ID_MJPEG);
    228   }
    229   if (NULL == codec)
    230     return -1;
    231   if (NULL == (sws = sws_getContext (frame->width,
    232                                      frame->height,
    233                                      frame->format,
    234                                      (int) width,
    235                                      (int) height,
    236                                      dst_fmt,
    237                                      SWS_BILINEAR,
    238                                      NULL,
    239                                      NULL,
    240                                      NULL)))
    241     return -1;
    242   if (NULL == (dst = av_frame_alloc ()))
    243     goto cleanup;
    244   dst->format = dst_fmt;
    245   dst->width = (int) width;
    246   dst->height = (int) height;
    247   dst->pts = 0;
    248   if (0 != av_frame_get_buffer (dst, 0))
    249     goto cleanup;
    250   if (0 > sws_scale (sws,
    251                      (const uint8_t *const *) frame->data,
    252                      frame->linesize,
    253                      0,
    254                      frame->height,
    255                      dst->data,
    256                      dst->linesize))
    257     goto cleanup;
    258   if (NULL == (ctx = avcodec_alloc_context3 (codec)))
    259     goto cleanup;
    260   ctx->width = (int) width;
    261   ctx->height = (int) height;
    262   ctx->pix_fmt = dst_fmt;
    263   ctx->time_base = (AVRational) { 1, 25 };
    264   ctx->thread_count = 1;
    265   if (png)
    266   {
    267     ctx->compression_level = level;
    268   }
    269   else
    270   {
    271     ctx->color_range = AVCOL_RANGE_JPEG;
    272     ctx->flags |= AV_CODEC_FLAG_QSCALE;
    273     ctx->global_quality = FF_QP2LAMBDA * qscale;
    274   }
    275   if (0 != avcodec_open2 (ctx, codec, NULL))
    276     goto cleanup;
    277   if (NULL == (pkt = av_packet_alloc ()))
    278     goto cleanup;
    279   dst->quality = ctx->global_quality;
    280   if (0 != avcodec_send_frame (ctx, dst))
    281     goto cleanup;
    282   r = avcodec_receive_packet (ctx, pkt);
    283   if (AVERROR (EAGAIN) == r)
    284   {
    285     /* encoder wants to be flushed before it hands anything back */
    286     if (0 != avcodec_send_frame (ctx, NULL))
    287       goto cleanup;
    288     r = avcodec_receive_packet (ctx, pkt);
    289   }
    290   if (0 != r)
    291     goto cleanup;
    292   if (pkt->size <= 0)
    293     goto cleanup;
    294   if ((size_t) pkt->size > max_bytes)
    295   {
    296     ret = -2;
    297     goto cleanup;
    298   }
    299   if (NULL == (*output = av_malloc (pkt->size)))
    300     goto cleanup;
    301   memcpy (*output,
    302           pkt->data,
    303           pkt->size);
    304   *output_size = (size_t) pkt->size;
    305   ret = 0;
    306 cleanup:
    307   if (NULL != pkt)
    308     av_packet_free (&pkt);
    309   if (NULL != ctx)
    310     avcodec_free_context (&ctx);
    311   if (NULL != dst)
    312     av_frame_free (&dst);
    313   sws_freeContext (sws);
    314   return ret;
    315 }
    316 
    317 
    318 /**
    319  * Scale @a frame down and encode it as JPEG or PNG.
    320  *
    321  * @param frame the decoded frame to convert
    322  * @param opt plugin options (target size and format)
    323  * @param[out] output set to the encoded image, to be freed with av_free()
    324  * @param[out] output_size set to the number of bytes in @a output
    325  * @return 0 on success
    326  */
    327 static int
    328 encode_thumbnail (const AVFrame *frame,
    329                   const struct EXTRACTOR_MediaOptions *opt,
    330                   uint8_t **output,
    331                   size_t *output_size)
    332 {
    333   unsigned int width;
    334   unsigned int height;
    335 
    336   if (! EXTRACTOR_media_thumbnail_size_ (frame->width,
    337                                          frame->height,
    338                                          opt->size,
    339                                          &width,
    340                                          &height))
    341     return -1;
    342   return (0 == encode_image (frame,
    343                              width,
    344                              height,
    345                              opt->png,
    346                              JPEG_QSCALE,
    347                              9,
    348                              EXTRACTOR_MEDIA_MAX_THUMBNAIL,
    349                              output,
    350                              output_size)) ? 0 : -1;
    351 }
    352 
    353 
    354 /**
    355  * Decode the video stream until we reach @a target seconds, and hand back
    356  * the frame we find there.  If we never get there (short file, broken
    357  * timestamps), the last frame we did decode is returned instead.
    358  *
    359  * Normally "there" is the first frame at or after @a target.  With
    360  * @a displayed, it is the frame on screen at @a target, i.e. the last
    361  * one starting at or before it; the difference matters for streams
    362  * that only carry a new frame when the picture changes.
    363  *
    364  * @param io open I/O state
    365  * @param ctx open decoder for @a stream
    366  * @param stream index of the video stream
    367  * @param target position we are aiming for, in seconds
    368  * @param skip_flat 1 to move on (a few times) if the frame at @a target
    369  *        is a flat surface
    370  * @param displayed 1 to return the frame displayed at @a target
    371  * @param max_frames give up after decoding this many frames
    372  * @param[out] frame frame to fill in
    373  * @return 0 if @a frame was filled in
    374  */
    375 static int
    376 decode_frame_at (struct EXTRACTOR_FFmpegIO *io,
    377                  AVCodecContext *ctx,
    378                  int stream,
    379                  double target,
    380                  int skip_flat,
    381                  int displayed,
    382                  unsigned int max_frames,
    383                  AVFrame *frame)
    384 {
    385   AVPacket *pkt;
    386   AVFrame *last;
    387   AVRational tb = io->fmt->streams[stream]->time_base;
    388   unsigned int frames = 0;
    389   unsigned int retries = 0;
    390   int have_last = 0;
    391   int overshot = 0;
    392   int ret = -1;
    393 
    394   if (NULL == (pkt = av_packet_alloc ()))
    395     return -1;
    396   if (NULL == (last = av_frame_alloc ()))
    397   {
    398     av_packet_free (&pkt);
    399     return -1;
    400   }
    401   while (frames < max_frames)
    402   {
    403     int r;
    404 
    405     if (EXTRACTOR_media_deadline_expired_ (&io->deadline))
    406       break;
    407     /* decoding can go on for a while without touching the file */
    408     (void) io->ec->seek (io->ec->cls,
    409                          0,
    410                          SEEK_CUR);
    411     r = av_read_frame (io->fmt, pkt);
    412     if (0 != r)
    413     {
    414       /* end of file: flush the decoder and take what is left */
    415       avcodec_send_packet (ctx, NULL);
    416     }
    417     else if (pkt->stream_index != stream)
    418     {
    419       av_packet_unref (pkt);
    420       continue;
    421     }
    422     else if (0 != avcodec_send_packet (ctx, pkt))
    423     {
    424       av_packet_unref (pkt);
    425       continue;
    426     }
    427     av_packet_unref (pkt);
    428     while (0 == avcodec_receive_frame (ctx, frame))
    429     {
    430       double ts;
    431 
    432       frames++;
    433       ts = (AV_NOPTS_VALUE == frame->best_effort_timestamp)
    434            ? -1.0
    435            : frame->best_effort_timestamp * av_q2d (tb);
    436       if ( (ts < target) &&
    437            (frames < max_frames) )
    438       {
    439         /* not there yet; hold on to it in case we never arrive */
    440         av_frame_unref (last);
    441         if (0 == av_frame_ref (last, frame))
    442           have_last = 1;
    443         av_frame_unref (frame);
    444         continue;
    445       }
    446       if (displayed &&
    447           have_last &&
    448           (ts > target) )
    449       {
    450         /* overshot: the previous frame is still on screen at target */
    451         av_frame_unref (frame);
    452         overshot = 1;
    453         break;
    454       }
    455       if (skip_flat &&
    456           (retries < MAX_FLAT_RETRIES) &&
    457           is_flat (frame) )
    458       {
    459         /* a black leader or a fade-in; try a bit further in */
    460         retries++;
    461         target = ((ts < 0.0) ? target : ts) + FLAT_FRAME_SKIP;
    462         av_frame_unref (last);
    463         if (0 == av_frame_ref (last, frame))
    464           have_last = 1;
    465         av_frame_unref (frame);
    466         continue;
    467       }
    468       av_frame_unref (last);
    469       av_frame_free (&last);
    470       av_packet_free (&pkt);
    471       return 0;
    472     }
    473     if (overshot)
    474       break;
    475     if (0 != r)
    476       break;   /* end of file, and the decoder had nothing left either */
    477   }
    478   if (have_last)
    479   {
    480     av_frame_unref (frame);
    481     if (0 == av_frame_ref (frame, last))
    482       ret = 0;
    483   }
    484   av_frame_unref (last);
    485   av_frame_free (&last);
    486   av_packet_free (&pkt);
    487   return ret;
    488 }
    489 
    490 
    491 /**
    492  * Is @a tag something we are willing to report as a language?  Stream
    493  * metadata comes straight from the file, so only accept what a language
    494  * tag can look like (letters, digits, '-' and '_'), and skip the
    495  * "undetermined" placeholder.
    496  *
    497  * @param tag language tag of a stream, may be NULL
    498  * @return 1 if @a tag should be reported
    499  */
    500 static int
    501 is_reportable_language (const char *tag)
    502 {
    503   size_t len;
    504   size_t i;
    505 
    506   if (NULL == tag)
    507     return 0;
    508   len = strlen (tag);
    509   if ( (0 == len) ||
    510        (len > MAX_LANGUAGE_LEN) )
    511     return 0;
    512   for (i = 0; i < len; i++)
    513     if (! ( ( (tag[i] >= 'a') && (tag[i] <= 'z') ) ||
    514             ( (tag[i] >= 'A') && (tag[i] <= 'Z') ) ||
    515             ( (tag[i] >= '0') && (tag[i] <= '9') ) ||
    516             ('-' == tag[i]) ||
    517             ('_' == tag[i]) ) )
    518       return 0;
    519   if (0 == strcasecmp (tag,
    520                        "und"))
    521     return 0;
    522   return 1;
    523 }
    524 
    525 
    526 /**
    527  * Report the language of every subtitle stream, each distinct language
    528  * once, in stream order.
    529  *
    530  * @param io open I/O state
    531  * @return 0 to continue, 1 if the application asked us to stop
    532  */
    533 static int
    534 emit_subtitle_languages (struct EXTRACTOR_FFmpegIO *io)
    535 {
    536   const char *seen[MAX_LANGUAGES];
    537   unsigned int nseen = 0;
    538   unsigned int i;
    539 
    540   for (i = 0; i < io->fmt->nb_streams; i++)
    541   {
    542     const AVStream *st = io->fmt->streams[i];
    543     const AVDictionaryEntry *e;
    544     unsigned int j;
    545 
    546     if (AVMEDIA_TYPE_SUBTITLE != st->codecpar->codec_type)
    547       continue;
    548     e = av_dict_get (st->metadata,
    549                      "language",
    550                      NULL,
    551                      0);
    552     if ( (NULL == e) ||
    553          (! is_reportable_language (e->value)) )
    554       continue;
    555     for (j = 0; j < nseen; j++)
    556       if (0 == strcasecmp (seen[j],
    557                            e->value))
    558         break;
    559     if (j < nseen)
    560       continue;
    561     if (nseen == MAX_LANGUAGES)
    562       break;
    563     seen[nseen++] = e->value;
    564     if (0 != io->ec->proc (io->ec->cls,
    565                            "thumbnailffmpeg",
    566                            EXTRACTOR_METATYPE_SUBTITLE_LANGUAGE,
    567                            EXTRACTOR_METAFORMAT_UTF8,
    568                            "text/plain",
    569                            e->value,
    570                            strlen (e->value) + 1))
    571       return 1;
    572   }
    573   return 0;
    574 }
    575 
    576 
    577 /**
    578  * One subtitle event, collected before we can write the SRT file.
    579  */
    580 struct Cue
    581 {
    582   /**
    583    * Start in milliseconds from the beginning of the file.
    584    */
    585   uint64_t start;
    586 
    587   /**
    588    * End in milliseconds, UINT64_MAX if unknown.
    589    */
    590   uint64_t end;
    591 
    592   /**
    593    * Text of the cue (not 0-terminated).
    594    */
    595   char *text;
    596 
    597   /**
    598    * Number of bytes in @e text.
    599    */
    600   size_t len;
    601 
    602   /**
    603    * Position in the stream, to keep the sort stable.
    604    */
    605   unsigned int seq;
    606 
    607   /**
    608    * 1 if @e text is an ASS dialogue event.
    609    */
    610   int is_ass;
    611 };
    612 
    613 
    614 /**
    615  * The cues of a subtitle stream.
    616  */
    617 struct CueList
    618 {
    619   /**
    620    * Array of cues.
    621    */
    622   struct Cue *cues;
    623 
    624   /**
    625    * Number of valid entries in @e cues.
    626    */
    627   unsigned int count;
    628 
    629   /**
    630    * Allocated length of @e cues.
    631    */
    632   unsigned int cap;
    633 
    634   /**
    635    * Total number of text bytes in @e cues.
    636    */
    637   size_t bytes;
    638 
    639   /**
    640    * Set once we ran into one of the limits.
    641    */
    642   int full;
    643 };
    644 
    645 
    646 /**
    647  * Append a cue to @a cl.
    648  *
    649  * @param[in,out] cl list to extend
    650  * @param start start in milliseconds
    651  * @param end end in milliseconds, UINT64_MAX if unknown
    652  * @param text text of the cue
    653  * @param is_ass 1 if @a text is an ASS dialogue event
    654  */
    655 static void
    656 cue_add (struct CueList *cl,
    657          uint64_t start,
    658          uint64_t end,
    659          const char *text,
    660          int is_ass)
    661 {
    662   struct Cue *c;
    663   size_t len;
    664 
    665   if ( (NULL == text) ||
    666        (cl->full) )
    667     return;
    668   len = strlen (text);
    669   if (0 == len)
    670     return;
    671   if ( (cl->count >= MAX_CUES) ||
    672        (cl->bytes + len > EXTRACTOR_MEDIA_MAX_SRT) )
    673   {
    674     cl->full = 1;
    675     return;
    676   }
    677   if (cl->count == cl->cap)
    678   {
    679     unsigned int ncap = (0 == cl->cap) ? 64 : 2 * cl->cap;
    680     struct Cue *n;
    681 
    682     if (ncap > MAX_CUES)
    683       ncap = MAX_CUES;
    684     n = realloc (cl->cues,
    685                  ncap * sizeof (struct Cue));
    686     if (NULL == n)
    687     {
    688       cl->full = 1;
    689       return;
    690     }
    691     cl->cues = n;
    692     cl->cap = ncap;
    693   }
    694   c = &cl->cues[cl->count];
    695   if (NULL == (c->text = malloc (len)))
    696   {
    697     cl->full = 1;
    698     return;
    699   }
    700   memcpy (c->text,
    701           text,
    702           len);
    703   c->len = len;
    704   c->start = start;
    705   c->end = end;
    706   c->is_ass = is_ass;
    707   c->seq = cl->count;
    708   cl->bytes += len;
    709   cl->count++;
    710 }
    711 
    712 
    713 /**
    714  * Order cues by start time, then by position in the stream.
    715  *
    716  * @param a first `struct Cue`
    717  * @param b second `struct Cue`
    718  * @return -1, 0 or 1 as required by qsort()
    719  */
    720 static int
    721 cue_cmp (const void *a,
    722          const void *b)
    723 {
    724   const struct Cue *ca = a;
    725   const struct Cue *cb = b;
    726 
    727   if (ca->start != cb->start)
    728     return (ca->start < cb->start) ? -1 : 1;
    729   if (ca->seq != cb->seq)
    730     return (ca->seq < cb->seq) ? -1 : 1;
    731   return 0;
    732 }
    733 
    734 
    735 /**
    736  * Release @a cl.
    737  *
    738  * @param[in,out] cl list to release
    739  */
    740 static void
    741 cue_clear (struct CueList *cl)
    742 {
    743   unsigned int i;
    744 
    745   for (i = 0; i < cl->count; i++)
    746     free (cl->cues[i].text);
    747   free (cl->cues);
    748   memset (cl, 0, sizeof (*cl));
    749 }
    750 
    751 
    752 /**
    753  * Convert @a ts in @a tb to milliseconds relative to @a origin_ms,
    754  * clamped to [0, #MAX_CUE_TIME_MS].
    755  *
    756  * @param ts time stamp
    757  * @param tb time base of @a ts
    758  * @param origin_ms start of the file in milliseconds
    759  * @return time in milliseconds
    760  */
    761 static uint64_t
    762 to_cue_ms (int64_t ts,
    763            AVRational tb,
    764            int64_t origin_ms)
    765 {
    766   int64_t ms;
    767 
    768   if ( (tb.num <= 0) ||
    769        (tb.den <= 0) )
    770     return 0;
    771   ms = av_rescale_q (ts,
    772                      tb,
    773                      (AVRational) { 1, 1000 });
    774   if ( (ms <= 0) ||
    775        (ms <= origin_ms) )
    776     return 0;
    777   if (origin_ms > 0)
    778     ms -= origin_ms;
    779   if ((uint64_t) ms > MAX_CUE_TIME_MS)
    780     return MAX_CUE_TIME_MS;
    781   return (uint64_t) ms;
    782 }
    783 
    784 
    785 /**
    786  * Decode one subtitle packet and collect its cues.
    787  *
    788  * @param ctx open subtitle decoder
    789  * @param pkt packet of the subtitle stream
    790  * @param tb time base of the stream
    791  * @param origin_ms start of the file in milliseconds
    792  * @param[in,out] cl where to put the cues
    793  */
    794 static void
    795 decode_subtitle_packet (AVCodecContext *ctx,
    796                         AVPacket *pkt,
    797                         AVRational tb,
    798                         int64_t origin_ms,
    799                         struct CueList *cl)
    800 {
    801   AVSubtitle sub;
    802   int got = 0;
    803   int64_t pts;
    804   uint64_t base;
    805   uint64_t start;
    806   uint64_t end;
    807   unsigned int i;
    808 
    809   pts = (AV_NOPTS_VALUE != pkt->pts) ? pkt->pts : pkt->dts;
    810   if (AV_NOPTS_VALUE == pts)
    811     return;   /* no idea when to show it */
    812   memset (&sub, 0, sizeof (sub));
    813   if ( (0 > avcodec_decode_subtitle2 (ctx,
    814                                       &sub,
    815                                       &got,
    816                                       pkt)) ||
    817        (! got) )
    818     return;
    819   base = to_cue_ms (pts,
    820                     tb,
    821                     origin_ms);
    822   start = base + sub.start_display_time;
    823   if ( (sub.end_display_time > sub.start_display_time) &&
    824        (UINT32_MAX != sub.end_display_time) )
    825     end = base + sub.end_display_time;
    826   else if (pkt->duration > 0)
    827     end = start + to_cue_ms (pkt->duration,
    828                              tb,
    829                              0);
    830   else
    831     end = UINT64_MAX;
    832   for (i = 0; i < sub.num_rects; i++)
    833   {
    834     const AVSubtitleRect *r = sub.rects[i];
    835 
    836     if (NULL == r)
    837       continue;
    838     if (SUBTITLE_ASS == r->type)
    839       cue_add (cl, start, end, r->ass, 1);
    840     else if (SUBTITLE_TEXT == r->type)
    841       cue_add (cl, start, end, r->text, 0);
    842   }
    843   avsubtitle_free (&sub);
    844 }
    845 
    846 
    847 /**
    848  * Find the first subtitle stream in language @a lang.
    849  *
    850  * @param io open I/O state
    851  * @param lang requested language
    852  * @return stream index, -1 if there is none
    853  */
    854 static int
    855 find_subtitle_stream (struct EXTRACTOR_FFmpegIO *io,
    856                       const char *lang)
    857 {
    858   unsigned int i;
    859 
    860   for (i = 0; i < io->fmt->nb_streams; i++)
    861   {
    862     const AVStream *st = io->fmt->streams[i];
    863     const AVDictionaryEntry *e;
    864 
    865     if (AVMEDIA_TYPE_SUBTITLE != st->codecpar->codec_type)
    866       continue;
    867     e = av_dict_get (st->metadata,
    868                      "language",
    869                      NULL,
    870                      0);
    871     if ( (NULL != e) &&
    872          EXTRACTOR_media_lang_matches_ (lang,
    873                                         e->value) )
    874       return (int) i;
    875   }
    876   return -1;
    877 }
    878 
    879 
    880 /**
    881  * Dump the first subtitle stream in the language requested with
    882  * "srt:LANG" as a SubRip file.  Must be called on a freshly opened
    883  * demuxer, as it reads the file from where the demuxer stands.
    884  *
    885  * @param io open I/O state
    886  * @param opt plugin options
    887  * @param[out] touched set to 1 if we read (and discarded) packets, so
    888  *             that the caller has to reopen the input
    889  * @return 0 to continue, 1 if the application asked us to stop
    890  */
    891 static int
    892 dump_srt (struct EXTRACTOR_FFmpegIO *io,
    893           const struct EXTRACTOR_MediaOptions *opt,
    894           int *touched)
    895 {
    896   const AVCodecDescriptor *desc;
    897   const AVCodec *codec;
    898   AVCodecContext *ctx = NULL;
    899   AVPacket *pkt = NULL;
    900   AVStream *st;
    901   struct CueList cl;
    902   struct EXTRACTOR_MediaSrt srt;
    903   int64_t origin_ms = 0;
    904   uint64_t next_start;
    905   unsigned int i;
    906   int stream;
    907   int ret = 0;
    908 
    909   *touched = 0;
    910   memset (&cl, 0, sizeof (cl));
    911   memset (&srt, 0, sizeof (srt));
    912   stream = find_subtitle_stream (io,
    913                                  opt->srt_lang);
    914   if (0 > stream)
    915     return 0;
    916   st = io->fmt->streams[stream];
    917   desc = avcodec_descriptor_get (st->codecpar->codec_id);
    918   if ( (NULL == desc) ||
    919        (0 == (desc->props & AV_CODEC_PROP_TEXT_SUB)) )
    920     return 0;   /* bitmap subtitles; nothing we could write as text */
    921   if (NULL == (codec = avcodec_find_decoder (st->codecpar->codec_id)))
    922     return 0;
    923   if (NULL == (ctx = avcodec_alloc_context3 (codec)))
    924     return 0;
    925   if (0 > avcodec_parameters_to_context (ctx,
    926                                          st->codecpar))
    927     goto cleanup;
    928   ctx->pkt_timebase = st->time_base;
    929   ctx->thread_count = 1;
    930   if (0 != avcodec_open2 (ctx, codec, NULL))
    931     goto cleanup;
    932   if (NULL == (pkt = av_packet_alloc ()))
    933     goto cleanup;
    934   if (AV_NOPTS_VALUE != io->fmt->start_time)
    935     origin_ms = io->fmt->start_time / 1000;
    936   /* let the demuxer skip everything else */
    937   for (i = 0; i < io->fmt->nb_streams; i++)
    938     if ((int) i != stream)
    939       io->fmt->streams[i]->discard = AVDISCARD_ALL;
    940   *touched = 1;
    941   while ( (! cl.full) &&
    942           (! EXTRACTOR_media_deadline_expired_ (&io->deadline)) )
    943   {
    944     (void) io->ec->seek (io->ec->cls,
    945                          0,
    946                          SEEK_CUR); /* keep-alive */
    947     if (0 != av_read_frame (io->fmt,
    948                             pkt))
    949       break;
    950     if (pkt->stream_index == stream)
    951       decode_subtitle_packet (ctx,
    952                               pkt,
    953                               st->time_base,
    954                               origin_ms,
    955                               &cl);
    956     av_packet_unref (pkt);
    957   }
    958   if (0 == cl.count)
    959     goto cleanup;
    960   /* containers normally deliver subtitles in order, but nothing forces
    961      them to; and the ends of open-ended cues depend on the order */
    962   qsort (cl.cues,
    963          cl.count,
    964          sizeof (struct Cue),
    965          &cue_cmp);
    966   next_start = UINT64_MAX;
    967   for (i = cl.count; i > 0; i--)
    968   {
    969     struct Cue *c = &cl.cues[i - 1];
    970 
    971     if (UINT64_MAX == c->end)
    972       c->end = (UINT64_MAX != next_start)
    973                ? next_start
    974                : c->start + DEFAULT_CUE_MS;
    975     if (c->end < c->start)
    976       c->end = c->start;
    977     if ( (i > 1) &&
    978          (cl.cues[i - 2].start < c->start) )
    979       next_start = c->start;
    980   }
    981   for (i = 0; i < cl.count; i++)
    982     EXTRACTOR_media_srt_add_ (&srt,
    983                               cl.cues[i].start,
    984                               cl.cues[i].end,
    985                               cl.cues[i].text,
    986                               cl.cues[i].len,
    987                               cl.cues[i].is_ass);
    988   if (0 != EXTRACTOR_media_srt_emit_ (&srt,
    989                                       io->ec,
    990                                       "thumbnailffmpeg"))
    991     ret = 1;
    992 cleanup:
    993   EXTRACTOR_media_srt_clear_ (&srt);
    994   cue_clear (&cl);
    995   if (NULL != pkt)
    996     av_packet_free (&pkt);
    997   avcodec_free_context (&ctx);
    998   return ret;
    999 }
   1000 
   1001 
   1002 /**
   1003  * A contact sheet under construction.
   1004  */
   1005 struct Gallery
   1006 {
   1007   /**
   1008    * The sheet, packed RGB24, initially black.
   1009    */
   1010   uint8_t *sheet;
   1011 
   1012   /**
   1013    * Scratch buffer for one tile, packed RGB24.
   1014    */
   1015   uint8_t *tile;
   1016 
   1017   /**
   1018    * The least flat candidate for the current tile, packed RGB24.
   1019    */
   1020   uint8_t *best;
   1021 
   1022   /**
   1023    * Scaler from decoded frames to tiles.
   1024    */
   1025   struct SwsContext *sws;
   1026 
   1027   /**
   1028    * Width of a tile.
   1029    */
   1030   unsigned int tw;
   1031 
   1032   /**
   1033    * Height of a tile.
   1034    */
   1035   unsigned int th;
   1036 
   1037   /**
   1038    * Number of columns.
   1039    */
   1040   unsigned int cols;
   1041 
   1042   /**
   1043    * Number of rows.
   1044    */
   1045   unsigned int rows;
   1046 };
   1047 
   1048 
   1049 /**
   1050  * How much is going on in a tile: the sum of the variances of its
   1051  * three channels.  Used to pick the least boring of several flat
   1052  * candidates.
   1053  *
   1054  * @param rgb packed RGB24 pixels
   1055  * @param width width in pixels
   1056  * @param height height in pixels
   1057  * @return the score, larger means more detail
   1058  */
   1059 static double
   1060 tile_score (const uint8_t *rgb,
   1061             unsigned int width,
   1062             unsigned int height)
   1063 {
   1064   double sum[3] = { 0, 0, 0 };
   1065   double sq[3] = { 0, 0, 0 };
   1066   double n = 0;
   1067   double score = 0;
   1068   unsigned int x;
   1069   unsigned int y;
   1070   unsigned int c;
   1071 
   1072   for (y = 0; y < height; y += 2)
   1073     for (x = 0; x < width; x += 2)
   1074     {
   1075       const uint8_t *p = &rgb[((size_t) y * width + x) * 3];
   1076 
   1077       for (c = 0; c < 3; c++)
   1078       {
   1079         sum[c] += p[c];
   1080         sq[c] += (double) p[c] * p[c];
   1081       }
   1082       n++;
   1083     }
   1084   if (0 == n)
   1085     return 0;
   1086   for (c = 0; c < 3; c++)
   1087     score += sq[c] / n - (sum[c] / n) * (sum[c] / n);
   1088   return score;
   1089 }
   1090 
   1091 
   1092 /**
   1093  * Seek to @a t_ms into the video, decode the frame there and scale it
   1094  * into the tile buffer of @a g.
   1095  *
   1096  * @param io open I/O state
   1097  * @param ctx open video decoder
   1098  * @param stream index of the video stream
   1099  * @param start_ts start of the stream in its time base
   1100  * @param t_ms position relative to @a start_ts in milliseconds
   1101  * @param frame scratch frame
   1102  * @param[in,out] g gallery whose tile buffer to fill
   1103  * @return 0 on success
   1104  */
   1105 static int
   1106 grab_tile (struct EXTRACTOR_FFmpegIO *io,
   1107            AVCodecContext *ctx,
   1108            int stream,
   1109            int64_t start_ts,
   1110            uint64_t t_ms,
   1111            AVFrame *frame,
   1112            struct Gallery *g)
   1113 {
   1114   AVRational tb = io->fmt->streams[stream]->time_base;
   1115   uint8_t *dst[4] = { g->tile, NULL, NULL, NULL };
   1116   int dst_stride[4] = { (int) g->tw * 3, 0, 0, 0 };
   1117   int64_t ts;
   1118   int ret = -1;
   1119 
   1120   if ( (tb.num <= 0) ||
   1121        (tb.den <= 0) )
   1122     return -1;
   1123   ts = av_rescale_q ((int64_t) t_ms,
   1124                      (AVRational) { 1, 1000 },
   1125                      tb);
   1126   if ( (ts < 0) ||
   1127        ( (start_ts > 0) &&
   1128          (ts > INT64_MAX - start_ts) ) )
   1129     return -1;
   1130   ts += start_ts;
   1131   if (0 > av_seek_frame (io->fmt,
   1132                          stream,
   1133                          ts,
   1134                          AVSEEK_FLAG_BACKWARD))
   1135     return -1;
   1136   avcodec_flush_buffers (ctx);
   1137   if (0 != decode_frame_at (io,
   1138                             ctx,
   1139                             stream,
   1140                             ts * av_q2d (tb),
   1141                             0,
   1142                             1,
   1143                             GALLERY_MAX_FRAMES,
   1144                             frame))
   1145     return -1;
   1146   if ( (frame->width <= 0) ||
   1147        (frame->height <= 0) ||
   1148        (frame->width > EXTRACTOR_MEDIA_MAX_DIMENSION) ||
   1149        (frame->height > EXTRACTOR_MEDIA_MAX_DIMENSION) ||
   1150        (((int64_t) frame->width) * frame->height
   1151         > EXTRACTOR_MEDIA_MAX_PIXELS) ||
   1152        (! sws_isSupportedInput (frame->format)) )
   1153     goto cleanup;
   1154   g->sws = sws_getCachedContext (g->sws,
   1155                                  frame->width,
   1156                                  frame->height,
   1157                                  frame->format,
   1158                                  (int) g->tw,
   1159                                  (int) g->th,
   1160                                  AV_PIX_FMT_RGB24,
   1161                                  SWS_BILINEAR,
   1162                                  NULL,
   1163                                  NULL,
   1164                                  NULL);
   1165   if (NULL == g->sws)
   1166     goto cleanup;
   1167   if ((int) g->th != sws_scale (g->sws,
   1168                                 (const uint8_t *const *) frame->data,
   1169                                 frame->linesize,
   1170                                 0,
   1171                                 frame->height,
   1172                                 dst,
   1173                                 dst_stride))
   1174     goto cleanup;
   1175   ret = 0;
   1176 cleanup:
   1177   av_frame_unref (frame);
   1178   return ret;
   1179 }
   1180 
   1181 
   1182 /**
   1183  * Copy a tile into cell @a i of the sheet.
   1184  *
   1185  * @param[in,out] g gallery to update
   1186  * @param i index of the cell
   1187  * @param tile packed RGB24 pixels of the tile
   1188  */
   1189 static void
   1190 place_tile (struct Gallery *g,
   1191             unsigned int i,
   1192             const uint8_t *tile)
   1193 {
   1194   size_t sheet_stride = (size_t) g->cols * g->tw * 3;
   1195   size_t tile_stride = (size_t) g->tw * 3;
   1196   size_t x0 = (size_t) (i % g->cols) * g->tw * 3;
   1197   size_t y0 = (size_t) (i / g->cols) * g->th;
   1198   unsigned int y;
   1199 
   1200   for (y = 0; y < g->th; y++)
   1201     memcpy (&g->sheet[(y0 + y) * sheet_stride + x0],
   1202             &tile[y * tile_stride],
   1203             tile_stride);
   1204 }
   1205 
   1206 
   1207 /**
   1208  * Encode the finished sheet, degrading quality and then size until it
   1209  * fits into #EXTRACTOR_MEDIA_MAX_GALLERY_BYTES.
   1210  *
   1211  * @param ec extraction context, kept alive between attempts
   1212  * @param g the gallery
   1213  * @param opt plugin options
   1214  * @param[out] output set to the image, to be freed with av_free()
   1215  * @param[out] output_size set to the number of bytes in @a output
   1216  * @return 0 on success
   1217  */
   1218 static int
   1219 encode_gallery (struct EXTRACTOR_ExtractContext *ec,
   1220                 const struct Gallery *g,
   1221                 const struct EXTRACTOR_MediaOptions *opt,
   1222                 uint8_t **output,
   1223                 size_t *output_size)
   1224 {
   1225   static const int qscales[] = { JPEG_QSCALE, 10, 20, 31 };
   1226   AVFrame *src;
   1227   unsigned int w;
   1228   unsigned int h;
   1229   unsigned int shrink;
   1230   int r = -1;
   1231 
   1232   if (NULL == (src = av_frame_alloc ()))
   1233     return -1;
   1234   src->format = AV_PIX_FMT_RGB24;
   1235   src->width = (int) (g->cols * g->tw);
   1236   src->height = (int) (g->rows * g->th);
   1237   src->data[0] = g->sheet;
   1238   src->linesize[0] = (int) (g->cols * g->tw * 3);
   1239   w = g->cols * g->tw;
   1240   h = g->rows * g->th;
   1241   for (shrink = 0; shrink < 4; shrink++)
   1242   {
   1243     (void) ec->seek (ec->cls, 0, SEEK_CUR); /* keep-alive */
   1244     if (opt->png)
   1245     {
   1246       r = encode_image (src, w, h, 1, 0, GALLERY_PNG_LEVEL,
   1247                         EXTRACTOR_MEDIA_MAX_GALLERY_BYTES,
   1248                         output, output_size);
   1249     }
   1250     else
   1251     {
   1252       unsigned int q;
   1253 
   1254       for (q = 0; q < sizeof (qscales) / sizeof (qscales[0]); q++)
   1255       {
   1256         (void) ec->seek (ec->cls, 0, SEEK_CUR); /* keep-alive */
   1257         r = encode_image (src, w, h, 0, qscales[q], 0,
   1258                           EXTRACTOR_MEDIA_MAX_GALLERY_BYTES,
   1259                           output, output_size);
   1260         if (-2 != r)
   1261           break;
   1262       }
   1263     }
   1264     if (-2 != r)
   1265       break;
   1266     /* still too big: halve the sheet (i.e. every tile) */
   1267     w /= 2;
   1268     h /= 2;
   1269     if ( (w < 2) ||
   1270          (h < 2) )
   1271       break;
   1272   }
   1273   /* the frame only borrowed the sheet */
   1274   src->data[0] = NULL;
   1275   av_frame_free (&src);
   1276   return (0 == r) ? 0 : -1;
   1277 }
   1278 
   1279 
   1280 /**
   1281  * Build and emit a contact sheet for "gallery:N".
   1282  *
   1283  * @param io open I/O state
   1284  * @param ctx open video decoder
   1285  * @param stream index of the video stream
   1286  * @param opt plugin options
   1287  * @param[out] stop set to 1 if the application asked us to stop
   1288  * @param[out] touched set to 1 if we moved the demuxer
   1289  * @return 0 if we are done (sheet emitted, or the application asked us
   1290  *         to stop), 1 if the caller should fall back to a single
   1291  *         thumbnail
   1292  */
   1293 static int
   1294 make_gallery (struct EXTRACTOR_FFmpegIO *io,
   1295               AVCodecContext *ctx,
   1296               int stream,
   1297               const struct EXTRACTOR_MediaOptions *opt,
   1298               int *stop,
   1299               int *touched)
   1300 {
   1301   /* where to look instead of a flat frame, in eighths of a slice */
   1302   static const int shifts[] = { 0, 2, -2, 3, -3 };
   1303   const AVStream *st = io->fmt->streams[stream];
   1304   struct Gallery g;
   1305   AVFrame *frame = NULL;
   1306   uint8_t *image = NULL;
   1307   size_t image_size = 0;
   1308   uint64_t duration_ms;
   1309   uint64_t slice;
   1310   int64_t start_ts;
   1311   unsigned int n = opt->gallery;
   1312   unsigned int box = opt->size;
   1313   unsigned int got = 0;
   1314   unsigned int i;
   1315   int ret = 1;
   1316 
   1317   *stop = 0;
   1318   *touched = 0;
   1319   memset (&g, 0, sizeof (g));
   1320   if ( (AV_NOPTS_VALUE == io->fmt->duration) ||
   1321        (io->fmt->duration <= 0) )
   1322     return 1;
   1323   duration_ms = (uint64_t) io->fmt->duration / 1000;
   1324   if (duration_ms > MAX_CUE_TIME_MS)
   1325     duration_ms = MAX_CUE_TIME_MS;
   1326   if ( (0 == duration_ms) ||
   1327        (0 == n) )
   1328     return 1;
   1329   /* shrink the tiles until the sheet is of a size we can handle */
   1330   for (;;)
   1331   {
   1332     uint64_t sw;
   1333     uint64_t sh;
   1334 
   1335     if (! EXTRACTOR_media_thumbnail_size_ (ctx->width,
   1336                                            ctx->height,
   1337                                            box,
   1338                                            &g.tw,
   1339                                            &g.th))
   1340       return 1;
   1341     EXTRACTOR_media_gallery_layout_ (n,
   1342                                      g.tw,
   1343                                      g.th,
   1344                                      &g.cols,
   1345                                      &g.rows);
   1346     sw = (uint64_t) g.cols * g.tw;
   1347     sh = (uint64_t) g.rows * g.th;
   1348     if ( (sw * sh <= GALLERY_MAX_SHEET_PIXELS) &&
   1349          (sw * sh <= EXTRACTOR_MEDIA_MAX_PIXELS) &&
   1350          (sw <= EXTRACTOR_MEDIA_MAX_DIMENSION) &&
   1351          (sh <= EXTRACTOR_MEDIA_MAX_DIMENSION) )
   1352       break;
   1353     if (box <= 8)
   1354       return 1;
   1355     box = box * 3 / 4;
   1356     if (box < 8)
   1357       box = 8;
   1358   }
   1359   g.sheet = calloc ((size_t) g.cols * g.tw * g.rows * g.th, 3);
   1360   g.tile = malloc ((size_t) g.tw * g.th * 3);
   1361   g.best = malloc ((size_t) g.tw * g.th * 3);
   1362   if ( (NULL == g.sheet) ||
   1363        (NULL == g.tile) ||
   1364        (NULL == g.best) ||
   1365        (NULL == (frame = av_frame_alloc ())) )
   1366     goto cleanup;
   1367   if (AV_NOPTS_VALUE != st->start_time)
   1368     start_ts = st->start_time;
   1369   else if (AV_NOPTS_VALUE != io->fmt->start_time)
   1370     start_ts = av_rescale_q (io->fmt->start_time,
   1371                              AV_TIME_BASE_Q,
   1372                              st->time_base);
   1373   else
   1374     start_ts = 0;
   1375   slice = duration_ms / n;
   1376   for (i = 0; i < n; i++)
   1377   {
   1378     uint64_t base = EXTRACTOR_media_gallery_time_ (i,
   1379                                                    n,
   1380                                                    duration_ms);
   1381     uint64_t tried[sizeof (shifts) / sizeof (shifts[0])];
   1382     double best_score = -1.0;
   1383     unsigned int ntried = 0;
   1384     unsigned int a;
   1385     int placed = 0;
   1386 
   1387     for (a = 0; a < sizeof (shifts) / sizeof (shifts[0]); a++)
   1388     {
   1389       int64_t off = (int64_t) (slice / 8) * shifts[a];
   1390       uint64_t t;
   1391       unsigned int k;
   1392       double score;
   1393 
   1394       if (EXTRACTOR_media_deadline_expired_ (&io->deadline))
   1395         break;
   1396       if ( (off < 0) &&
   1397            ((uint64_t) -off > base) )
   1398         t = 0;
   1399       else
   1400         t = base + off;
   1401       if (t >= duration_ms)
   1402         t = duration_ms - 1;
   1403       for (k = 0; k < ntried; k++)
   1404         if (tried[k] == t)
   1405           break;
   1406       if (k < ntried)
   1407         continue;
   1408       tried[ntried++] = t;
   1409       *touched = 1;
   1410       if (0 != grab_tile (io,
   1411                           ctx,
   1412                           stream,
   1413                           start_ts,
   1414                           t,
   1415                           frame,
   1416                           &g))
   1417         continue;
   1418       if (! EXTRACTOR_media_frame_is_flat_ (g.tile,
   1419                                             g.tw,
   1420                                             g.th,
   1421                                             (size_t) g.tw * 3))
   1422       {
   1423         place_tile (&g, i, g.tile);
   1424         placed = 1;
   1425         break;
   1426       }
   1427       score = tile_score (g.tile,
   1428                           g.tw,
   1429                           g.th);
   1430       if (score > best_score)
   1431       {
   1432         best_score = score;
   1433         memcpy (g.best,
   1434                 g.tile,
   1435                 (size_t) g.tw * g.th * 3);
   1436       }
   1437     }
   1438     if ( (! placed) &&
   1439          (best_score >= 0.0) )
   1440     {
   1441       place_tile (&g, i, g.best);
   1442       placed = 1;
   1443     }
   1444     if (placed)
   1445       got++;
   1446     if (EXTRACTOR_media_deadline_expired_ (&io->deadline))
   1447       break;   /* emit what we have; the other cells stay black */
   1448   }
   1449   if (0 == got)
   1450     goto cleanup;
   1451   ret = 0;
   1452   if (0 != encode_gallery (io->ec,
   1453                            &g,
   1454                            opt,
   1455                            &image,
   1456                            &image_size))
   1457     goto cleanup;
   1458   if (0 != io->ec->proc (io->ec->cls,
   1459                          "thumbnailffmpeg",
   1460                          EXTRACTOR_METATYPE_THUMBNAIL,
   1461                          EXTRACTOR_METAFORMAT_BINARY,
   1462                          opt->png ? "image/png" : "image/jpeg",
   1463                          (const char *) image,
   1464                          image_size))
   1465     *stop = 1;
   1466 cleanup:
   1467   if (NULL != image)
   1468     av_free (image);
   1469   if (NULL != frame)
   1470     av_frame_free (&frame);
   1471   sws_freeContext (g.sws);
   1472   free (g.sheet);
   1473   free (g.tile);
   1474   free (g.best);
   1475   return ret;
   1476 }
   1477 
   1478 
   1479 /**
   1480  * Main method for the ffmpeg-thumbnailer plugin.
   1481  *
   1482  * @param ec extraction context
   1483  */
   1484 void
   1485 EXTRACTOR_thumbnailffmpeg_extract_method (struct EXTRACTOR_ExtractContext *ec);
   1486 
   1487 void
   1488 EXTRACTOR_thumbnailffmpeg_extract_method (struct EXTRACTOR_ExtractContext *ec)
   1489 {
   1490   struct EXTRACTOR_MediaOptions opt;
   1491   struct EXTRACTOR_FFmpegIO io;
   1492   AVCodecContext *ctx = NULL;
   1493   AVFrame *frame = NULL;
   1494   uint8_t *thumbnail = NULL;
   1495   size_t thumbnail_size = 0;
   1496   double target;
   1497   int stream;
   1498 
   1499   EXTRACTOR_media_options_parse_ (ec->config,
   1500                                   &opt);
   1501   EXTRACTOR_media_deadline_start_ (&io.deadline,
   1502                                    opt.deadline);
   1503   io.ec = ec;
   1504   if (! EXTRACTOR_media_is_media_ (ec, 0))
   1505     return;
   1506   if (0 != EXTRACTOR_ffmpeg_open_ (&io))
   1507     return;
   1508   /* subtitle metadata does not depend on there being a usable video
   1509      stream (think of a subtitles-only .mks), so do it first */
   1510   if (0 != emit_subtitle_languages (&io))
   1511     goto cleanup;
   1512   if ('\0' != opt.srt_lang[0])
   1513   {
   1514     int touched;
   1515 
   1516     if (0 != dump_srt (&io,
   1517                        &opt,
   1518                        &touched))
   1519       goto cleanup;
   1520     if (touched &&
   1521         (0 != EXTRACTOR_ffmpeg_reopen_ (&io)) )
   1522       return;
   1523   }
   1524   stream = EXTRACTOR_ffmpeg_open_stream_ (&io,
   1525                                           AVMEDIA_TYPE_VIDEO,
   1526                                           &ctx);
   1527   if (0 > stream)
   1528     goto cleanup;
   1529   if ( (ctx->width <= 0) ||
   1530        (ctx->height <= 0) ||
   1531        (ctx->width > EXTRACTOR_MEDIA_MAX_DIMENSION) ||
   1532        (ctx->height > EXTRACTOR_MEDIA_MAX_DIMENSION) ||
   1533        (((int64_t) ctx->width) * ctx->height > EXTRACTOR_MEDIA_MAX_PIXELS) )
   1534     goto cleanup;
   1535   if (0 != opt.gallery)
   1536   {
   1537     int stop;
   1538     int touched;
   1539 
   1540     if (0 == make_gallery (&io,
   1541                            ctx,
   1542                            stream,
   1543                            &opt,
   1544                            &stop,
   1545                            &touched))
   1546       goto cleanup;
   1547     /* no duration, or not a single frame: try for one thumbnail */
   1548     if (touched)
   1549     {
   1550       /* gallery seeks may have left the demuxer and decoder anywhere */
   1551       avcodec_free_context (&ctx);
   1552       if (0 != EXTRACTOR_ffmpeg_reopen_ (&io))
   1553         return;
   1554       stream = EXTRACTOR_ffmpeg_open_stream_ (&io,
   1555                                               AVMEDIA_TYPE_VIDEO,
   1556                                               &ctx);
   1557       if (0 > stream)
   1558         goto cleanup;
   1559     }
   1560   }
   1561   target = EXTRACTOR_ffmpeg_seek_ (&io,
   1562                                    stream,
   1563                                    ctx,
   1564                                    opt.offset);
   1565   if (NULL == (frame = av_frame_alloc ()))
   1566     goto cleanup;
   1567   if (0 != decode_frame_at (&io,
   1568                             ctx,
   1569                             stream,
   1570                             target,
   1571                             1,
   1572                             0,
   1573                             MAX_FRAMES,
   1574                             frame))
   1575     goto cleanup;
   1576   if (0 != encode_thumbnail (frame,
   1577                              &opt,
   1578                              &thumbnail,
   1579                              &thumbnail_size))
   1580     goto cleanup;
   1581   ec->proc (ec->cls,
   1582             "thumbnailffmpeg",
   1583             EXTRACTOR_METATYPE_THUMBNAIL,
   1584             EXTRACTOR_METAFORMAT_BINARY,
   1585             opt.png ? "image/png" : "image/jpeg",
   1586             (const char *) thumbnail,
   1587             thumbnail_size);
   1588 cleanup:
   1589   if (NULL != thumbnail)
   1590     av_free (thumbnail);
   1591   if (NULL != frame)
   1592     av_frame_free (&frame);
   1593   if (NULL != ctx)
   1594     avcodec_free_context (&ctx);
   1595   EXTRACTOR_ffmpeg_close_ (&io);
   1596 }
   1597 
   1598 
   1599 /**
   1600  * Keep the diagnostics of the media framework out of the output of the
   1601  * calling application; a decoder complaining about a broken file is not
   1602  * something the application asked to see.
   1603  *
   1604  * ("force-kill" would be tempting here, to get a fresh process for every
   1605  * file, but the host cannot know about that special without loading the
   1606  * plugin in-process: it only finds out that the child is gone once it has
   1607  * already handed it the next file, which is then silently lost.)
   1608  *
   1609  * @return special options for this plugin
   1610  */
   1611 const char *
   1612 EXTRACTOR_thumbnailffmpeg_options (void);
   1613 
   1614 const char *
   1615 EXTRACTOR_thumbnailffmpeg_options ()
   1616 {
   1617   return "close-stderr";
   1618 }
   1619 
   1620 
   1621 /**
   1622  * Silence FFmpeg's logging.
   1623  */
   1624 void __attribute__ ((constructor))
   1625 thumbnailffmpeg_init (void);
   1626 
   1627 void __attribute__ ((constructor))
   1628 thumbnailffmpeg_init ()
   1629 {
   1630   EXTRACTOR_ffmpeg_silence_ ();
   1631 }
   1632 
   1633 
   1634 /* end of thumbnailffmpeg_extractor.c */