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 */