LCOV - code coverage report
Current view: top level - src/libavformat - apngenc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 111 130 85.4 %
Date: 2017-06-23 19:00:59 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :  * APNG muxer
       3             :  * Copyright (c) 2015 Donny Yang
       4             :  *
       5             :  * first version by Donny Yang <work@kota.moe>
       6             :  *
       7             :  * This file is part of FFmpeg.
       8             :  *
       9             :  * FFmpeg is free software; you can redistribute it and/or
      10             :  * modify it under the terms of the GNU Lesser General Public
      11             :  * License as published by the Free Software Foundation; either
      12             :  * version 2.1 of the License, or (at your option) any later version.
      13             :  *
      14             :  * FFmpeg is distributed in the hope that it will be useful,
      15             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17             :  * Lesser General Public License for more details.
      18             :  *
      19             :  * You should have received a copy of the GNU Lesser General Public
      20             :  * License along with FFmpeg; if not, write to the Free Software
      21             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      22             :  */
      23             : 
      24             : #include "avformat.h"
      25             : #include "internal.h"
      26             : #include "libavutil/avassert.h"
      27             : #include "libavutil/crc.h"
      28             : #include "libavutil/intreadwrite.h"
      29             : #include "libavutil/log.h"
      30             : #include "libavutil/opt.h"
      31             : #include "libavcodec/png.h"
      32             : #include "libavcodec/apng.h"
      33             : 
      34             : typedef struct APNGMuxContext {
      35             :     AVClass *class;
      36             : 
      37             :     uint32_t plays;
      38             :     AVRational last_delay;
      39             : 
      40             :     uint64_t acTL_offset;
      41             :     uint32_t frame_number;
      42             : 
      43             :     AVPacket *prev_packet;
      44             :     AVRational prev_delay;
      45             : 
      46             :     int framerate_warned;
      47             : 
      48             :     uint8_t *extra_data;
      49             :     int extra_data_size;
      50             : } APNGMuxContext;
      51             : 
      52          54 : static uint8_t *apng_find_chunk(uint32_t tag, uint8_t *buf, size_t length)
      53             : {
      54             :     size_t b;
      55          60 :     for (b = 0; b < length; b += AV_RB32(buf + b) + 12)
      56          58 :         if (AV_RB32(&buf[b + 4]) == tag)
      57          52 :             return &buf[b];
      58           2 :     return NULL;
      59             : }
      60             : 
      61           5 : static void apng_write_chunk(AVIOContext *io_context, uint32_t tag,
      62             :                              uint8_t *buf, size_t length)
      63             : {
      64           5 :     const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE);
      65           5 :     uint32_t crc = ~0U;
      66             :     uint8_t tagbuf[4];
      67             : 
      68           5 :     av_assert0(crc_table);
      69             : 
      70           5 :     avio_wb32(io_context, length);
      71           5 :     AV_WB32(tagbuf, tag);
      72           5 :     crc = av_crc(crc_table, crc, tagbuf, 4);
      73           5 :     avio_wb32(io_context, tag);
      74           5 :     if (length > 0) {
      75           2 :         crc = av_crc(crc_table, crc, buf, length);
      76           2 :         avio_write(io_context, buf, length);
      77             :     }
      78           5 :     avio_wb32(io_context, ~crc);
      79           5 : }
      80             : 
      81           3 : static int apng_write_header(AVFormatContext *format_context)
      82             : {
      83           3 :     APNGMuxContext *apng = format_context->priv_data;
      84           3 :     AVCodecParameters *par = format_context->streams[0]->codecpar;
      85             : 
      86           6 :     if (format_context->nb_streams != 1 ||
      87           6 :         format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
      88           3 :         format_context->streams[0]->codecpar->codec_id   != AV_CODEC_ID_APNG) {
      89           0 :         av_log(format_context, AV_LOG_ERROR,
      90             :                "APNG muxer supports only a single video APNG stream.\n");
      91           0 :         return AVERROR(EINVAL);
      92             :     }
      93             : 
      94           3 :     if (apng->last_delay.num > USHRT_MAX || apng->last_delay.den > USHRT_MAX) {
      95           0 :         av_reduce(&apng->last_delay.num, &apng->last_delay.den,
      96           0 :                   apng->last_delay.num, apng->last_delay.den, USHRT_MAX);
      97           0 :         av_log(format_context, AV_LOG_WARNING,
      98             :                "Last frame delay is too precise. Reducing to %d/%d (%f).\n",
      99           0 :                apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den);
     100             :     }
     101             : 
     102           3 :     avio_wb64(format_context->pb, PNGSIG);
     103             :     // Remaining headers are written when they are copied from the encoder
     104             : 
     105           3 :     if (par->extradata_size) {
     106           1 :         apng->extra_data = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
     107           1 :         if (!apng->extra_data)
     108           0 :             return AVERROR(ENOMEM);
     109           1 :         apng->extra_data_size = par->extradata_size;
     110           1 :         memcpy(apng->extra_data, par->extradata, par->extradata_size);
     111             :     }
     112             : 
     113           3 :     return 0;
     114             : }
     115             : 
     116          51 : static int flush_packet(AVFormatContext *format_context, AVPacket *packet)
     117             : {
     118          51 :     APNGMuxContext *apng = format_context->priv_data;
     119          51 :     AVIOContext *io_context = format_context->pb;
     120          51 :     AVStream *codec_stream = format_context->streams[0];
     121          51 :     uint8_t *side_data = NULL;
     122          51 :     int side_data_size = 0;
     123             : 
     124          51 :     av_assert0(apng->prev_packet);
     125             : 
     126          51 :     side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size);
     127             : 
     128          51 :     if (side_data_size) {
     129           2 :         av_freep(&apng->extra_data);
     130           2 :         apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE);
     131           2 :         if (!apng->extra_data)
     132           0 :             return AVERROR(ENOMEM);
     133           2 :         apng->extra_data_size = side_data_size;
     134           2 :         memcpy(apng->extra_data, side_data, apng->extra_data_size);
     135             :     }
     136             : 
     137          52 :     if (apng->frame_number == 0 && !packet) {
     138             :         uint8_t *existing_acTL_chunk;
     139             :         uint8_t *existing_fcTL_chunk;
     140             : 
     141           1 :         av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n");
     142             : 
     143             :         // Write normal PNG headers without acTL chunk
     144           1 :         existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
     145           1 :         if (existing_acTL_chunk) {
     146           0 :             uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12;
     147           0 :             avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data);
     148           0 :             avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL);
     149             :         } else {
     150           1 :             avio_write(io_context, apng->extra_data, apng->extra_data_size);
     151             :         }
     152             : 
     153             :         // Write frame data without fcTL chunk
     154           1 :         existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
     155           1 :         if (existing_fcTL_chunk) {
     156           1 :             uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12;
     157           1 :             avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data);
     158           1 :             avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL);
     159             :         } else {
     160           0 :             avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
     161             :         }
     162             :     } else {
     163             :         uint8_t *existing_fcTL_chunk;
     164             : 
     165          50 :         if (apng->frame_number == 0) {
     166             :             uint8_t *existing_acTL_chunk;
     167             : 
     168             :             // Write normal PNG headers
     169           2 :             avio_write(io_context, apng->extra_data, apng->extra_data_size);
     170             : 
     171           2 :             existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size);
     172           2 :             if (!existing_acTL_chunk) {
     173             :                 uint8_t buf[8];
     174             :                 // Write animation control header
     175           1 :                 apng->acTL_offset = avio_tell(io_context);
     176           1 :                 AV_WB32(buf, UINT_MAX); // number of frames (filled in later)
     177           1 :                 AV_WB32(buf + 4, apng->plays);
     178           1 :                 apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
     179             :             }
     180             :         }
     181             : 
     182          50 :         existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size);
     183          50 :         if (existing_fcTL_chunk) {
     184             :             AVRational delay;
     185             : 
     186          50 :             existing_fcTL_chunk += 8;
     187          50 :             delay.num = AV_RB16(existing_fcTL_chunk + 20);
     188          50 :             delay.den = AV_RB16(existing_fcTL_chunk + 22);
     189             : 
     190          50 :             if (delay.num == 0 && delay.den == 0) {
     191          25 :                 if (packet) {
     192          24 :                     int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num;
     193          24 :                     int64_t delay_den_raw = codec_stream->time_base.den;
     194          24 :                     if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, USHRT_MAX) &&
     195           0 :                         !apng->framerate_warned) {
     196           0 :                         av_log(format_context, AV_LOG_WARNING,
     197             :                                "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n");
     198           0 :                         apng->framerate_warned = 1;
     199             :                     }
     200           1 :                 } else if (apng->last_delay.num > 0) {
     201           0 :                     delay = apng->last_delay;
     202             :                 } else {
     203           1 :                     delay = apng->prev_delay;
     204             :                 }
     205             : 
     206             :                 // Update frame control header with new delay
     207          25 :                 AV_WB16(existing_fcTL_chunk + 20, delay.num);
     208          25 :                 AV_WB16(existing_fcTL_chunk + 22, delay.den);
     209          25 :                 AV_WB32(existing_fcTL_chunk + 26, ~av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), ~0U, existing_fcTL_chunk - 4, 26 + 4));
     210             :             }
     211          50 :             apng->prev_delay = delay;
     212             :         }
     213             : 
     214             :         // Write frame data
     215          50 :         avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size);
     216             :     }
     217          51 :     ++apng->frame_number;
     218             : 
     219          51 :     av_packet_unref(apng->prev_packet);
     220          51 :     if (packet)
     221          48 :         av_copy_packet(apng->prev_packet, packet);
     222          51 :     return 0;
     223             : }
     224             : 
     225          51 : static int apng_write_packet(AVFormatContext *format_context, AVPacket *packet)
     226             : {
     227          51 :     APNGMuxContext *apng = format_context->priv_data;
     228             :     int ret;
     229             : 
     230          51 :     if (!apng->prev_packet) {
     231           3 :         apng->prev_packet = av_malloc(sizeof(*apng->prev_packet));
     232           3 :         if (!apng->prev_packet)
     233           0 :             return AVERROR(ENOMEM);
     234             : 
     235           3 :         av_copy_packet(apng->prev_packet, packet);
     236             :     } else {
     237          48 :         ret = flush_packet(format_context, packet);
     238          48 :         if (ret < 0)
     239           0 :             return ret;
     240             :     }
     241             : 
     242          51 :     return 0;
     243             : }
     244             : 
     245           3 : static int apng_write_trailer(AVFormatContext *format_context)
     246             : {
     247           3 :     APNGMuxContext *apng = format_context->priv_data;
     248           3 :     AVIOContext *io_context = format_context->pb;
     249             :     uint8_t buf[8];
     250             :     int ret;
     251             : 
     252           3 :     if (apng->prev_packet) {
     253           3 :         ret = flush_packet(format_context, NULL);
     254           3 :         av_freep(&apng->prev_packet);
     255           3 :         if (ret < 0)
     256           0 :             return ret;
     257             :     }
     258             : 
     259           3 :     apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0);
     260             : 
     261           3 :     if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) {
     262           1 :         avio_seek(io_context, apng->acTL_offset, SEEK_SET);
     263             : 
     264           1 :         AV_WB32(buf, apng->frame_number);
     265           1 :         AV_WB32(buf + 4, apng->plays);
     266           1 :         apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8);
     267             :     }
     268             : 
     269           3 :     av_freep(&apng->extra_data);
     270           3 :     apng->extra_data = 0;
     271             : 
     272           3 :     return 0;
     273             : }
     274             : 
     275             : #define OFFSET(x) offsetof(APNGMuxContext, x)
     276             : #define ENC AV_OPT_FLAG_ENCODING_PARAM
     277             : static const AVOption options[] = {
     278             :     { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays),
     279             :       AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT_MAX, ENC },
     280             :     { "final_delay", "Force delay after the last frame", OFFSET(last_delay),
     281             :       AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, USHRT_MAX, ENC },
     282             :     { NULL },
     283             : };
     284             : 
     285             : static const AVClass apng_muxer_class = {
     286             :     .class_name = "APNG muxer",
     287             :     .item_name  = av_default_item_name,
     288             :     .version    = LIBAVUTIL_VERSION_INT,
     289             :     .option     = options,
     290             : };
     291             : 
     292             : AVOutputFormat ff_apng_muxer = {
     293             :     .name           = "apng",
     294             :     .long_name      = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"),
     295             :     .mime_type      = "image/png",
     296             :     .extensions     = "apng",
     297             :     .priv_data_size = sizeof(APNGMuxContext),
     298             :     .audio_codec    = AV_CODEC_ID_NONE,
     299             :     .video_codec    = AV_CODEC_ID_APNG,
     300             :     .write_header   = apng_write_header,
     301             :     .write_packet   = apng_write_packet,
     302             :     .write_trailer  = apng_write_trailer,
     303             :     .priv_class     = &apng_muxer_class,
     304             :     .flags          = AVFMT_VARIABLE_FPS,
     305             : };

Generated by: LCOV version 1.13