LCOV - code coverage report
Current view: top level - libavformat - hdsenc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 348 0.0 %
Date: 2017-12-18 20:14:19 Functions: 0 13 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Live HDS fragmenter
       3             :  * Copyright (c) 2013 Martin Storsjo
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or
       8             :  * modify it under the terms of the GNU Lesser General Public
       9             :  * License as published by the Free Software Foundation; either
      10             :  * version 2.1 of the License, or (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * Lesser General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public
      18             :  * License along with FFmpeg; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : 
      22             : #include "config.h"
      23             : #include <float.h>
      24             : #if HAVE_UNISTD_H
      25             : #include <unistd.h>
      26             : #endif
      27             : 
      28             : #include "avformat.h"
      29             : #include "avio_internal.h"
      30             : #include "internal.h"
      31             : #include "os_support.h"
      32             : 
      33             : #include "libavutil/avstring.h"
      34             : #include "libavutil/base64.h"
      35             : #include "libavutil/intreadwrite.h"
      36             : #include "libavutil/mathematics.h"
      37             : #include "libavutil/opt.h"
      38             : 
      39             : typedef struct Fragment {
      40             :     char file[1024];
      41             :     int64_t start_time, duration;
      42             :     int n;
      43             : } Fragment;
      44             : 
      45             : typedef struct OutputStream {
      46             :     int bitrate;
      47             :     int first_stream;
      48             :     AVFormatContext *ctx;
      49             :     int ctx_inited;
      50             :     uint8_t iobuf[32768];
      51             :     char temp_filename[1024];
      52             :     int64_t frag_start_ts, last_ts;
      53             :     AVIOContext *out;
      54             :     int packets_written;
      55             :     int nb_fragments, fragments_size, fragment_index;
      56             :     Fragment **fragments;
      57             : 
      58             :     int has_audio, has_video;
      59             : 
      60             :     uint8_t *metadata;
      61             :     int metadata_size;
      62             : 
      63             :     uint8_t *extra_packets[2];
      64             :     int extra_packet_sizes[2];
      65             :     int nb_extra_packets;
      66             : } OutputStream;
      67             : 
      68             : typedef struct HDSContext {
      69             :     const AVClass *class;  /* Class for private options. */
      70             :     int window_size;
      71             :     int extra_window_size;
      72             :     int min_frag_duration;
      73             :     int remove_at_exit;
      74             : 
      75             :     OutputStream *streams;
      76             :     int nb_streams;
      77             : } HDSContext;
      78             : 
      79           0 : static int parse_header(OutputStream *os, const uint8_t *buf, int buf_size)
      80             : {
      81           0 :     if (buf_size < 13)
      82           0 :         return AVERROR_INVALIDDATA;
      83           0 :     if (memcmp(buf, "FLV", 3))
      84           0 :         return AVERROR_INVALIDDATA;
      85           0 :     buf      += 13;
      86           0 :     buf_size -= 13;
      87           0 :     while (buf_size >= 11 + 4) {
      88           0 :         int type = buf[0];
      89           0 :         int size = AV_RB24(&buf[1]) + 11 + 4;
      90           0 :         if (size > buf_size)
      91           0 :             return AVERROR_INVALIDDATA;
      92           0 :         if (type == 8 || type == 9) {
      93           0 :             if (os->nb_extra_packets >= FF_ARRAY_ELEMS(os->extra_packets))
      94           0 :                 return AVERROR_INVALIDDATA;
      95           0 :             os->extra_packet_sizes[os->nb_extra_packets] = size;
      96           0 :             os->extra_packets[os->nb_extra_packets] = av_malloc(size);
      97           0 :             if (!os->extra_packets[os->nb_extra_packets])
      98           0 :                 return AVERROR(ENOMEM);
      99           0 :             memcpy(os->extra_packets[os->nb_extra_packets], buf, size);
     100           0 :             os->nb_extra_packets++;
     101           0 :         } else if (type == 0x12) {
     102           0 :             if (os->metadata)
     103           0 :                 return AVERROR_INVALIDDATA;
     104           0 :             os->metadata_size = size - 11 - 4;
     105           0 :             os->metadata      = av_malloc(os->metadata_size);
     106           0 :             if (!os->metadata)
     107           0 :                 return AVERROR(ENOMEM);
     108           0 :             memcpy(os->metadata, buf + 11, os->metadata_size);
     109             :         }
     110           0 :         buf      += size;
     111           0 :         buf_size -= size;
     112             :     }
     113           0 :     if (!os->metadata)
     114           0 :         return AVERROR_INVALIDDATA;
     115           0 :     return 0;
     116             : }
     117             : 
     118           0 : static int hds_write(void *opaque, uint8_t *buf, int buf_size)
     119             : {
     120           0 :     OutputStream *os = opaque;
     121           0 :     if (os->out) {
     122           0 :         avio_write(os->out, buf, buf_size);
     123             :     } else {
     124           0 :         if (!os->metadata_size) {
     125             :             int ret;
     126             :             // Assuming the IO buffer is large enough to fit the
     127             :             // FLV header and all metadata and extradata packets
     128           0 :             if ((ret = parse_header(os, buf, buf_size)) < 0)
     129           0 :                 return ret;
     130             :         }
     131             :     }
     132           0 :     return buf_size;
     133             : }
     134             : 
     135           0 : static void hds_free(AVFormatContext *s)
     136             : {
     137           0 :     HDSContext *c = s->priv_data;
     138             :     int i, j;
     139           0 :     if (!c->streams)
     140           0 :         return;
     141           0 :     for (i = 0; i < s->nb_streams; i++) {
     142           0 :         OutputStream *os = &c->streams[i];
     143           0 :         if (os->out)
     144           0 :             ff_format_io_close(s, &os->out);
     145           0 :         if (os->ctx && os->ctx_inited)
     146           0 :             av_write_trailer(os->ctx);
     147           0 :         if (os->ctx)
     148           0 :             avio_context_free(&os->ctx->pb);
     149           0 :         if (os->ctx)
     150           0 :             avformat_free_context(os->ctx);
     151           0 :         av_freep(&os->metadata);
     152           0 :         for (j = 0; j < os->nb_extra_packets; j++)
     153           0 :             av_freep(&os->extra_packets[j]);
     154           0 :         for (j = 0; j < os->nb_fragments; j++)
     155           0 :             av_freep(&os->fragments[j]);
     156           0 :         av_freep(&os->fragments);
     157             :     }
     158           0 :     av_freep(&c->streams);
     159             : }
     160             : 
     161           0 : static int write_manifest(AVFormatContext *s, int final)
     162             : {
     163           0 :     HDSContext *c = s->priv_data;
     164             :     AVIOContext *out;
     165             :     char filename[1024], temp_filename[1024];
     166             :     int ret, i;
     167           0 :     double duration = 0;
     168             : 
     169           0 :     if (c->nb_streams > 0)
     170           0 :         duration = c->streams[0].last_ts * av_q2d(s->streams[0]->time_base);
     171             : 
     172           0 :     snprintf(filename, sizeof(filename), "%s/index.f4m", s->filename);
     173           0 :     snprintf(temp_filename, sizeof(temp_filename), "%s/index.f4m.tmp", s->filename);
     174           0 :     ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL);
     175           0 :     if (ret < 0) {
     176           0 :         av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
     177           0 :         return ret;
     178             :     }
     179           0 :     avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
     180           0 :     avio_printf(out, "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n");
     181           0 :     avio_printf(out, "\t<id>%s</id>\n", av_basename(s->filename));
     182           0 :     avio_printf(out, "\t<streamType>%s</streamType>\n",
     183             :                      final ? "recorded" : "live");
     184           0 :     avio_printf(out, "\t<deliveryType>streaming</deliveryType>\n");
     185           0 :     if (final)
     186           0 :         avio_printf(out, "\t<duration>%f</duration>\n", duration);
     187           0 :     for (i = 0; i < c->nb_streams; i++) {
     188           0 :         OutputStream *os = &c->streams[i];
     189           0 :         int b64_size = AV_BASE64_SIZE(os->metadata_size);
     190           0 :         char *base64 = av_malloc(b64_size);
     191           0 :         if (!base64) {
     192           0 :             ff_format_io_close(s, &out);
     193           0 :             return AVERROR(ENOMEM);
     194             :         }
     195           0 :         av_base64_encode(base64, b64_size, os->metadata, os->metadata_size);
     196             : 
     197           0 :         avio_printf(out, "\t<bootstrapInfo profile=\"named\" url=\"stream%d.abst\" id=\"bootstrap%d\" />\n", i, i);
     198           0 :         avio_printf(out, "\t<media bitrate=\"%d\" url=\"stream%d\" bootstrapInfoId=\"bootstrap%d\">\n", os->bitrate/1000, i, i);
     199           0 :         avio_printf(out, "\t\t<metadata>%s</metadata>\n", base64);
     200           0 :         avio_printf(out, "\t</media>\n");
     201           0 :         av_free(base64);
     202             :     }
     203           0 :     avio_printf(out, "</manifest>\n");
     204           0 :     avio_flush(out);
     205           0 :     ff_format_io_close(s, &out);
     206           0 :     return ff_rename(temp_filename, filename, s);
     207             : }
     208             : 
     209           0 : static void update_size(AVIOContext *out, int64_t pos)
     210             : {
     211           0 :     int64_t end = avio_tell(out);
     212           0 :     avio_seek(out, pos, SEEK_SET);
     213           0 :     avio_wb32(out, end - pos);
     214           0 :     avio_seek(out, end, SEEK_SET);
     215           0 : }
     216             : 
     217             : /* Note, the .abst files need to be served with the "binary/octet"
     218             :  * mime type, otherwise at least the OSMF player can easily fail
     219             :  * with "stream not found" when polling for the next fragment. */
     220           0 : static int write_abst(AVFormatContext *s, OutputStream *os, int final)
     221             : {
     222           0 :     HDSContext *c = s->priv_data;
     223             :     AVIOContext *out;
     224             :     char filename[1024], temp_filename[1024];
     225             :     int i, ret;
     226             :     int64_t asrt_pos, afrt_pos;
     227           0 :     int start = 0, fragments;
     228           0 :     int index = s->streams[os->first_stream]->id;
     229           0 :     int64_t cur_media_time = 0;
     230           0 :     if (c->window_size)
     231           0 :         start = FFMAX(os->nb_fragments - c->window_size, 0);
     232           0 :     fragments = os->nb_fragments - start;
     233           0 :     if (final)
     234           0 :         cur_media_time = os->last_ts;
     235           0 :     else if (os->nb_fragments)
     236           0 :         cur_media_time = os->fragments[os->nb_fragments - 1]->start_time;
     237             : 
     238           0 :     snprintf(filename, sizeof(filename),
     239           0 :              "%s/stream%d.abst", s->filename, index);
     240           0 :     snprintf(temp_filename, sizeof(temp_filename),
     241           0 :              "%s/stream%d.abst.tmp", s->filename, index);
     242           0 :     ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL);
     243           0 :     if (ret < 0) {
     244           0 :         av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
     245           0 :         return ret;
     246             :     }
     247           0 :     avio_wb32(out, 0); // abst size
     248           0 :     avio_wl32(out, MKTAG('a','b','s','t'));
     249           0 :     avio_wb32(out, 0); // version + flags
     250           0 :     avio_wb32(out, os->fragment_index - 1); // BootstrapinfoVersion
     251           0 :     avio_w8(out, final ? 0 : 0x20); // profile, live, update
     252           0 :     avio_wb32(out, 1000); // timescale
     253           0 :     avio_wb64(out, cur_media_time);
     254           0 :     avio_wb64(out, 0); // SmpteTimeCodeOffset
     255           0 :     avio_w8(out, 0); // MovieIdentifer (null string)
     256           0 :     avio_w8(out, 0); // ServerEntryCount
     257           0 :     avio_w8(out, 0); // QualityEntryCount
     258           0 :     avio_w8(out, 0); // DrmData (null string)
     259           0 :     avio_w8(out, 0); // MetaData (null string)
     260           0 :     avio_w8(out, 1); // SegmentRunTableCount
     261           0 :     asrt_pos = avio_tell(out);
     262           0 :     avio_wb32(out, 0); // asrt size
     263           0 :     avio_wl32(out, MKTAG('a','s','r','t'));
     264           0 :     avio_wb32(out, 0); // version + flags
     265           0 :     avio_w8(out, 0); // QualityEntryCount
     266           0 :     avio_wb32(out, 1); // SegmentRunEntryCount
     267           0 :     avio_wb32(out, 1); // FirstSegment
     268           0 :     avio_wb32(out, final ? (os->fragment_index - 1) : 0xffffffff); // FragmentsPerSegment
     269           0 :     update_size(out, asrt_pos);
     270           0 :     avio_w8(out, 1); // FragmentRunTableCount
     271           0 :     afrt_pos = avio_tell(out);
     272           0 :     avio_wb32(out, 0); // afrt size
     273           0 :     avio_wl32(out, MKTAG('a','f','r','t'));
     274           0 :     avio_wb32(out, 0); // version + flags
     275           0 :     avio_wb32(out, 1000); // timescale
     276           0 :     avio_w8(out, 0); // QualityEntryCount
     277           0 :     avio_wb32(out, fragments); // FragmentRunEntryCount
     278           0 :     for (i = start; i < os->nb_fragments; i++) {
     279           0 :         avio_wb32(out, os->fragments[i]->n);
     280           0 :         avio_wb64(out, os->fragments[i]->start_time);
     281           0 :         avio_wb32(out, os->fragments[i]->duration);
     282             :     }
     283           0 :     update_size(out, afrt_pos);
     284           0 :     update_size(out, 0);
     285           0 :     ff_format_io_close(s, &out);
     286           0 :     return ff_rename(temp_filename, filename, s);
     287             : }
     288             : 
     289           0 : static int init_file(AVFormatContext *s, OutputStream *os, int64_t start_ts)
     290             : {
     291             :     int ret, i;
     292           0 :     ret = s->io_open(s, &os->out, os->temp_filename, AVIO_FLAG_WRITE, NULL);
     293           0 :     if (ret < 0)
     294           0 :         return ret;
     295           0 :     avio_wb32(os->out, 0);
     296           0 :     avio_wl32(os->out, MKTAG('m','d','a','t'));
     297           0 :     for (i = 0; i < os->nb_extra_packets; i++) {
     298           0 :         AV_WB24(os->extra_packets[i] + 4, start_ts);
     299           0 :         os->extra_packets[i][7] = (start_ts >> 24) & 0x7f;
     300           0 :         avio_write(os->out, os->extra_packets[i], os->extra_packet_sizes[i]);
     301             :     }
     302           0 :     return 0;
     303             : }
     304             : 
     305           0 : static void close_file(AVFormatContext *s, OutputStream *os)
     306             : {
     307           0 :     int64_t pos = avio_tell(os->out);
     308           0 :     avio_seek(os->out, 0, SEEK_SET);
     309           0 :     avio_wb32(os->out, pos);
     310           0 :     avio_flush(os->out);
     311           0 :     ff_format_io_close(s, &os->out);
     312           0 : }
     313             : 
     314           0 : static int hds_write_header(AVFormatContext *s)
     315             : {
     316           0 :     HDSContext *c = s->priv_data;
     317           0 :     int ret = 0, i;
     318             :     AVOutputFormat *oformat;
     319             : 
     320           0 :     if (mkdir(s->filename, 0777) == -1 && errno != EEXIST) {
     321           0 :         ret = AVERROR(errno);
     322           0 :         av_log(s, AV_LOG_ERROR , "Failed to create directory %s\n", s->filename);
     323           0 :         goto fail;
     324             :     }
     325             : 
     326           0 :     oformat = av_guess_format("flv", NULL, NULL);
     327           0 :     if (!oformat) {
     328           0 :         ret = AVERROR_MUXER_NOT_FOUND;
     329           0 :         goto fail;
     330             :     }
     331             : 
     332           0 :     c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams));
     333           0 :     if (!c->streams) {
     334           0 :         ret = AVERROR(ENOMEM);
     335           0 :         goto fail;
     336             :     }
     337             : 
     338           0 :     for (i = 0; i < s->nb_streams; i++) {
     339           0 :         OutputStream *os = &c->streams[c->nb_streams];
     340             :         AVFormatContext *ctx;
     341           0 :         AVStream *st = s->streams[i];
     342             : 
     343           0 :         if (!st->codecpar->bit_rate) {
     344           0 :             av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
     345           0 :             ret = AVERROR(EINVAL);
     346           0 :             goto fail;
     347             :         }
     348           0 :         if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
     349           0 :             if (os->has_video) {
     350           0 :                 c->nb_streams++;
     351           0 :                 os++;
     352             :             }
     353           0 :             os->has_video = 1;
     354           0 :         } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
     355           0 :             if (os->has_audio) {
     356           0 :                 c->nb_streams++;
     357           0 :                 os++;
     358             :             }
     359           0 :             os->has_audio = 1;
     360             :         } else {
     361           0 :             av_log(s, AV_LOG_ERROR, "Unsupported stream type in stream %d\n", i);
     362           0 :             ret = AVERROR(EINVAL);
     363           0 :             goto fail;
     364             :         }
     365           0 :         os->bitrate += s->streams[i]->codecpar->bit_rate;
     366             : 
     367           0 :         if (!os->ctx) {
     368           0 :             os->first_stream = i;
     369           0 :             ctx = avformat_alloc_context();
     370           0 :             if (!ctx) {
     371           0 :                 ret = AVERROR(ENOMEM);
     372           0 :                 goto fail;
     373             :             }
     374           0 :             os->ctx = ctx;
     375           0 :             ctx->oformat = oformat;
     376           0 :             ctx->interrupt_callback = s->interrupt_callback;
     377           0 :             ctx->flags = s->flags;
     378             : 
     379           0 :             ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf),
     380             :                                          AVIO_FLAG_WRITE, os,
     381             :                                          NULL, hds_write, NULL);
     382           0 :             if (!ctx->pb) {
     383           0 :                 ret = AVERROR(ENOMEM);
     384           0 :                 goto fail;
     385             :             }
     386             :         } else {
     387           0 :             ctx = os->ctx;
     388             :         }
     389           0 :         s->streams[i]->id = c->nb_streams;
     390             : 
     391           0 :         if (!(st = avformat_new_stream(ctx, NULL))) {
     392           0 :             ret = AVERROR(ENOMEM);
     393           0 :             goto fail;
     394             :         }
     395           0 :         avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
     396           0 :         st->codecpar->codec_tag = 0;
     397           0 :         st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
     398           0 :         st->time_base = s->streams[i]->time_base;
     399             :     }
     400           0 :     if (c->streams[c->nb_streams].ctx)
     401           0 :         c->nb_streams++;
     402             : 
     403           0 :     for (i = 0; i < c->nb_streams; i++) {
     404           0 :         OutputStream *os = &c->streams[i];
     405             :         int j;
     406           0 :         if ((ret = avformat_write_header(os->ctx, NULL)) < 0) {
     407           0 :              goto fail;
     408             :         }
     409           0 :         os->ctx_inited = 1;
     410           0 :         avio_flush(os->ctx->pb);
     411           0 :         for (j = 0; j < os->ctx->nb_streams; j++)
     412           0 :             s->streams[os->first_stream + j]->time_base = os->ctx->streams[j]->time_base;
     413             : 
     414           0 :         snprintf(os->temp_filename, sizeof(os->temp_filename),
     415           0 :                  "%s/stream%d_temp", s->filename, i);
     416           0 :         ret = init_file(s, os, 0);
     417           0 :         if (ret < 0)
     418           0 :             goto fail;
     419             : 
     420           0 :         if (!os->has_video && c->min_frag_duration <= 0) {
     421           0 :             av_log(s, AV_LOG_WARNING,
     422             :                    "No video stream in output stream %d and no min frag duration set\n", i);
     423             :         }
     424           0 :         os->fragment_index = 1;
     425           0 :         write_abst(s, os, 0);
     426             :     }
     427           0 :     ret = write_manifest(s, 0);
     428             : 
     429           0 : fail:
     430           0 :     if (ret)
     431           0 :         hds_free(s);
     432           0 :     return ret;
     433             : }
     434             : 
     435           0 : static int add_fragment(OutputStream *os, const char *file,
     436             :                         int64_t start_time, int64_t duration)
     437             : {
     438             :     Fragment *frag;
     439           0 :     if (duration == 0)
     440           0 :         duration = 1;
     441           0 :     if (os->nb_fragments >= os->fragments_size) {
     442             :         int ret;
     443           0 :         os->fragments_size = (os->fragments_size + 1) * 2;
     444           0 :         if ((ret = av_reallocp_array(&os->fragments, os->fragments_size,
     445             :                                      sizeof(*os->fragments))) < 0) {
     446           0 :             os->fragments_size = 0;
     447           0 :             os->nb_fragments   = 0;
     448           0 :             return ret;
     449             :         }
     450             :     }
     451           0 :     frag = av_mallocz(sizeof(*frag));
     452           0 :     if (!frag)
     453           0 :         return AVERROR(ENOMEM);
     454           0 :     av_strlcpy(frag->file, file, sizeof(frag->file));
     455           0 :     frag->start_time = start_time;
     456           0 :     frag->duration   = duration;
     457           0 :     frag->n          = os->fragment_index;
     458           0 :     os->fragments[os->nb_fragments++] = frag;
     459           0 :     os->fragment_index++;
     460           0 :     return 0;
     461             : }
     462             : 
     463           0 : static int hds_flush(AVFormatContext *s, OutputStream *os, int final,
     464             :                      int64_t end_ts)
     465             : {
     466           0 :     HDSContext *c = s->priv_data;
     467           0 :     int i, ret = 0;
     468             :     char target_filename[1024];
     469           0 :     int index = s->streams[os->first_stream]->id;
     470             : 
     471           0 :     if (!os->packets_written)
     472           0 :         return 0;
     473             : 
     474           0 :     avio_flush(os->ctx->pb);
     475           0 :     os->packets_written = 0;
     476           0 :     close_file(s, os);
     477             : 
     478           0 :     snprintf(target_filename, sizeof(target_filename),
     479           0 :              "%s/stream%dSeg1-Frag%d", s->filename, index, os->fragment_index);
     480           0 :     ret = ff_rename(os->temp_filename, target_filename, s);
     481           0 :     if (ret < 0)
     482           0 :         return ret;
     483           0 :     add_fragment(os, target_filename, os->frag_start_ts, end_ts - os->frag_start_ts);
     484             : 
     485           0 :     if (!final) {
     486           0 :         ret = init_file(s, os, end_ts);
     487           0 :         if (ret < 0)
     488           0 :             return ret;
     489             :     }
     490             : 
     491           0 :     if (c->window_size || (final && c->remove_at_exit)) {
     492           0 :         int remove = os->nb_fragments - c->window_size - c->extra_window_size;
     493           0 :         if (final && c->remove_at_exit)
     494           0 :             remove = os->nb_fragments;
     495           0 :         if (remove > 0) {
     496           0 :             for (i = 0; i < remove; i++) {
     497           0 :                 unlink(os->fragments[i]->file);
     498           0 :                 av_freep(&os->fragments[i]);
     499             :             }
     500           0 :             os->nb_fragments -= remove;
     501           0 :             memmove(os->fragments, os->fragments + remove,
     502           0 :                     os->nb_fragments * sizeof(*os->fragments));
     503             :         }
     504             :     }
     505             : 
     506           0 :     if (ret >= 0)
     507           0 :         ret = write_abst(s, os, final);
     508           0 :     return ret;
     509             : }
     510             : 
     511           0 : static int hds_write_packet(AVFormatContext *s, AVPacket *pkt)
     512             : {
     513           0 :     HDSContext *c = s->priv_data;
     514           0 :     AVStream *st = s->streams[pkt->stream_index];
     515           0 :     OutputStream *os = &c->streams[s->streams[pkt->stream_index]->id];
     516           0 :     int64_t end_dts = os->fragment_index * (int64_t)c->min_frag_duration;
     517             :     int ret;
     518             : 
     519           0 :     if (st->first_dts == AV_NOPTS_VALUE)
     520           0 :         st->first_dts = pkt->dts;
     521             : 
     522           0 :     if ((!os->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
     523           0 :         av_compare_ts(pkt->dts - st->first_dts, st->time_base,
     524           0 :                       end_dts, AV_TIME_BASE_Q) >= 0 &&
     525           0 :         pkt->flags & AV_PKT_FLAG_KEY && os->packets_written) {
     526             : 
     527           0 :         if ((ret = hds_flush(s, os, 0, pkt->dts)) < 0)
     528           0 :             return ret;
     529             :     }
     530             : 
     531             :     // Note, these fragment start timestamps, that represent a whole
     532             :     // OutputStream, assume all streams in it have the same time base.
     533           0 :     if (!os->packets_written)
     534           0 :         os->frag_start_ts = pkt->dts;
     535           0 :     os->last_ts = pkt->dts;
     536             : 
     537           0 :     os->packets_written++;
     538           0 :     return ff_write_chained(os->ctx, pkt->stream_index - os->first_stream, pkt, s, 0);
     539             : }
     540             : 
     541           0 : static int hds_write_trailer(AVFormatContext *s)
     542             : {
     543           0 :     HDSContext *c = s->priv_data;
     544             :     int i;
     545             : 
     546           0 :     for (i = 0; i < c->nb_streams; i++)
     547           0 :         hds_flush(s, &c->streams[i], 1, c->streams[i].last_ts);
     548           0 :     write_manifest(s, 1);
     549             : 
     550           0 :     if (c->remove_at_exit) {
     551             :         char filename[1024];
     552           0 :         snprintf(filename, sizeof(filename), "%s/index.f4m", s->filename);
     553           0 :         unlink(filename);
     554           0 :         for (i = 0; i < c->nb_streams; i++) {
     555           0 :             snprintf(filename, sizeof(filename), "%s/stream%d.abst", s->filename, i);
     556           0 :             unlink(filename);
     557             :         }
     558           0 :         rmdir(s->filename);
     559             :     }
     560             : 
     561           0 :     hds_free(s);
     562           0 :     return 0;
     563             : }
     564             : 
     565             : #define OFFSET(x) offsetof(HDSContext, x)
     566             : #define E AV_OPT_FLAG_ENCODING_PARAM
     567             : static const AVOption options[] = {
     568             :     { "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
     569             :     { "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
     570             :     { "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT_MAX, E },
     571             :     { "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     572             :     { NULL },
     573             : };
     574             : 
     575             : static const AVClass hds_class = {
     576             :     .class_name = "HDS muxer",
     577             :     .item_name  = av_default_item_name,
     578             :     .option     = options,
     579             :     .version    = LIBAVUTIL_VERSION_INT,
     580             : };
     581             : 
     582             : AVOutputFormat ff_hds_muxer = {
     583             :     .name           = "hds",
     584             :     .long_name      = NULL_IF_CONFIG_SMALL("HDS Muxer"),
     585             :     .priv_data_size = sizeof(HDSContext),
     586             :     .audio_codec    = AV_CODEC_ID_AAC,
     587             :     .video_codec    = AV_CODEC_ID_H264,
     588             :     .flags          = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
     589             :     .write_header   = hds_write_header,
     590             :     .write_packet   = hds_write_packet,
     591             :     .write_trailer  = hds_write_trailer,
     592             :     .priv_class     = &hds_class,
     593             : };

Generated by: LCOV version 1.13