diff mbox

[FFmpeg-devel,03/15] avformat/dashenc: allow splitting fragments following P-Frame reordering

Message ID 20191221180252.6091-3-jamrial@gmail.com
State New
Headers show

Commit Message

James Almer Dec. 21, 2019, 6:02 p.m. UTC
Currently implemented by using the pic_type value from AV_PKT_DATA_QUALITY_STATS
packet side data.

Signed-off-by: James Almer <jamrial@gmail.com>
---
 libavformat/dashenc.c | 45 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 05f2700d16..82941eb459 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -62,6 +62,7 @@  enum {
     FRAG_TYPE_NONE = 0,
     FRAG_TYPE_EVERY_FRAME,
     FRAG_TYPE_DURATION,
+    FRAG_TYPE_PFRAMES,
     FRAG_TYPE_NB
 };
 
@@ -100,6 +101,7 @@  typedef struct OutputStream {
     Segment **segments;
     int64_t first_pts, start_pts, max_pts;
     int64_t last_dts, last_pts;
+    int last_flags;
     int bit_rate;
     SegmentType segment_type;  /* segment type selected for this particular stream */
     const char *format_name;
@@ -115,6 +117,7 @@  typedef struct OutputStream {
     char temp_path[1024];
     double availability_time_offset;
     int total_pkt_size;
+    int total_pkt_duration;
     int muxer_overhead;
     int frag_type;
 } OutputStream;
@@ -1362,6 +1365,8 @@  static int dash_init(AVFormatContext *s)
             av_log(s, AV_LOG_WARNING, "frag_type set to duration for stream %d but no frag_duration set\n", i);
             os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE;
         }
+        if (os->frag_type == FRAG_TYPE_PFRAMES && st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
+            os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE;
 
         if (os->segment_type == SEGMENT_TYPE_MP4) {
             if (c->streaming)
@@ -1430,6 +1435,8 @@  static int dash_init(AVFormatContext *s)
         av_log(s, AV_LOG_WARNING, "no video stream and no seg duration set\n");
         return AVERROR(EINVAL);
     }
+    if (!c->has_video && c->frag_type == FRAG_TYPE_PFRAMES)
+        av_log(s, AV_LOG_WARNING, "no video stream and P-frame fragmentation set\n");
 
     c->nr_of_streams_flushed = 0;
 
@@ -1698,6 +1705,7 @@  static int dash_flush(AVFormatContext *s, int final, int stream)
                                  av_rescale_q(os->max_pts - os->start_pts,
                                               st->time_base, AV_TIME_BASE_Q);
         os->total_pkt_size = 0;
+        os->total_pkt_duration = 0;
 
         if (!os->bit_rate) {
             // calculate average bitrate of first segment
@@ -1799,6 +1807,9 @@  static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
                         sizeof(c->availability_start_time));
     }
 
+    if (!os->packets_written)
+        os->availability_time_offset = 0;
+
     if (!os->availability_time_offset &&
         (os->frag_type == FRAG_TYPE_NONE ||
          os->frag_type == FRAG_TYPE_DURATION ||
@@ -1866,11 +1877,40 @@  static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
         os->max_pts = pkt->pts + pkt->duration;
     else
         os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration);
-    os->packets_written++;
-    os->total_pkt_size += pkt->size;
+
+    if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+        os->frag_type == FRAG_TYPE_PFRAMES &&
+        os->packets_written) {
+        int side_size, pic_type = 0;
+        uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, &side_size);
+        if (side && side_size > 4)
+            pic_type = side[4];
+
+        if ((pic_type == AV_PICTURE_TYPE_P &&
+             st->codecpar->video_delay &&
+             !(os->last_flags & AV_PKT_FLAG_KEY)) ||
+            pkt->flags & AV_PKT_FLAG_KEY) {
+            ret = av_write_frame(os->ctx, NULL);
+            if (ret < 0)
+                return ret;
+
+            if (!os->availability_time_offset) {
+                int64_t frag_duration = av_rescale_q(os->total_pkt_duration, st->time_base,
+                                                     AV_TIME_BASE_Q);
+                os->availability_time_offset = ((double) os->seg_duration -
+                                                 frag_duration) / AV_TIME_BASE;
+            }
+        }
+    }
+
     if ((ret = ff_write_chained(os->ctx, 0, pkt, s, 0)) < 0)
         return ret;
 
+    os->packets_written++;
+    os->total_pkt_size += pkt->size;
+    os->total_pkt_duration += pkt->duration;
+    os->last_flags = pkt->flags;
+
     if (!os->init_range_length)
         flush_init_segment(s, os);
 
@@ -1997,6 +2037,7 @@  static const AVOption options[] = {
     { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_NONE }, 0, UINT_MAX, E, "frag_type"},
     { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_EVERY_FRAME }, 0, UINT_MAX, E, "frag_type"},
     { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_DURATION }, 0, UINT_MAX, E, "frag_type"},
+    { "pframes", "fragment at keyframes and following P-Frame reordering (Video only, experimental)", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_PFRAMES }, 0, UINT_MAX, E, "frag_type"},
     { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
     { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },