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

Generated by: LCOV version 1.13