LCOV - code coverage report
Current view: top level - libavformat/tests - movenc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 474 504 94.0 %
Date: 2017-10-22 09:09:27 Functions: 16 17 94.1 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015 Martin Storsjo
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : #include "config.h"
      22             : 
      23             : #include "libavutil/intreadwrite.h"
      24             : #include "libavutil/mathematics.h"
      25             : #include "libavutil/md5.h"
      26             : 
      27             : #include "libavformat/avformat.h"
      28             : 
      29             : #if HAVE_UNISTD_H
      30             : #include <unistd.h>
      31             : #endif
      32             : 
      33             : #if !HAVE_GETOPT
      34             : #include "compat/getopt.c"
      35             : #endif
      36             : 
      37             : #define HASH_SIZE 16
      38             : 
      39             : static const uint8_t h264_extradata[] = {
      40             :     0x01, 0x4d, 0x40, 0x1e, 0xff, 0xe1, 0x00, 0x02, 0x67, 0x4d, 0x01, 0x00, 0x02, 0x68, 0xef
      41             : };
      42             : static const uint8_t aac_extradata[] = {
      43             :     0x12, 0x10
      44             : };
      45             : 
      46             : 
      47             : static const char *format = "mp4";
      48             : AVFormatContext *ctx;
      49             : uint8_t iobuf[32768];
      50             : AVDictionary *opts;
      51             : 
      52             : int write_file;
      53             : const char *cur_name;
      54             : FILE* out;
      55             : int out_size;
      56             : struct AVMD5* md5;
      57             : uint8_t hash[HASH_SIZE];
      58             : 
      59             : AVStream *video_st, *audio_st;
      60             : int64_t audio_dts, video_dts;
      61             : 
      62             : int bframes;
      63             : int64_t duration;
      64             : int64_t audio_duration;
      65             : int frames;
      66             : int gop_size;
      67             : int64_t next_p_pts;
      68             : enum AVPictureType last_picture;
      69             : int skip_write;
      70             : int skip_write_audio;
      71             : int clear_duration;
      72             : int force_iobuf_size;
      73             : int do_interleave;
      74             : int fake_pkt_duration;
      75             : 
      76             : int num_warnings;
      77             : 
      78             : int check_faults;
      79             : 
      80             : 
      81           7 : static void count_warnings(void *avcl, int level, const char *fmt, va_list vl)
      82             : {
      83           7 :     if (level == AV_LOG_WARNING)
      84           5 :         num_warnings++;
      85           7 : }
      86             : 
      87           2 : static void init_count_warnings(void)
      88             : {
      89           2 :     av_log_set_callback(count_warnings);
      90           2 :     num_warnings = 0;
      91           2 : }
      92             : 
      93           2 : static void reset_count_warnings(void)
      94             : {
      95           2 :     av_log_set_callback(av_log_default_callback);
      96           2 : }
      97             : 
      98         119 : static int io_write(void *opaque, uint8_t *buf, int size)
      99             : {
     100         119 :     out_size += size;
     101         119 :     av_md5_update(md5, buf, size);
     102         119 :     if (out)
     103           0 :         fwrite(buf, 1, size, out);
     104         119 :     return size;
     105             : }
     106             : 
     107         119 : static int io_write_data_type(void *opaque, uint8_t *buf, int size,
     108             :                               enum AVIODataMarkerType type, int64_t time)
     109             : {
     110         119 :     char timebuf[30], content[5] = { 0 };
     111             :     const char *str;
     112         119 :     switch (type) {
     113          38 :     case AVIO_DATA_MARKER_HEADER:         str = "header";   break;
     114          45 :     case AVIO_DATA_MARKER_SYNC_POINT:     str = "sync";     break;
     115           3 :     case AVIO_DATA_MARKER_BOUNDARY_POINT: str = "boundary"; break;
     116           6 :     case AVIO_DATA_MARKER_UNKNOWN:        str = "unknown";  break;
     117          27 :     case AVIO_DATA_MARKER_TRAILER:        str = "trailer";  break;
     118             :     }
     119         119 :     if (time == AV_NOPTS_VALUE)
     120          71 :         snprintf(timebuf, sizeof(timebuf), "nopts");
     121             :     else
     122          48 :         snprintf(timebuf, sizeof(timebuf), "%"PRId64, time);
     123             :     // There can be multiple header/trailer callbacks, only log the box type
     124             :     // for header at out_size == 0
     125         119 :     if (type != AVIO_DATA_MARKER_UNKNOWN &&
     126          86 :         type != AVIO_DATA_MARKER_TRAILER &&
     127          38 :         (type != AVIO_DATA_MARKER_HEADER || out_size == 0) &&
     128             :         size >= 8)
     129          72 :         memcpy(content, &buf[4], 4);
     130             :     else
     131          47 :         snprintf(content, sizeof(content), "-");
     132         119 :     printf("write_data len %d, time %s, type %s atom %s\n", size, timebuf, str, content);
     133         119 :     return io_write(opaque, buf, size);
     134             : }
     135             : 
     136          34 : static void init_out(const char *name)
     137             : {
     138             :     char buf[100];
     139          34 :     cur_name = name;
     140          34 :     snprintf(buf, sizeof(buf), "%s.%s", cur_name, format);
     141             : 
     142          34 :     av_md5_init(md5);
     143          34 :     if (write_file) {
     144           0 :         out = fopen(buf, "wb");
     145           0 :         if (!out)
     146           0 :             perror(buf);
     147             :     }
     148          34 :     out_size = 0;
     149          34 : }
     150             : 
     151          34 : static void close_out(void)
     152             : {
     153             :     int i;
     154          34 :     av_md5_final(md5, hash);
     155         578 :     for (i = 0; i < HASH_SIZE; i++)
     156         544 :         printf("%02x", hash[i]);
     157          34 :     printf(" %d %s\n", out_size, cur_name);
     158          34 :     if (out)
     159           0 :         fclose(out);
     160          34 :     out = NULL;
     161          34 : }
     162             : 
     163          19 : static void check_func(int value, int line, const char *msg, ...)
     164             : {
     165          19 :     if (!value) {
     166             :         va_list ap;
     167           0 :         va_start(ap, msg);
     168           0 :         printf("%d: ", line);
     169           0 :         vprintf(msg, ap);
     170           0 :         printf("\n");
     171           0 :         check_faults++;
     172           0 :         va_end(ap);
     173             :     }
     174          19 : }
     175             : #define check(value, ...) check_func(value, __LINE__, __VA_ARGS__)
     176             : 
     177          27 : static void init_fps(int bf, int audio_preroll, int fps)
     178             : {
     179             :     AVStream *st;
     180          27 :     int iobuf_size = force_iobuf_size ? force_iobuf_size : sizeof(iobuf);
     181          27 :     ctx = avformat_alloc_context();
     182          27 :     if (!ctx)
     183           0 :         exit(1);
     184          27 :     ctx->oformat = av_guess_format(format, NULL, NULL);
     185          27 :     if (!ctx->oformat)
     186           0 :         exit(1);
     187          27 :     ctx->pb = avio_alloc_context(iobuf, iobuf_size, AVIO_FLAG_WRITE, NULL, NULL, io_write, NULL);
     188          27 :     if (!ctx->pb)
     189           0 :         exit(1);
     190          27 :     ctx->pb->write_data_type = io_write_data_type;
     191          27 :     ctx->flags |= AVFMT_FLAG_BITEXACT;
     192             : 
     193          27 :     st = avformat_new_stream(ctx, NULL);
     194          27 :     if (!st)
     195           0 :         exit(1);
     196          27 :     st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
     197          27 :     st->codecpar->codec_id = AV_CODEC_ID_H264;
     198          27 :     st->codecpar->width = 640;
     199          27 :     st->codecpar->height = 480;
     200          27 :     st->time_base.num = 1;
     201          27 :     st->time_base.den = 30;
     202          27 :     st->codecpar->extradata_size = sizeof(h264_extradata);
     203          27 :     st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
     204          27 :     if (!st->codecpar->extradata)
     205           0 :         exit(1);
     206          27 :     memcpy(st->codecpar->extradata, h264_extradata, sizeof(h264_extradata));
     207          27 :     video_st = st;
     208             : 
     209          27 :     st = avformat_new_stream(ctx, NULL);
     210          27 :     if (!st)
     211           0 :         exit(1);
     212          27 :     st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
     213          27 :     st->codecpar->codec_id = AV_CODEC_ID_AAC;
     214          27 :     st->codecpar->sample_rate = 44100;
     215          27 :     st->codecpar->channels = 2;
     216          27 :     st->time_base.num = 1;
     217          27 :     st->time_base.den = 44100;
     218          27 :     st->codecpar->extradata_size = sizeof(aac_extradata);
     219          27 :     st->codecpar->extradata = av_mallocz(st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
     220          27 :     if (!st->codecpar->extradata)
     221           0 :         exit(1);
     222          27 :     memcpy(st->codecpar->extradata, aac_extradata, sizeof(aac_extradata));
     223          27 :     audio_st = st;
     224             : 
     225          27 :     if (avformat_write_header(ctx, &opts) < 0)
     226           0 :         exit(1);
     227          27 :     av_dict_free(&opts);
     228             : 
     229          27 :     frames = 0;
     230          27 :     gop_size = 30;
     231          27 :     duration = video_st->time_base.den / fps;
     232          27 :     audio_duration = 1024LL * audio_st->time_base.den / audio_st->codecpar->sample_rate;
     233          27 :     if (audio_preroll)
     234          11 :         audio_preroll = 2048LL * audio_st->time_base.den / audio_st->codecpar->sample_rate;
     235             : 
     236          27 :     bframes = bf;
     237          27 :     video_dts = bframes ? -duration : 0;
     238          27 :     audio_dts = -audio_preroll;
     239          27 : }
     240             : 
     241          23 : static void init(int bf, int audio_preroll)
     242             : {
     243          23 :     init_fps(bf, audio_preroll, 30);
     244          23 : }
     245             : 
     246          44 : static void mux_frames(int n, int c)
     247             : {
     248          44 :     int end_frames = frames + n;
     249        5107 :     while (1) {
     250             :         AVPacket pkt;
     251        5151 :         uint8_t pktdata[8] = { 0 };
     252        5151 :         av_init_packet(&pkt);
     253             : 
     254        5151 :         if (av_compare_ts(audio_dts, audio_st->time_base, video_dts, video_st->time_base) < 0) {
     255        3487 :             pkt.dts = pkt.pts = audio_dts;
     256        3487 :             pkt.stream_index = 1;
     257        3487 :             pkt.duration = audio_duration;
     258        3487 :             audio_dts += audio_duration;
     259             :         } else {
     260        1664 :             if (frames == end_frames)
     261          44 :                 break;
     262        1620 :             pkt.dts = video_dts;
     263        1620 :             pkt.stream_index = 0;
     264        1620 :             pkt.duration = duration;
     265        1620 :             if ((frames % gop_size) == 0) {
     266          54 :                 pkt.flags |= AV_PKT_FLAG_KEY;
     267          54 :                 last_picture = AV_PICTURE_TYPE_I;
     268          54 :                 pkt.pts = pkt.dts + duration;
     269          54 :                 video_dts = pkt.pts;
     270             :             } else {
     271        1566 :                 if (last_picture == AV_PICTURE_TYPE_P) {
     272         756 :                     last_picture = AV_PICTURE_TYPE_B;
     273         756 :                     pkt.pts = pkt.dts;
     274         756 :                     video_dts = next_p_pts;
     275             :                 } else {
     276         810 :                     last_picture = AV_PICTURE_TYPE_P;
     277         810 :                     if (((frames + 1) % gop_size) == 0) {
     278          54 :                         pkt.pts = pkt.dts + duration;
     279          54 :                         video_dts = pkt.pts;
     280             :                     } else {
     281         756 :                         next_p_pts = pkt.pts = pkt.dts + 2 * duration;
     282         756 :                         video_dts += duration;
     283             :                     }
     284             :                 }
     285             :             }
     286        1620 :             if (!bframes)
     287         600 :                 pkt.pts = pkt.dts;
     288        1620 :             if (fake_pkt_duration)
     289           1 :                 pkt.duration = fake_pkt_duration;
     290        1620 :             frames++;
     291             :         }
     292             : 
     293        5107 :         if (clear_duration)
     294         328 :             pkt.duration = 0;
     295        5107 :         AV_WB32(pktdata + 4, pkt.pts);
     296        5107 :         pkt.data = pktdata;
     297        5107 :         pkt.size = 8;
     298        5107 :         if (skip_write)
     299         910 :             continue;
     300        4739 :         if (skip_write_audio && pkt.stream_index == 1)
     301         174 :             continue;
     302             : 
     303        4565 :         if (c) {
     304          73 :             pkt.pts += (1LL<<32);
     305          73 :             pkt.dts += (1LL<<32);
     306             :         }
     307             : 
     308        4565 :         if (do_interleave)
     309         173 :             av_interleaved_write_frame(ctx, &pkt);
     310             :         else
     311        4392 :             av_write_frame(ctx, &pkt);
     312             :     }
     313          44 : }
     314             : 
     315          36 : static void mux_gops(int n)
     316             : {
     317          36 :     mux_frames(gop_size * n, 0);
     318          36 : }
     319             : 
     320           5 : static void skip_gops(int n)
     321             : {
     322           5 :     skip_write = 1;
     323           5 :     mux_gops(n);
     324           5 :     skip_write = 0;
     325           5 : }
     326             : 
     327           2 : static void signal_init_ts(void)
     328             : {
     329             :     AVPacket pkt;
     330           2 :     av_init_packet(&pkt);
     331           2 :     pkt.size = 0;
     332           2 :     pkt.data = NULL;
     333             : 
     334           2 :     pkt.stream_index = 0;
     335           2 :     pkt.dts = video_dts;
     336           2 :     pkt.pts = 0;
     337           2 :     av_write_frame(ctx, &pkt);
     338             : 
     339           2 :     pkt.stream_index = 1;
     340           2 :     pkt.dts = pkt.pts = audio_dts;
     341           2 :     av_write_frame(ctx, &pkt);
     342           2 : }
     343             : 
     344          27 : static void finish(void)
     345             : {
     346          27 :     av_write_trailer(ctx);
     347          27 :     avio_context_free(&ctx->pb);
     348          27 :     avformat_free_context(ctx);
     349          27 :     ctx = NULL;
     350          27 : }
     351             : 
     352           0 : static void help(void)
     353             : {
     354           0 :     printf("movenc-test [-w]\n"
     355             :            "-w          write output into files\n");
     356           0 : }
     357             : 
     358           1 : int main(int argc, char **argv)
     359             : {
     360             :     int c;
     361             :     uint8_t header[HASH_SIZE];
     362             :     uint8_t content[HASH_SIZE];
     363             :     int empty_moov_pos;
     364             :     int prev_pos;
     365             : 
     366             :     for (;;) {
     367           1 :         c = getopt(argc, argv, "wh");
     368           1 :         if (c == -1)
     369           1 :             break;
     370           0 :         switch (c) {
     371           0 :         case 'w':
     372           0 :             write_file = 1;
     373           0 :             break;
     374           0 :         default:
     375             :         case 'h':
     376           0 :             help();
     377           0 :             return 0;
     378             :         }
     379             :     }
     380             : 
     381           1 :     av_register_all();
     382             : 
     383           1 :     md5 = av_md5_alloc();
     384           1 :     if (!md5)
     385           0 :         return 1;
     386             : 
     387             :     // Write a fragmented file with an initial moov that actually contains some
     388             :     // samples. One moov+mdat with 1 second of data and one moof+mdat with 1
     389             :     // second of data.
     390           1 :     init_out("non-empty-moov");
     391           1 :     av_dict_set(&opts, "movflags", "frag_keyframe", 0);
     392           1 :     init(0, 0);
     393           1 :     mux_gops(2);
     394           1 :     finish();
     395           1 :     close_out();
     396             : 
     397             :     // Write a similar file, but with B-frames and audio preroll, handled
     398             :     // via an edit list.
     399           1 :     init_out("non-empty-moov-elst");
     400           1 :     av_dict_set(&opts, "movflags", "frag_keyframe", 0);
     401           1 :     av_dict_set(&opts, "use_editlist", "1", 0);
     402           1 :     init(1, 1);
     403           1 :     mux_gops(2);
     404           1 :     finish();
     405           1 :     close_out();
     406             : 
     407             :     // Use B-frames but no audio-preroll, but without an edit list.
     408             :     // Due to avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO, the dts
     409             :     // of the first audio packet is > 0, but it is set to zero since edit
     410             :     // lists aren't used, increasing the duration of the first packet instead.
     411           1 :     init_out("non-empty-moov-no-elst");
     412           1 :     av_dict_set(&opts, "movflags", "frag_keyframe", 0);
     413           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     414           1 :     init(1, 0);
     415           1 :     mux_gops(2);
     416           1 :     finish();
     417           1 :     close_out();
     418             : 
     419           1 :     format = "ismv";
     420             :     // Write an ISMV, with B-frames and audio preroll.
     421           1 :     init_out("ismv");
     422           1 :     av_dict_set(&opts, "movflags", "frag_keyframe", 0);
     423           1 :     init(1, 1);
     424           1 :     mux_gops(2);
     425           1 :     finish();
     426           1 :     close_out();
     427           1 :     format = "mp4";
     428             : 
     429             :     // An initial moov that doesn't contain any samples, followed by two
     430             :     // moof+mdat pairs.
     431           1 :     init_out("empty-moov");
     432           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
     433           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     434           1 :     init(0, 0);
     435           1 :     mux_gops(2);
     436           1 :     finish();
     437           1 :     close_out();
     438           1 :     memcpy(content, hash, HASH_SIZE);
     439             : 
     440             :     // Similar to the previous one, but with input that doesn't start at
     441             :     // pts/dts 0. avoid_negative_ts behaves in the same way as
     442             :     // in non-empty-moov-no-elst above.
     443           1 :     init_out("empty-moov-no-elst");
     444           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
     445           1 :     init(1, 0);
     446           1 :     mux_gops(2);
     447           1 :     finish();
     448           1 :     close_out();
     449             : 
     450             :     // Same as the previous one, but disable avoid_negative_ts (which
     451             :     // would require using an edit list, but with empty_moov, one can't
     452             :     // write a sensible edit list, when the start timestamps aren't known).
     453             :     // This should trigger a warning - we check that the warning is produced.
     454           1 :     init_count_warnings();
     455           1 :     init_out("empty-moov-no-elst-no-adjust");
     456           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
     457           1 :     av_dict_set(&opts, "avoid_negative_ts", "0", 0);
     458           1 :     init(1, 0);
     459           1 :     mux_gops(2);
     460           1 :     finish();
     461           1 :     close_out();
     462             : 
     463           1 :     reset_count_warnings();
     464           1 :     check(num_warnings > 0, "No warnings printed for unhandled start offset");
     465             : 
     466             :     // Verify that delay_moov produces the same as empty_moov for
     467             :     // simple input
     468           1 :     init_out("delay-moov");
     469           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
     470           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     471           1 :     init(0, 0);
     472           1 :     mux_gops(2);
     473           1 :     finish();
     474           1 :     close_out();
     475           1 :     check(!memcmp(hash, content, HASH_SIZE), "delay_moov differs from empty_moov");
     476             : 
     477             :     // Test writing content that requires an edit list using delay_moov
     478           1 :     init_out("delay-moov-elst");
     479           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
     480           1 :     init(1, 1);
     481           1 :     mux_gops(2);
     482           1 :     finish();
     483           1 :     close_out();
     484             : 
     485             :     // Test writing a file with one track lacking packets, with delay_moov.
     486           1 :     skip_write_audio = 1;
     487           1 :     init_out("delay-moov-empty-track");
     488           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
     489           1 :     init(0, 0);
     490           1 :     mux_gops(2);
     491             :     // The automatic flushing shouldn't output anything, since we're still
     492             :     // waiting for data for some tracks
     493           1 :     check(out_size == 0, "delay_moov flushed prematurely");
     494             :     // When closed (or manually flushed), all the written data should still
     495             :     // be output.
     496           1 :     finish();
     497           1 :     close_out();
     498           1 :     check(out_size > 0, "delay_moov didn't output anything");
     499             : 
     500             :     // Check that manually flushing still outputs things as expected. This
     501             :     // produces two fragments, while the one above produces only one.
     502           1 :     init_out("delay-moov-empty-track-flush");
     503           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov", 0);
     504           1 :     init(0, 0);
     505           1 :     mux_gops(1);
     506           1 :     av_write_frame(ctx, NULL); // Force writing the moov
     507           1 :     check(out_size > 0, "No moov written");
     508           1 :     av_write_frame(ctx, NULL);
     509           1 :     mux_gops(1);
     510           1 :     av_write_frame(ctx, NULL);
     511           1 :     finish();
     512           1 :     close_out();
     513             : 
     514           1 :     skip_write_audio = 0;
     515             : 
     516             : 
     517             : 
     518             :     // Verify that the header written by delay_moov when manually flushed
     519             :     // is identical to the one by empty_moov.
     520           1 :     init_out("empty-moov-header");
     521           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
     522           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     523           1 :     init(0, 0);
     524           1 :     close_out();
     525           1 :     memcpy(header, hash, HASH_SIZE);
     526           1 :     init_out("empty-moov-content");
     527           1 :     mux_gops(2);
     528             :     // Written 2 seconds of content, with an automatic flush after 1 second.
     529           1 :     check(out_size > 0, "No automatic flush?");
     530           1 :     empty_moov_pos = prev_pos = out_size;
     531             :     // Manually flush the second fragment
     532           1 :     av_write_frame(ctx, NULL);
     533           1 :     check(out_size > prev_pos, "No second fragment flushed?");
     534           1 :     prev_pos = out_size;
     535             :     // Check that an extra flush doesn't output any more data
     536           1 :     av_write_frame(ctx, NULL);
     537           1 :     check(out_size == prev_pos, "More data written?");
     538           1 :     close_out();
     539           1 :     memcpy(content, hash, HASH_SIZE);
     540             :     // Ignore the trailer written here
     541           1 :     finish();
     542             : 
     543           1 :     init_out("delay-moov-header");
     544           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov", 0);
     545           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     546           1 :     init(0, 0);
     547           1 :     check(out_size == 0, "Output written during init with delay_moov");
     548           1 :     mux_gops(1); // Write 1 second of content
     549           1 :     av_write_frame(ctx, NULL); // Force writing the moov
     550           1 :     close_out();
     551           1 :     check(!memcmp(hash, header, HASH_SIZE), "delay_moov header differs from empty_moov");
     552           1 :     init_out("delay-moov-content");
     553           1 :     av_write_frame(ctx, NULL); // Flush the first fragment
     554           1 :     check(out_size == empty_moov_pos, "Manually flushed content differs from automatically flushed, %d vs %d", out_size, empty_moov_pos);
     555           1 :     mux_gops(1); // Write the rest of the content
     556           1 :     av_write_frame(ctx, NULL); // Flush the second fragment
     557           1 :     close_out();
     558           1 :     check(!memcmp(hash, content, HASH_SIZE), "delay_moov content differs from empty_moov");
     559           1 :     finish();
     560             : 
     561             : 
     562             :     // Verify that we can produce an identical second fragment without
     563             :     // writing the first one. First write the reference fragments that
     564             :     // we want to reproduce.
     565           1 :     av_dict_set(&opts, "movflags", "frag_custom+empty_moov+dash", 0);
     566           1 :     init(0, 0);
     567           1 :     mux_gops(1);
     568           1 :     av_write_frame(ctx, NULL); // Output the first fragment
     569           1 :     init_out("empty-moov-second-frag");
     570           1 :     mux_gops(1);
     571           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     572           1 :     close_out();
     573           1 :     memcpy(content, hash, HASH_SIZE);
     574           1 :     finish();
     575             : 
     576             :     // Produce the same second fragment without actually writing the first
     577             :     // one before.
     578           1 :     av_dict_set(&opts, "movflags", "frag_custom+empty_moov+dash+frag_discont", 0);
     579           1 :     av_dict_set(&opts, "fragment_index", "2", 0);
     580           1 :     av_dict_set(&opts, "avoid_negative_ts", "0", 0);
     581           1 :     av_dict_set(&opts, "use_editlist", "0", 0);
     582           1 :     init(0, 0);
     583           1 :     skip_gops(1);
     584           1 :     init_out("empty-moov-second-frag-discont");
     585           1 :     mux_gops(1);
     586           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     587           1 :     close_out();
     588           1 :     check(!memcmp(hash, content, HASH_SIZE), "discontinuously written fragment differs");
     589           1 :     finish();
     590             : 
     591             :     // Produce the same thing by using delay_moov, which requires a slightly
     592             :     // different call sequence.
     593           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash+frag_discont", 0);
     594           1 :     av_dict_set(&opts, "fragment_index", "2", 0);
     595           1 :     init(0, 0);
     596           1 :     skip_gops(1);
     597           1 :     mux_gops(1);
     598           1 :     av_write_frame(ctx, NULL); // Output the moov
     599           1 :     init_out("delay-moov-second-frag-discont");
     600           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     601           1 :     close_out();
     602           1 :     check(!memcmp(hash, content, HASH_SIZE), "discontinuously written fragment differs");
     603           1 :     finish();
     604             : 
     605             : 
     606             :     // Test discontinuously written fragments with B-frames (where the
     607             :     // assumption of starting at pts=0 works) but not with audio preroll
     608             :     // (which can't be guessed).
     609           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash", 0);
     610           1 :     init(1, 0);
     611           1 :     mux_gops(1);
     612           1 :     init_out("delay-moov-elst-init");
     613           1 :     av_write_frame(ctx, NULL); // Output the moov
     614           1 :     close_out();
     615           1 :     memcpy(header, hash, HASH_SIZE);
     616           1 :     av_write_frame(ctx, NULL); // Output the first fragment
     617           1 :     init_out("delay-moov-elst-second-frag");
     618           1 :     mux_gops(1);
     619           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     620           1 :     close_out();
     621           1 :     memcpy(content, hash, HASH_SIZE);
     622           1 :     finish();
     623             : 
     624           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash+frag_discont", 0);
     625           1 :     av_dict_set(&opts, "fragment_index", "2", 0);
     626           1 :     init(1, 0);
     627           1 :     skip_gops(1);
     628           1 :     mux_gops(1); // Write the second fragment
     629           1 :     init_out("delay-moov-elst-init-discont");
     630           1 :     av_write_frame(ctx, NULL); // Output the moov
     631           1 :     close_out();
     632           1 :     check(!memcmp(hash, header, HASH_SIZE), "discontinuously written header differs");
     633           1 :     init_out("delay-moov-elst-second-frag-discont");
     634           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     635           1 :     close_out();
     636           1 :     check(!memcmp(hash, content, HASH_SIZE), "discontinuously written fragment differs");
     637           1 :     finish();
     638             : 
     639             : 
     640             :     // Test discontinuously written fragments with B-frames and audio preroll,
     641             :     // properly signaled.
     642           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash", 0);
     643           1 :     init(1, 1);
     644           1 :     mux_gops(1);
     645           1 :     init_out("delay-moov-elst-signal-init");
     646           1 :     av_write_frame(ctx, NULL); // Output the moov
     647           1 :     close_out();
     648           1 :     memcpy(header, hash, HASH_SIZE);
     649           1 :     av_write_frame(ctx, NULL); // Output the first fragment
     650           1 :     init_out("delay-moov-elst-signal-second-frag");
     651           1 :     mux_gops(1);
     652           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     653           1 :     close_out();
     654           1 :     memcpy(content, hash, HASH_SIZE);
     655           1 :     finish();
     656             : 
     657           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash+frag_discont", 0);
     658           1 :     av_dict_set(&opts, "fragment_index", "2", 0);
     659           1 :     init(1, 1);
     660           1 :     signal_init_ts();
     661           1 :     skip_gops(1);
     662           1 :     mux_gops(1); // Write the second fragment
     663           1 :     init_out("delay-moov-elst-signal-init-discont");
     664           1 :     av_write_frame(ctx, NULL); // Output the moov
     665           1 :     close_out();
     666           1 :     check(!memcmp(hash, header, HASH_SIZE), "discontinuously written header differs");
     667           1 :     init_out("delay-moov-elst-signal-second-frag-discont");
     668           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     669           1 :     close_out();
     670           1 :     check(!memcmp(hash, content, HASH_SIZE), "discontinuously written fragment differs");
     671           1 :     finish();
     672             : 
     673             : 
     674             :     // Test muxing discontinuous fragments with very large (> (1<<31)) timestamps.
     675           1 :     av_dict_set(&opts, "movflags", "frag_custom+delay_moov+dash+frag_discont", 0);
     676           1 :     av_dict_set(&opts, "fragment_index", "2", 0);
     677           1 :     init(1, 1);
     678           1 :     signal_init_ts();
     679           1 :     skip_gops(1);
     680           1 :     mux_frames(gop_size, 1); // Write the second fragment
     681           1 :     init_out("delay-moov-elst-signal-init-discont-largets");
     682           1 :     av_write_frame(ctx, NULL); // Output the moov
     683           1 :     close_out();
     684           1 :     init_out("delay-moov-elst-signal-second-frag-discont-largets");
     685           1 :     av_write_frame(ctx, NULL); // Output the second fragment
     686           1 :     close_out();
     687           1 :     finish();
     688             : 
     689             :     // Test VFR content, with sidx atoms (which declare the pts duration
     690             :     // of a fragment, forcing overriding the start pts of the next one).
     691             :     // Here, the fragment duration in pts is significantly different from
     692             :     // the duration in dts. The video stream starts at dts=-10,pts=0, and
     693             :     // the second fragment starts at dts=155,pts=156. The trun duration sum
     694             :     // of the first fragment is 165, which also is written as
     695             :     // baseMediaDecodeTime in the tfdt in the second fragment. The sidx for
     696             :     // the first fragment says earliest_presentation_time = 0 and
     697             :     // subsegment_duration = 156, which also matches the sidx in the second
     698             :     // fragment. For the audio stream, the pts and dts durations also don't
     699             :     // match - the input stream starts at pts=-2048, but that part is excluded
     700             :     // by the edit list.
     701           1 :     init_out("vfr");
     702           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+dash", 0);
     703           1 :     init_fps(1, 1, 3);
     704           1 :     mux_frames(gop_size/2, 0);
     705           1 :     duration /= 10;
     706           1 :     mux_frames(gop_size/2, 0);
     707           1 :     mux_gops(1);
     708           1 :     finish();
     709           1 :     close_out();
     710             : 
     711             :     // Test VFR content, with cleared duration fields. In these cases,
     712             :     // the muxer must guess the duration of the last packet of each
     713             :     // fragment. As long as the framerate doesn't vary (too much) at the
     714             :     // fragment edge, it works just fine. Additionally, when automatically
     715             :     // cutting fragments, the muxer already know the timestamps of the next
     716             :     // packet for one stream (in most cases the video stream), avoiding
     717             :     // having to use guesses for that one.
     718           1 :     init_count_warnings();
     719           1 :     clear_duration = 1;
     720           1 :     init_out("vfr-noduration");
     721           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+dash", 0);
     722           1 :     init_fps(1, 1, 3);
     723           1 :     mux_frames(gop_size/2, 0);
     724           1 :     duration /= 10;
     725           1 :     mux_frames(gop_size/2, 0);
     726           1 :     mux_gops(1);
     727           1 :     finish();
     728           1 :     close_out();
     729           1 :     clear_duration = 0;
     730           1 :     reset_count_warnings();
     731           1 :     check(num_warnings > 0, "No warnings printed for filled in durations");
     732             : 
     733             :     // Test with an IO buffer size that is too small to hold a full fragment;
     734             :     // this will cause write_data_type to be called with the type unknown.
     735           1 :     force_iobuf_size = 1500;
     736           1 :     init_out("large_frag");
     737           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
     738           1 :     init_fps(1, 1, 3);
     739           1 :     mux_gops(2);
     740           1 :     finish();
     741           1 :     close_out();
     742           1 :     force_iobuf_size = 0;
     743             : 
     744             :     // Test VFR content with bframes with interleaving.
     745             :     // Here, using av_interleaved_write_frame allows the muxer to get the
     746             :     // fragment end durations right. We always set the packet duration to
     747             :     // the expected, but we simulate dropped frames at one point.
     748           1 :     do_interleave = 1;
     749           1 :     init_out("vfr-noduration-interleave");
     750           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov", 0);
     751           1 :     av_dict_set(&opts, "frag_duration", "650000", 0);
     752           1 :     init_fps(1, 1, 30);
     753           1 :     mux_frames(gop_size/2, 0);
     754             :     // Pretend that the packet duration is the normal, even if
     755             :     // we actually skip a bunch of frames. (I.e., simulate that
     756             :     // we don't know of the framedrop in advance.)
     757           1 :     fake_pkt_duration = duration;
     758           1 :     duration *= 10;
     759           1 :     mux_frames(1, 0);
     760           1 :     fake_pkt_duration = 0;
     761           1 :     duration /= 10;
     762           1 :     mux_frames(gop_size/2 - 1, 0);
     763           1 :     mux_gops(1);
     764           1 :     finish();
     765           1 :     close_out();
     766           1 :     clear_duration = 0;
     767           1 :     do_interleave = 0;
     768             : 
     769             :     // Write a fragmented file with b-frames and audio preroll,
     770             :     // with negative cts values, removing the edit list for the
     771             :     // video track.
     772           1 :     init_out("delay-moov-elst-neg-cts");
     773           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+delay_moov+negative_cts_offsets", 0);
     774           1 :     init(1, 1);
     775           1 :     mux_gops(2);
     776           1 :     finish();
     777           1 :     close_out();
     778             : 
     779             :     // Write a fragmented file with b-frames without audio preroll,
     780             :     // with negative cts values, avoiding any edit lists, allowing
     781             :     // to use empty_moov instead of delay_moov.
     782           1 :     init_out("empty-moov-neg-cts");
     783           1 :     av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+negative_cts_offsets", 0);
     784           1 :     init(1, 0);
     785           1 :     mux_gops(2);
     786           1 :     finish();
     787           1 :     close_out();
     788             : 
     789           1 :     av_free(md5);
     790             : 
     791           1 :     return check_faults > 0 ? 1 : 0;
     792             : }

Generated by: LCOV version 1.13