Browse code

lavf/segment: add -segment_times option

Address trac ticket #1504.

Stefano Sabatini authored on 2012/01/29 06:36:38
Showing 3 changed files
... ...
@@ -474,6 +474,9 @@ the segment start and end time expressed in seconds.
474 474
 Default value is "flat".
475 475
 @item segment_time @var{time}
476 476
 Set segment duration to @var{time}. Default value is "2".
477
+@item segment_times @var{times}
478
+Specify a list of split points. @var{times} contains a list of comma
479
+separated duration specifications, in increasing order.
477 480
 @item segment_wrap @var{limit}
478 481
 Wrap around segment index once it reaches @var{limit}.
479 482
 @end table
... ...
@@ -490,6 +493,13 @@ ffmpeg -i in.mkv -codec copy -map 0 -f segment -segment_list out.list out%03d.nu
490 490
 @end example
491 491
 
492 492
 @item
493
+As the example above, but segment the input file according to the split
494
+points specified by the @var{segment_times} option:
495
+@example
496
+ffmpeg -i in.mkv -codec copy -map 0 -f segment -segment_list_type ext -segment_list out.list -segment_list_size 0 -segment_times 1,2,3,5,8,13,21 out%03d.nut
497
+@end example
498
+
499
+@item
493 500
 To convert the @file{in.mkv} to TS segments using the @code{libx264}
494 501
 and @code{libfaac} encoders:
495 502
 @example
... ...
@@ -24,6 +24,7 @@
24 24
 #include "avformat.h"
25 25
 #include "internal.h"
26 26
 
27
+#include "libavutil/avassert.h"
27 28
 #include "libavutil/log.h"
28 29
 #include "libavutil/opt.h"
29 30
 #include "libavutil/avstring.h"
... ...
@@ -48,6 +49,9 @@ typedef struct {
48 48
     int  wrap;             ///< number after which the index wraps
49 49
     char *time_str;        ///< segment duration specification string
50 50
     int64_t time;          ///< segment duration
51
+    char *times_str;       ///< segment times specification string
52
+    int64_t *times;        ///< list of segment interval specification
53
+    int nb_times;          ///< number of elments in the times array
51 54
     int has_video;
52 55
     double start_time, end_time;
53 56
 } SegmentContext;
... ...
@@ -136,6 +140,59 @@ end:
136 136
     return ret;
137 137
 }
138 138
 
139
+static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
140
+                       const char *times_str)
141
+{
142
+    char *p;
143
+    int i, ret = 0;
144
+    char *times_str1 = av_strdup(times_str);
145
+    char *saveptr = NULL;
146
+
147
+    if (!times_str1)
148
+        return AVERROR(ENOMEM);
149
+
150
+#define FAIL(err) ret = err; goto end
151
+
152
+    *nb_times = 1;
153
+    for (p = times_str1; *p; p++)
154
+        if (*p == ',')
155
+            (*nb_times)++;
156
+
157
+    *times = av_malloc(sizeof(**times) * *nb_times);
158
+    if (!*times) {
159
+        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
160
+        FAIL(AVERROR(ENOMEM));
161
+    }
162
+
163
+    p = times_str1;
164
+    for (i = 0; i < *nb_times; i++) {
165
+        int64_t t;
166
+        char *tstr = av_strtok(p, ",", &saveptr);
167
+        av_assert0(tstr);
168
+        p = NULL;
169
+
170
+        ret = av_parse_time(&t, tstr, 1);
171
+        if (ret < 0) {
172
+            av_log(log_ctx, AV_LOG_ERROR,
173
+                   "Invalid time duration specification in %s\n", p);
174
+            FAIL(AVERROR(EINVAL));
175
+        }
176
+        (*times)[i] = t;
177
+
178
+        /* check on monotonicity */
179
+        if (i && (*times)[i-1] > (*times)[i]) {
180
+            av_log(log_ctx, AV_LOG_ERROR,
181
+                   "Specified time %f is greater than the following time %f\n",
182
+                   (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
183
+            FAIL(AVERROR(EINVAL));
184
+        }
185
+    }
186
+
187
+end:
188
+    av_free(times_str1);
189
+    return ret;
190
+}
191
+
139 192
 static int seg_write_header(AVFormatContext *s)
140 193
 {
141 194
     SegmentContext *seg = s->priv_data;
... ...
@@ -144,11 +201,25 @@ static int seg_write_header(AVFormatContext *s)
144 144
 
145 145
     seg->number = 0;
146 146
 
147
-    if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
147
+    if (seg->time_str && seg->times_str) {
148 148
         av_log(s, AV_LOG_ERROR,
149
-               "Invalid time duration specification '%s' for segment_time option\n",
150
-               seg->time_str);
151
-        return ret;
149
+               "segment_time and segment_times options are mutually exclusive, select just one of them\n");
150
+        return AVERROR(EINVAL);
151
+    }
152
+
153
+    if (seg->times_str) {
154
+        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
155
+            return ret;
156
+    } else {
157
+        /* set default value if not specified */
158
+        if (!seg->time_str)
159
+            seg->time_str = av_strdup("2");
160
+        if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
161
+            av_log(s, AV_LOG_ERROR,
162
+                   "Invalid time duration specification '%s' for segment_time option\n",
163
+                   seg->time_str);
164
+            return ret;
165
+        }
152 166
     }
153 167
 
154 168
     oc = avformat_alloc_context();
... ...
@@ -221,9 +292,15 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
221 221
     SegmentContext *seg = s->priv_data;
222 222
     AVFormatContext *oc = seg->avf;
223 223
     AVStream *st = oc->streams[pkt->stream_index];
224
-    int64_t end_pts = seg->time * seg->number;
224
+    int64_t end_pts;
225 225
     int ret;
226 226
 
227
+    if (seg->times) {
228
+        end_pts = seg->number <= seg->nb_times ? seg->times[seg->number-1] : INT64_MAX;
229
+    } else {
230
+        end_pts = seg->time * seg->number;
231
+    }
232
+
227 233
     /* if the segment has video, start a new segment *only* with a key video frame */
228 234
     if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
229 235
         av_compare_ts(pkt->pts, st->time_base,
... ...
@@ -264,6 +341,7 @@ static int seg_write_trailer(struct AVFormatContext *s)
264 264
         avio_close(seg->list_pb);
265 265
 
266 266
     av_opt_free(seg);
267
+    av_freep(&seg->times);
267 268
 
268 269
     oc->streams = NULL;
269 270
     oc->nb_streams = 0;
... ...
@@ -280,7 +358,8 @@ static const AVOption options[] = {
280 280
     { "segment_list_type", "set the segment list type",                  OFFSET(list_type), AV_OPT_TYPE_INT,  {.dbl = LIST_TYPE_FLAT}, 0, LIST_TYPE_NB-1, E, "list_type" },
281 281
     { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" },
282 282
     { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, 0, "list_type" },
283
-    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = "2"},   0, 0,       E },
283
+    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
284
+    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
284 285
     { "segment_wrap",      "set number after which the index wraps",     OFFSET(wrap),    AV_OPT_TYPE_INT,    {.dbl = 0},     0, INT_MAX, E },
285 286
     { NULL },
286 287
 };
... ...
@@ -31,7 +31,7 @@
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 54
33 33
 #define LIBAVFORMAT_VERSION_MINOR 15
34
-#define LIBAVFORMAT_VERSION_MICRO 102
34
+#define LIBAVFORMAT_VERSION_MICRO 103
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \