[FFmpeg-devel] [PATCH 6/6] concat: support seeking inside sub-files [WIP].

Nicolas George george at nsup.org
Fri Feb 21 20:50:18 CET 2014


From: Nicolas George <nicolas.george at normalesup.org>

Signed-off-by: Nicolas George <george at nsup.org>
---
 libavformat/concatdec.c | 112 +++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 102 insertions(+), 10 deletions(-)

diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c
index 979499a..9d2c73e 100644
--- a/libavformat/concatdec.c
+++ b/libavformat/concatdec.c
@@ -19,16 +19,21 @@
  */
 
 #include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/opt.h"
 #include "libavutil/parseutils.h"
 #include "avformat.h"
 #include "internal.h"
 #include "url.h"
 
+#include "libavutil/timestamp.h"
+
 typedef struct {
     char *url;
     int64_t start_time;
+    int64_t delta;
     int64_t duration;
+    int64_t seek_time;
 } ConcatFile;
 
 typedef struct {
@@ -115,6 +120,8 @@ static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
     file->url        = url;
     file->start_time = AV_NOPTS_VALUE;
     file->duration   = AV_NOPTS_VALUE;
+    file->seek_time  = AV_NOPTS_VALUE;
+    file->delta      = AV_NOPTS_VALUE;
 
     return 0;
 
@@ -149,6 +156,21 @@ static int open_file(AVFormatContext *avf, unsigned fileno)
         file->start_time = !fileno ? 0 :
                            cat->files[fileno - 1].start_time +
                            cat->files[fileno - 1].duration;
+    if (file->delta == AV_NOPTS_VALUE) {
+        int64_t first_pts = cat->avf->start_time;
+        if (file->seek_time != AV_NOPTS_VALUE)
+            first_pts = file->seek_time;
+        file->delta = file->start_time - first_pts;
+    }
+    if (file->seek_time != AV_NOPTS_VALUE) {
+        ret = avformat_seek_file(cat->avf, -1, 0, file->seek_time,
+                                 file->seek_time, 0);
+        if (ret < 0) {
+            av_log(avf, AV_LOG_ERROR, "Error seeking in '%s': %s\n",
+                   file->url, av_err2str(ret));
+            return ret;
+        }
+    }
     return 0;
 }
 
@@ -207,6 +229,20 @@ static int concat_read_header(AVFormatContext *avf)
                 FAIL(ret);
             }
             file->duration = dur;
+        } else if (!strcmp(keyword, "seek")) {
+            char *time_str = get_keyword(&cursor);
+            int64_t time;
+            if (!file) {
+                av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n",
+                       line);
+                FAIL(AVERROR_INVALIDDATA);
+            }
+            if ((ret = av_parse_time(&time, time_str, 1)) < 0) {
+                av_log(avf, AV_LOG_ERROR, "Line %d: invalid seek time '%s'\n",
+                       line, time_str);
+                FAIL(ret);
+            }
+            file->seek_time = time;
         } else if (!strcmp(keyword, "ffconcat")) {
             char *ver_kw  = get_keyword(&cursor);
             char *ver_val = get_keyword(&cursor);
@@ -275,11 +311,71 @@ static int open_next_file(AVFormatContext *avf)
     return open_file(avf, fileno);
 }
 
+static uint8_t *get_skip_side_data(AVPacket *pkt)
+{
+    uint8_t *side_data;
+
+    /* TODO use if already present */
+    side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
+    return side_data;
+}
+
+static int alter_packet_ts(AVFormatContext *avf, AVPacket *pkt)
+{
+    ConcatContext *cat = avf->priv_data;
+    ConcatFile *file = cat->cur_file;
+    AVStream *st = cat->avf->streams[pkt->stream_index];
+    AVRational tb = st->time_base;
+    AVRational rtb = { 1, st->codec->sample_rate };
+    int64_t real_pts = pkt->pts;
+    int64_t delta = av_rescale_q(file->delta, AV_TIME_BASE_Q, tb);
+    int64_t skip;
+    uint8_t *side_data;
+
+    if (pkt->pts != AV_NOPTS_VALUE)
+        pkt->pts += delta;
+    if (pkt->dts != AV_NOPTS_VALUE)
+        pkt->dts += delta;
+
+    if (file->seek_time != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE &&
+        av_rescale_q(real_pts, tb, AV_TIME_BASE_Q) < file->seek_time) {
+        switch (st->codec->codec_type) {
+
+        case AVMEDIA_TYPE_VIDEO:
+            pkt->flags |= AV_PKT_FLAG_DISCARD;
+            pkt->pts = file->start_time;
+            pkt->dts = AV_NOPTS_VALUE;
+            break;
+
+        case AVMEDIA_TYPE_AUDIO:
+            if (!rtb.den) {
+                av_log(cat->avf, AV_LOG_ERROR,
+                       "Sample rate not set, accurate seeking impossible");
+                return 0;
+            }
+            skip = av_rescale_q(file->seek_time, AV_TIME_BASE_Q, rtb)
+                 - av_rescale_q(real_pts,        tb,             rtb);
+            if (!(side_data = get_skip_side_data(pkt)))
+                return AVERROR(ENOMEM);
+            skip = av_clip(skip, 0, INT_MAX);
+            skip = FFMAX(skip, AV_RL32(side_data));
+            AV_WL32(side_data, skip);
+            pkt->flags |= AV_PKT_FLAG_DISCARD;
+            break;
+
+        default:
+            av_log(avf, AV_LOG_WARNING,
+                   "Frame-accurate seeking not implemented for %s streams\n",
+                   av_get_media_type_string(st->codec->codec_type));
+        }
+    }
+    return 0;
+}
+
 static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
 {
     ConcatContext *cat = avf->priv_data;
     int ret;
-    int64_t delta;
 
     while (1) {
         if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
@@ -288,15 +384,11 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
     }
     if (ret < 0)
         return ret;
-
-    delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
-                         AV_TIME_BASE_Q,
-                         cat->avf->streams[pkt->stream_index]->time_base);
-    if (pkt->pts != AV_NOPTS_VALUE)
-        pkt->pts += delta;
-    if (pkt->dts != AV_NOPTS_VALUE)
-        pkt->dts += delta;
-    return ret;
+    if ((ret = alter_packet_ts(avf, pkt)) < 0) {
+        av_free_packet(pkt);
+        return ret;
+    }
+    return 0;
 }
 
 static void rescale_interval(AVRational tb_in, AVRational tb_out,
-- 
1.8.5.3



More information about the ffmpeg-devel mailing list