"Fossies" - the Fresh Open Source Software Archive

Member "libextractor-1.11/src/plugins/thumbnailffmpeg_extractor.c" (30 Jan 2021, 22425 Bytes) of package /linux/privat/libextractor-1.11.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "thumbnailffmpeg_extractor.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.8_vs_1.9.

    1 /*
    2      This file is part of libextractor.
    3      Copyright Copyright (C) 2008, 2012 Heikki Lindholm 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 thumbnailffmpeg_extractor.c
   22  * @author Heikki Lindholm
   23  * @author Christian Grothoff
   24  * @brief this extractor produces a binary encoded
   25  * thumbnail of images and videos using the ffmpeg libs.
   26  *
   27  * This is a thumbnail extractor using the ffmpeg libraries that will eventually
   28  * support extracting thumbnails from both image and video files.
   29  *
   30  * Note that ffmpeg has a few issues:
   31  * (1) there are no recent official releases of the ffmpeg libs
   32  * (2) ffmpeg has a history of having security issues (parser is not robust)
   33  *
   34  *  So this plugin cannot be recommended for system with high security
   35  *requirements.
   36  */
   37 #include "platform.h"
   38 #include "extractor.h"
   39 #include <magic.h>
   40 
   41 #include <libavutil/avutil.h>
   42 #include <libavutil/imgutils.h>
   43 #include <libavformat/avformat.h>
   44 #include <libavcodec/avcodec.h>
   45 #include <libswscale/swscale.h>
   46 
   47 #if USE_JPEG
   48 #ifdef PIX_FMT_YUVJ420P
   49 #define PIX_OUTPUT_FORMAT PIX_FMT_YUVJ420P
   50 #else
   51 #define PIX_OUTPUT_FORMAT AV_PIX_FMT_YUVJ420P
   52 #endif
   53 #else
   54 #ifdef PIX_FMT_RGB24
   55 #define PIX_OUTPUT_FORMAT PIX_FMT_RGB24
   56 #else
   57 #define PIX_OUTPUT_FORMAT AV_PIX_FMT_RGB24
   58 #endif
   59 #endif
   60 
   61 /**
   62  * Set to 1 to use JPEG, PNG otherwise
   63  */
   64 #define USE_JPEG 1
   65 
   66 /**
   67  * Set to 1 to enable a output file for testing.
   68  */
   69 #define OUTPUT_FILE 0
   70 
   71 
   72 /**
   73  * Set to 1 to use jpeg.
   74  */
   75 #define DEBUG 0
   76 
   77 /**
   78  * max dimension in pixels for the thumbnail.
   79  */
   80 #define MAX_THUMB_DIMENSION 128
   81 
   82 /**
   83  * Maximum size in bytes for the thumbnail.
   84  */
   85 #define MAX_THUMB_BYTES (100 * 1024)
   86 
   87 /**
   88  * Number of bytes to feed to libav in one go.
   89  */
   90 #define BUFFER_SIZE (32 * 1024)
   91 
   92 /**
   93  * Number of bytes to feed to libav in one go, with padding (padding is zeroed).
   94  */
   95 #ifdef AV_INPUT_BUFFER_PADDING_SIZE
   96 #define PADDED_BUFFER_SIZE (BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE)
   97 #else
   98 /* legacy */
   99 #define PADDED_BUFFER_SIZE (BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE)
  100 #endif
  101 
  102 #ifndef AV_CODEC_FLAG_QSCALE
  103 #define AV_CODEC_FLAG_QSCALE CODEC_FLAG_QSCALE
  104 #endif
  105 
  106 
  107 /**
  108  * Global handle to MAGIC data.
  109  */
  110 static magic_t magic;
  111 
  112 
  113 /**
  114  * Read callback.
  115  *
  116  * @param opaque the 'struct EXTRACTOR_ExtractContext'
  117  * @param buf where to write data
  118  * @param buf_size how many bytes to read
  119  * @return -1 on error (or for unknown file size)
  120  */
  121 static int
  122 read_cb (void *opaque,
  123          uint8_t *buf,
  124          int buf_size)
  125 {
  126   struct EXTRACTOR_ExtractContext *ec = opaque;
  127   void *data;
  128   ssize_t ret;
  129 
  130   ret = ec->read (ec->cls, &data, buf_size);
  131   if (ret <= 0)
  132     return ret;
  133   memcpy (buf, data, ret);
  134   return ret;
  135 }
  136 
  137 
  138 /**
  139  * Seek callback.
  140  *
  141  * @param opaque the 'struct EXTRACTOR_ExtractContext'
  142  * @param offset where to seek
  143  * @param whence how to seek; AVSEEK_SIZE to return file size without seeking
  144  * @return -1 on error (or for unknown file size)
  145  */
  146 static int64_t
  147 seek_cb (void *opaque,
  148          int64_t offset,
  149          int whence)
  150 {
  151   struct EXTRACTOR_ExtractContext *ec = opaque;
  152 
  153   if (AVSEEK_SIZE == whence)
  154     return ec->get_size (ec->cls);
  155   return ec->seek (ec->cls, offset, whence);
  156 }
  157 
  158 
  159 /**
  160  * Rescale and encode a PNG thumbnail.
  161  *
  162  * @param src_width source image width
  163  * @param src_height source image height
  164  * @param src_stride
  165  * @param src_pixfmt
  166  * @param src_data source data
  167  * @param dst_width desired thumbnail width
  168  * @param dst_height desired thumbnail height
  169  * @param output_data where to store the resulting PNG data
  170  * @param output_max_size maximum size of result that is allowed
  171  * @return the number of bytes used, 0 on error
  172  */
  173 static size_t
  174 create_thumbnail (AVCodecContext *pCodecCtx, int src_width, int src_height,
  175                   int src_stride[],
  176                   enum AVPixelFormat src_pixfmt,
  177                   const uint8_t *const src_data[],
  178                   int dst_width, int dst_height,
  179                   uint8_t **output_data,
  180                   size_t output_max_size)
  181 {
  182   AVCodecContext *encoder_codec_ctx;
  183   AVDictionary *opts;
  184   AVCodec *encoder_codec;
  185   struct SwsContext *scaler_ctx;
  186   AVFrame *dst_frame;
  187   uint8_t *dst_buffer;
  188   uint8_t *encoder_output_buffer;
  189   size_t encoder_output_buffer_size;
  190   int err;
  191 
  192   AVPacket pkt;
  193   av_init_packet (&pkt);
  194   pkt.data = NULL;
  195   pkt.size = 0;
  196   int gotPacket;
  197 #if USE_JPEG
  198   if (NULL == (encoder_codec = avcodec_find_encoder (AV_CODEC_ID_MJPEG)))
  199 #else
  200   if (NULL == (encoder_codec = avcodec_find_encoder_by_name ("png")))
  201 #endif
  202   {
  203 #if DEBUG
  204     fprintf (stderr,
  205              "Couldn't find a encoder\n");
  206 #endif
  207     return 0;
  208   }
  209 
  210   /* NOTE: the scaler will be used even if the src and dst image dimensions
  211    * match, because the scaler will also perform colour space conversion */
  212   if (NULL ==
  213       (scaler_ctx =
  214          sws_getContext (src_width, src_height, src_pixfmt,
  215                          dst_width, dst_height,
  216                          PIX_OUTPUT_FORMAT,
  217                          SWS_BILINEAR, NULL, NULL, NULL)))
  218   {
  219 #if DEBUG
  220     fprintf (stderr,
  221              "Failed to get a scaler context\n");
  222 #endif
  223     return 0;
  224   }
  225 
  226   dst_frame = av_frame_alloc ();
  227   if (NULL == dst_frame)
  228   {
  229 #if DEBUG
  230     fprintf (stderr,
  231              "Failed to allocate the destination image frame\n");
  232 #endif
  233     sws_freeContext (scaler_ctx);
  234     return 0;
  235   }
  236   if (NULL == (dst_buffer =
  237                  av_malloc (av_image_get_buffer_size (PIX_OUTPUT_FORMAT,
  238                                                       dst_width,
  239                                                       dst_height,
  240                                                       1))))
  241   {
  242 #if DEBUG
  243     fprintf (stderr,
  244              "Failed to allocate the destination image buffer\n");
  245 #endif
  246     av_frame_free (&dst_frame);
  247     sws_freeContext (scaler_ctx);
  248     return 0;
  249   }
  250   av_image_fill_arrays (dst_frame->data,
  251                         dst_frame->linesize,
  252                         dst_buffer,
  253                         PIX_OUTPUT_FORMAT,
  254                         dst_width,
  255                         dst_height,
  256                         1);
  257   sws_scale (scaler_ctx,
  258              src_data,
  259              src_stride,
  260              0, src_height,
  261              dst_frame->data,
  262              dst_frame->linesize);
  263 
  264   encoder_output_buffer_size = output_max_size;
  265   if (NULL == (encoder_output_buffer = av_malloc (encoder_output_buffer_size)))
  266   {
  267 #if DEBUG
  268     fprintf (stderr,
  269              "Failed to allocate the encoder output buffer\n");
  270 #endif
  271     av_free (dst_buffer);
  272     av_frame_free (&dst_frame);
  273     sws_freeContext (scaler_ctx);
  274     return 0;
  275   }
  276 
  277   if (NULL == (encoder_codec_ctx = avcodec_alloc_context3 (encoder_codec)))
  278   {
  279 #if DEBUG
  280     fprintf (stderr,
  281              "Failed to allocate the encoder codec context\n");
  282 #endif
  283     av_free (encoder_output_buffer);
  284     av_free (dst_buffer);
  285     av_frame_free (&dst_frame);
  286     sws_freeContext (scaler_ctx);
  287     return 0;
  288   }
  289   encoder_codec_ctx->width = dst_width;
  290   encoder_codec_ctx->height = dst_height;
  291 #if USE_JPEG
  292   encoder_codec_ctx->bit_rate      = pCodecCtx->bit_rate;
  293   encoder_codec_ctx->codec_id      = AV_CODEC_ID_MJPEG;
  294   encoder_codec_ctx->codec_type    = AVMEDIA_TYPE_VIDEO;
  295   encoder_codec_ctx->time_base.num = pCodecCtx->time_base.num;
  296   encoder_codec_ctx->time_base.den = pCodecCtx->time_base.den;
  297   encoder_codec_ctx->pix_fmt = PIX_OUTPUT_FORMAT;
  298 #else
  299   encoder_codec_ctx->pix_fmt = PIX_OUTPUT_FORMAT;
  300 #endif
  301 
  302   opts = NULL;
  303   if (avcodec_open2 (encoder_codec_ctx, encoder_codec, &opts) < 0)
  304   {
  305 #if DEBUG
  306     fprintf (stderr,
  307              "Failed to open the encoder\n");
  308 #endif
  309     avcodec_free_context (&encoder_codec_ctx);
  310     av_free (encoder_output_buffer);
  311     av_free (dst_buffer);
  312     av_frame_free (&dst_frame);
  313     sws_freeContext  (scaler_ctx);
  314     return 0;
  315   }
  316 
  317 
  318 #ifdef USE_JPEG
  319 #if FF_API_MPV_OPT
  320   encoder_codec_ctx->mb_lmin        = encoder_codec_ctx->lmin =
  321     encoder_codec_ctx->qmin * FF_QP2LAMBDA;
  322   encoder_codec_ctx->mb_lmax        = encoder_codec_ctx->lmax =
  323     encoder_codec_ctx->qmax * FF_QP2LAMBDA;
  324 #else
  325   encoder_codec_ctx->mb_lmin = encoder_codec_ctx->qmin * FF_QP2LAMBDA;
  326   encoder_codec_ctx->mb_lmax = encoder_codec_ctx->qmax * FF_QP2LAMBDA;
  327 #endif
  328   encoder_codec_ctx->flags          = AV_CODEC_FLAG_QSCALE;
  329   encoder_codec_ctx->global_quality = encoder_codec_ctx->qmin * FF_QP2LAMBDA;
  330 
  331   dst_frame->pts     = 1;
  332   dst_frame->quality = encoder_codec_ctx->global_quality;
  333 #endif
  334 
  335   err = avcodec_encode_video2 (encoder_codec_ctx,
  336                                &pkt,
  337                                dst_frame, &gotPacket);
  338 
  339   if (err < 0)
  340     goto cleanup;
  341   err = pkt.size;
  342   memcpy (encoder_output_buffer,pkt.data, pkt.size);
  343 
  344   av_packet_unref (&pkt);
  345 cleanup:
  346   av_dict_free (&opts);
  347   avcodec_close (encoder_codec_ctx);
  348   avcodec_free_context (&encoder_codec_ctx);
  349   av_free (dst_buffer);
  350   av_frame_free (&dst_frame);
  351   sws_freeContext (scaler_ctx);
  352   *output_data = encoder_output_buffer;
  353 
  354   return err < 0 ? 0 : err;
  355 }
  356 
  357 
  358 /**
  359  * calculate the thumbnail dimensions, taking pixel aspect into account
  360  *
  361  * @param src_width source image width
  362  * @param src_height source image height
  363  * @param src_sar_num
  364  * @param src_sar_den
  365  * @param dst_width desired thumbnail width (set)
  366  * @param dst_height desired thumbnail height (set)
  367   */
  368 static void
  369 calculate_thumbnail_dimensions (int src_width,
  370                                 int src_height,
  371                                 int src_sar_num,
  372                                 int src_sar_den,
  373                                 int *dst_width,
  374                                 int *dst_height)
  375 {
  376   if ( (src_sar_num <= 0) || (src_sar_den <= 0) )
  377   {
  378     src_sar_num = 1;
  379     src_sar_den = 1;
  380   }
  381   if ((src_width * src_sar_num) / src_sar_den > src_height)
  382   {
  383     *dst_width = MAX_THUMB_DIMENSION;
  384     *dst_height = (*dst_width * src_height)
  385                   / ((src_width * src_sar_num) / src_sar_den);
  386   }
  387   else
  388   {
  389     *dst_height = MAX_THUMB_DIMENSION;
  390     *dst_width = (*dst_height
  391                   * ((src_width * src_sar_num) / src_sar_den))
  392                  / src_height;
  393   }
  394   if (*dst_width < 8)
  395     *dst_width = 8;
  396   if (*dst_height < 1)
  397     *dst_height = 1;
  398 #if DEBUG
  399   fprintf (stderr,
  400            "Thumbnail dimensions: %d %d\n",
  401            *dst_width, *dst_height);
  402 #endif
  403 }
  404 
  405 
  406 #define ENUM_CODEC_ID enum AVCodecID
  407 
  408 /**
  409  * Perform thumbnailing when the input is an image.
  410  *
  411  * @param image_codec_id ffmpeg codec for the image format
  412  * @param ec extraction context to use
  413  */
  414 static void
  415 extract_image (ENUM_CODEC_ID image_codec_id,
  416                struct EXTRACTOR_ExtractContext *ec)
  417 {
  418   AVDictionary *opts;
  419   AVCodecContext *codec_ctx;
  420   AVCodec *codec;
  421   AVPacket avpkt;
  422   AVFrame *frame;
  423   uint8_t *encoded_thumbnail;
  424   int thumb_width;
  425   int thumb_height;
  426   int err;
  427   int frame_finished;
  428   ssize_t iret;
  429   void *data;
  430   unsigned char padded_data[PADDED_BUFFER_SIZE];
  431 
  432   if (NULL == (codec = avcodec_find_decoder (image_codec_id)))
  433   {
  434 #if DEBUG
  435     fprintf (stderr,
  436              "No suitable codec found\n");
  437 #endif
  438     return;
  439   }
  440   if (NULL == (codec_ctx = avcodec_alloc_context3 (codec)))
  441   {
  442 #if DEBUG
  443     fprintf (stderr,
  444              "Failed to allocate codec context\n");
  445 #endif
  446     return;
  447   }
  448   opts = NULL;
  449   if (0 != avcodec_open2 (codec_ctx, codec, &opts))
  450   {
  451 #if DEBUG
  452     fprintf (stderr,
  453              "Failed to open image codec\n");
  454 #endif
  455     avcodec_free_context (&codec_ctx);
  456     return;
  457   }
  458   av_dict_free (&opts);
  459   frame = av_frame_alloc ();
  460   if (NULL == frame)
  461   {
  462 #if DEBUG
  463     fprintf (stderr,
  464              "Failed to allocate frame\n");
  465 #endif
  466     avcodec_close (codec_ctx);
  467     avcodec_free_context (&codec_ctx);
  468     return;
  469   }
  470 
  471   frame_finished = 0;
  472   while (! frame_finished)
  473   {
  474     if (0 >= (iret = ec->read (ec->cls,
  475                                &data,
  476                                BUFFER_SIZE)))
  477       break;
  478     memcpy (padded_data, data, iret);
  479     memset (&padded_data[iret], 0, PADDED_BUFFER_SIZE - iret);
  480     av_init_packet (&avpkt);
  481     avpkt.data = padded_data;
  482     avpkt.size = iret;
  483     avcodec_decode_video2 (codec_ctx, frame, &frame_finished, &avpkt);
  484   }
  485   if (! frame_finished)
  486   {
  487 #if DEBUG
  488     fprintf (stderr,
  489              "Failed to decode a complete frame\n");
  490 #endif
  491     av_frame_free (&frame);
  492     avcodec_close (codec_ctx);
  493     avcodec_free_context (&codec_ctx);
  494     return;
  495   }
  496   calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height,
  497                                   codec_ctx->sample_aspect_ratio.num,
  498                                   codec_ctx->sample_aspect_ratio.den,
  499                                   &thumb_width, &thumb_height);
  500 
  501   err = create_thumbnail (codec_ctx, codec_ctx->width, codec_ctx->height,
  502                           frame->linesize, codec_ctx->pix_fmt,
  503                           (const uint8_t *const*) frame->data,
  504                           thumb_width, thumb_height,
  505                           &encoded_thumbnail, MAX_THUMB_BYTES);
  506   if (err > 0)
  507   {
  508     ec->proc (ec->cls,
  509               "thumbnailffmpeg",
  510               EXTRACTOR_METATYPE_THUMBNAIL,
  511               EXTRACTOR_METAFORMAT_BINARY,
  512               "image/png",
  513               (const char*) encoded_thumbnail,
  514               err);
  515 
  516     #if OUTPUT_FILE
  517     FILE *f;
  518       #ifdef USE_JPEG
  519     f = fopen ("thumb.jpg", "wb");
  520       #else
  521     f = fopen ("thumb.png", "wb");
  522       #endif
  523     if (! f)
  524     {
  525       fprintf (stderr, "Could not open %s\n", "file");
  526       exit (1);
  527     }
  528 
  529     fwrite (encoded_thumbnail, 1, err, f);
  530     fclose (f);
  531 
  532         #endif
  533 
  534 
  535     av_free (encoded_thumbnail);
  536   }
  537   av_frame_free (&frame);
  538   avcodec_close (codec_ctx);
  539   avcodec_free_context (&codec_ctx);
  540 }
  541 
  542 
  543 /**
  544  * Perform thumbnailing when the input is a video
  545  *
  546  * @param ec extraction context to use
  547  */
  548 static void
  549 extract_video (struct EXTRACTOR_ExtractContext *ec)
  550 {
  551   AVPacket packet;
  552   AVIOContext *io_ctx;
  553   struct AVFormatContext *format_ctx;
  554   AVCodecContext *codec_ctx;
  555   AVCodecParameters *codecpar;
  556   AVCodec *codec;
  557   AVDictionary *options;
  558   AVFrame *frame;
  559   uint8_t *encoded_thumbnail;
  560   int video_stream_index;
  561   int thumb_width;
  562   int thumb_height;
  563   int i;
  564   int err;
  565   int frame_finished;
  566   unsigned char *iob;
  567   int duration;
  568 
  569   if (NULL == (iob = av_malloc (16 * 1024)))
  570     return;
  571   if (NULL == (io_ctx = avio_alloc_context (iob,
  572                                             16 * 1024,
  573                                             0, ec,
  574                                             &read_cb,
  575                                             NULL /* no writing */,
  576                                             &seek_cb)))
  577   {
  578     av_free (iob);
  579     return;
  580   }
  581   if (NULL == (format_ctx = avformat_alloc_context ()))
  582   {
  583     av_free (io_ctx);
  584     return;
  585   }
  586   format_ctx->pb = io_ctx;
  587   options = NULL;
  588   if (0 != avformat_open_input (&format_ctx, "<no file>", NULL, &options))
  589   {
  590     av_free (io_ctx);
  591     return;
  592   }
  593   av_dict_free (&options);
  594   if (0 > avformat_find_stream_info (format_ctx, NULL))
  595   {
  596 #if DEBUG
  597     fprintf (stderr,
  598              "Failed to read stream info\n");
  599 #endif
  600     avformat_close_input (&format_ctx);
  601     av_free (io_ctx);
  602     return;
  603   }
  604   codec = NULL;
  605   codec_ctx = NULL;
  606   video_stream_index = -1;
  607   for (i = 0; i<format_ctx->nb_streams; i++)
  608   {
  609     codecpar = format_ctx->streams[i]->codecpar;
  610     codec_ctx = format_ctx->streams[i]->codec;
  611     if (AVMEDIA_TYPE_VIDEO != codec_ctx->codec_type)
  612       continue;
  613     if (NULL == (codec = avcodec_find_decoder (codecpar->codec_id)))
  614       continue;
  615     options = NULL;
  616     if (0 != (err = avcodec_open2 (codec_ctx, codec, &options)))
  617     {
  618       codec = NULL;
  619       continue;
  620     }
  621     av_dict_free (&options);
  622     video_stream_index = i;
  623     break;
  624   }
  625   if ( (-1 == video_stream_index) ||
  626        (0 == codec_ctx->width) ||
  627        (0 == codec_ctx->height) )
  628   {
  629 #if DEBUG
  630     fprintf (stderr,
  631              "No video streams or no suitable codec found\n");
  632 #endif
  633     if (NULL != codec)
  634       avcodec_close (codec_ctx);
  635     avformat_close_input (&format_ctx);
  636     av_free (io_ctx);
  637     return;
  638   }
  639 
  640   frame = av_frame_alloc ();
  641   if (NULL == frame)
  642   {
  643 #if DEBUG
  644     fprintf (stderr,
  645              "Failed to allocate frame\n");
  646 #endif
  647     avcodec_close (codec_ctx);
  648     avformat_close_input (&format_ctx);
  649     av_free (io_ctx);
  650     return;
  651   }
  652 
  653   if (format_ctx->duration == AV_NOPTS_VALUE)
  654   {
  655     duration = -1;
  656 #if DEBUG
  657     fprintf (stderr,
  658              "Duration unknown\n");
  659 #endif
  660   }
  661   else
  662   {
  663     duration = format_ctx->duration;
  664   }
  665 
  666   /* if duration is known, seek to first tried,
  667    * else use 10 sec into stream */
  668 
  669   if (-1 != duration)
  670     err = av_seek_frame (format_ctx, -1, (duration / 3), 0);
  671   else
  672     err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0);
  673 
  674   if (err >= 0)
  675     avcodec_flush_buffers (codec_ctx);
  676   frame_finished = 0;
  677 
  678   while (1)
  679   {
  680     err = av_read_frame (format_ctx, &packet);
  681     if (err < 0)
  682       break;
  683     if (packet.stream_index == video_stream_index)
  684     {
  685       avcodec_decode_video2 (codec_ctx,
  686                              frame,
  687                              &frame_finished,
  688                              &packet);
  689       if (frame_finished && frame->key_frame)
  690       {
  691         av_packet_unref (&packet);
  692         break;
  693       }
  694     }
  695     av_packet_unref (&packet);
  696   }
  697 
  698   if (! frame_finished)
  699   {
  700 #if DEBUG
  701     fprintf (stderr,
  702              "Failed to decode a complete frame\n");
  703 #endif
  704     av_frame_free (&frame);
  705     avcodec_close (codec_ctx);
  706     avformat_close_input (&format_ctx);
  707     av_free (io_ctx);
  708     return;
  709   }
  710   calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height,
  711                                   codec_ctx->sample_aspect_ratio.num,
  712                                   codec_ctx->sample_aspect_ratio.den,
  713                                   &thumb_width, &thumb_height);
  714 
  715   err = create_thumbnail (codec_ctx, codec_ctx->width, codec_ctx->height,
  716                           frame->linesize, codec_ctx->pix_fmt,
  717                           (const uint8_t*const *) frame->data,
  718                           thumb_width, thumb_height,
  719                           &encoded_thumbnail, MAX_THUMB_BYTES);
  720   if (err > 0)
  721   {
  722     ec->proc (ec->cls,
  723               "thumbnailffmpeg",
  724               EXTRACTOR_METATYPE_THUMBNAIL,
  725               EXTRACTOR_METAFORMAT_BINARY,
  726               "image/png",
  727               (const char*) encoded_thumbnail,
  728               err);
  729 #if OUTPUT_FILE
  730     FILE *f;
  731 #ifdef USE_JPEG
  732     f = fopen ("thumb.jpg", "wb");
  733 #else
  734     f = fopen ("thumb.png", "wb");
  735 #endif
  736     if (! f)
  737     {
  738       fprintf (stderr, "Could not open %s\n", "file");
  739       exit (1);
  740     }
  741 
  742     fwrite (encoded_thumbnail, 1, err, f);
  743     fclose (f);
  744 #endif
  745     av_free (encoded_thumbnail);
  746   }
  747   av_frame_free (&frame);
  748   avcodec_close (codec_ctx);
  749   avformat_close_input (&format_ctx);
  750   av_free (io_ctx);
  751 }
  752 
  753 
  754 /**
  755  * Pair of mime type and ffmpeg decoder ID.
  756  */
  757 struct MIMEToDecoderMapping
  758 {
  759   /**
  760    * String for a mime type.
  761    */
  762   const char *mime_type;
  763 
  764   /**
  765    * Corresponding ffmpeg decoder ID.
  766    */
  767   ENUM_CODEC_ID codec_id;
  768 };
  769 
  770 
  771 /**
  772  * map MIME image types to an ffmpeg decoder
  773  */
  774 static const struct MIMEToDecoderMapping m2d_map[] = {
  775 
  776 #if LIBAVCODEC_BUILD >= AV_VERSION_INT (54,25,0)
  777   { "image/x-bmp", AV_CODEC_ID_BMP },
  778   { "image/gif", AV_CODEC_ID_GIF },
  779   { "image/jpeg", AV_CODEC_ID_MJPEG },
  780   { "image/png", AV_CODEC_ID_PNG },
  781   { "image/x-png", AV_CODEC_ID_PNG },
  782   { "image/x-portable-pixmap", AV_CODEC_ID_PPM },
  783   { NULL, AV_CODEC_ID_NONE }
  784 #else
  785   { "image/x-bmp", CODEC_ID_BMP },
  786   { "image/gif", CODEC_ID_GIF },
  787   { "image/jpeg", CODEC_ID_MJPEG },
  788   { "image/png", CODEC_ID_PNG },
  789   { "image/x-png", CODEC_ID_PNG },
  790   { "image/x-portable-pixmap", CODEC_ID_PPM },
  791   { NULL, CODEC_ID_NONE }
  792 #endif
  793 
  794 };
  795 
  796 
  797 /**
  798  * Main method for the ffmpeg-thumbnailer plugin.
  799  *
  800  * @param ec extraction context
  801  */
  802 void
  803 EXTRACTOR_thumbnailffmpeg_extract_method (struct EXTRACTOR_ExtractContext *ec)
  804 {
  805   unsigned int i;
  806   ssize_t iret;
  807   void *data;
  808   const char *mime;
  809 
  810   if (-1 == (iret = ec->read (ec->cls,
  811                               &data,
  812                               16 * 1024)))
  813     return;
  814   if (NULL == (mime = magic_buffer (magic, data, iret)))
  815     return;
  816   if (0 != ec->seek (ec->cls, 0, SEEK_SET))
  817     return;
  818   for (i = 0; NULL != m2d_map[i].mime_type; i++)
  819     if (0 == strcmp (m2d_map[i].mime_type, mime))
  820     {
  821       extract_image (m2d_map[i].codec_id, ec);
  822       return;
  823     }
  824   extract_video (ec);
  825 }
  826 
  827 
  828 /**
  829  * This plugin sometimes is installed under the alias 'thumbnail'.
  830  * So we need to provide a second entry method.
  831  *
  832  * @param ec extraction context
  833  */
  834 void
  835 EXTRACTOR_thumbnail_extract_method (struct EXTRACTOR_ExtractContext *ec)
  836 {
  837   EXTRACTOR_thumbnailffmpeg_extract_method (ec);
  838 }
  839 
  840 
  841 /**
  842  * Log callback.  Does nothing.
  843  *
  844  * @param ptr NULL
  845  * @param level log level
  846  * @param format format string
  847  * @param ap arguments for format
  848  */
  849 static void
  850 thumbnailffmpeg_av_log_callback (void*ptr,
  851                                  int level,
  852                                  const char *format,
  853                                  va_list ap)
  854 {
  855 #if DEBUG
  856   vfprintf (stderr, format, ap);
  857 #endif
  858 }
  859 
  860 
  861 /**
  862  * Initialize av-libs and load magic file.
  863  */
  864 void __attribute__ ((constructor))
  865 thumbnailffmpeg_lib_init (void)
  866 {
  867   av_log_set_callback (&thumbnailffmpeg_av_log_callback);
  868   magic = magic_open (MAGIC_MIME_TYPE);
  869   if (0 != magic_load (magic, NULL))
  870   {
  871     /* FIXME: how to deal with errors? */
  872   }
  873 }
  874 
  875 
  876 /**
  877  * Destructor for the library, cleans up.
  878  */
  879 void __attribute__ ((destructor))
  880 thumbnailffmpeg_ltdl_fini ()
  881 {
  882   if (NULL != magic)
  883   {
  884     magic_close (magic);
  885     magic = NULL;
  886   }
  887 }
  888 
  889 
  890 /* end of thumbnailffmpeg_extractor.c */