libavformat/segment.c
Go to the documentation of this file.
00001 /*
00002  * Generic segmenter
00003  * Copyright (c) 2011, Luca Barbato
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include <strings.h>
00023 #include <float.h>
00024 
00025 #include "avformat.h"
00026 #include "internal.h"
00027 
00028 #include "libavutil/log.h"
00029 #include "libavutil/opt.h"
00030 #include "libavutil/avstring.h"
00031 #include "libavutil/parseutils.h"
00032 #include "libavutil/mathematics.h"
00033 
00034 typedef struct {
00035     const AVClass *class;  
00036     int number;
00037     AVFormatContext *avf;
00038     char *format;          
00039     char *list;            
00040     float time;            
00041     int  size;             
00042     int64_t offset_time;
00043     int64_t recording_time;
00044     int has_video;
00045     AVIOContext *pb;
00046 } SegmentContext;
00047 
00048 static int segment_start(AVFormatContext *s)
00049 {
00050     SegmentContext *c = s->priv_data;
00051     AVFormatContext *oc = c->avf;
00052     int err = 0;
00053 
00054     if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
00055                               s->filename, c->number++) < 0)
00056         return AVERROR(EINVAL);
00057 
00058     if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00059                           &s->interrupt_callback, NULL)) < 0)
00060         return err;
00061 
00062     if (!oc->priv_data && oc->oformat->priv_data_size > 0) {
00063         oc->priv_data = av_mallocz(oc->oformat->priv_data_size);
00064         if (!oc->priv_data) {
00065             avio_close(oc->pb);
00066             return AVERROR(ENOMEM);
00067         }
00068         if (oc->oformat->priv_class) {
00069             *(const AVClass**)oc->priv_data = oc->oformat->priv_class;
00070             av_opt_set_defaults(oc->priv_data);
00071         }
00072     }
00073 
00074     if ((err = oc->oformat->write_header(oc)) < 0) {
00075         goto fail;
00076     }
00077 
00078     return 0;
00079 
00080 fail:
00081     avio_close(oc->pb);
00082     av_freep(&oc->priv_data);
00083 
00084     return err;
00085 }
00086 
00087 static int segment_end(AVFormatContext *oc)
00088 {
00089     int ret = 0;
00090 
00091     if (oc->oformat->write_trailer)
00092         ret = oc->oformat->write_trailer(oc);
00093 
00094     avio_close(oc->pb);
00095     if (oc->oformat->priv_class)
00096         av_opt_free(oc->priv_data);
00097     av_freep(&oc->priv_data);
00098 
00099     return ret;
00100 }
00101 
00102 static int seg_write_header(AVFormatContext *s)
00103 {
00104     SegmentContext *seg = s->priv_data;
00105     AVFormatContext *oc;
00106     int ret, i;
00107 
00108     seg->number = 0;
00109     seg->offset_time = 0;
00110     seg->recording_time = seg->time * 1000000;
00111 
00112     if (seg->list)
00113         if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
00114                               &s->interrupt_callback, NULL)) < 0)
00115             return ret;
00116 
00117     for (i = 0; i< s->nb_streams; i++)
00118         seg->has_video +=
00119             (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO);
00120 
00121     if (seg->has_video > 1)
00122         av_log(s, AV_LOG_WARNING,
00123                "More than a single video stream present, "
00124                "expect issues decoding it.\n");
00125 
00126     oc = avformat_alloc_context();
00127 
00128     if (!oc) {
00129         ret = AVERROR(ENOMEM);
00130         goto fail;
00131     }
00132 
00133     oc->oformat = av_guess_format(seg->format, s->filename, NULL);
00134 
00135     if (!oc->oformat) {
00136         ret = AVERROR_MUXER_NOT_FOUND;
00137         goto fail;
00138     }
00139     if (oc->oformat->flags & AVFMT_NOFILE) {
00140         av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
00141                oc->oformat->name);
00142         ret = AVERROR(EINVAL);
00143         goto fail;
00144     }
00145 
00146     seg->avf = oc;
00147 
00148     oc->streams = s->streams;
00149     oc->nb_streams = s->nb_streams;
00150 
00151     if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
00152                               s->filename, seg->number++) < 0) {
00153         ret = AVERROR(EINVAL);
00154         goto fail;
00155     }
00156 
00157     if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
00158                           &s->interrupt_callback, NULL)) < 0)
00159         goto fail;
00160 
00161     if ((ret = avformat_write_header(oc, NULL)) < 0) {
00162         avio_close(oc->pb);
00163         goto fail;
00164     }
00165 
00166     if (seg->list) {
00167         avio_printf(seg->pb, "%s\n", oc->filename);
00168         avio_flush(seg->pb);
00169     }
00170 
00171 fail:
00172     if (ret) {
00173         oc->streams = NULL;
00174         oc->nb_streams = 0;
00175         if (seg->list)
00176             avio_close(seg->pb);
00177         avformat_free_context(oc);
00178     }
00179     return ret;
00180 }
00181 
00182 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
00183 {
00184     SegmentContext *seg = s->priv_data;
00185     AVFormatContext *oc = seg->avf;
00186     AVStream *st = oc->streams[pkt->stream_index];
00187     int64_t end_pts = seg->recording_time * seg->number;
00188     int ret;
00189 
00190     if ((seg->has_video && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) &&
00191         av_compare_ts(pkt->pts, st->time_base,
00192                       end_pts, AV_TIME_BASE_Q) >= 0 &&
00193         pkt->flags & AV_PKT_FLAG_KEY) {
00194 
00195         av_log(s, AV_LOG_DEBUG, "Next segment starts at %d %"PRId64"\n",
00196                pkt->stream_index, pkt->pts);
00197 
00198         ret = segment_end(oc);
00199 
00200         if (!ret)
00201             ret = segment_start(s);
00202 
00203         if (ret)
00204             goto fail;
00205 
00206         if (seg->list) {
00207             avio_printf(seg->pb, "%s\n", oc->filename);
00208             avio_flush(seg->pb);
00209             if (seg->size && !(seg->number % seg->size)) {
00210                 avio_close(seg->pb);
00211                 if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
00212                                       &s->interrupt_callback, NULL)) < 0)
00213                     goto fail;
00214 
00215             }
00216         }
00217     }
00218 
00219     ret = oc->oformat->write_packet(oc, pkt);
00220 
00221 fail:
00222     if (ret < 0) {
00223         oc->streams = NULL;
00224         oc->nb_streams = 0;
00225         if (seg->list)
00226             avio_close(seg->pb);
00227         avformat_free_context(oc);
00228     }
00229 
00230     return ret;
00231 }
00232 
00233 static int seg_write_trailer(struct AVFormatContext *s)
00234 {
00235     SegmentContext *seg = s->priv_data;
00236     AVFormatContext *oc = seg->avf;
00237     int ret = segment_end(oc);
00238     if (seg->list)
00239         avio_close(seg->pb);
00240     oc->streams = NULL;
00241     oc->nb_streams = 0;
00242     avformat_free_context(oc);
00243     return ret;
00244 }
00245 
00246 #define OFFSET(x) offsetof(SegmentContext, x)
00247 #define E AV_OPT_FLAG_ENCODING_PARAM
00248 static const AVOption options[] = {
00249     { "segment_format",    "container format used for the segments",  OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
00250     { "segment_time",      "segment length in seconds",               OFFSET(time),    AV_OPT_TYPE_FLOAT,  {.dbl = 2},     0, FLT_MAX, E },
00251     { "segment_list",      "output the segment list",                 OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
00252     { "segment_list_size", "maximum number of playlist entries",      OFFSET(size),    AV_OPT_TYPE_INT,    {.dbl = 5},     0, INT_MAX, E },
00253     { NULL },
00254 };
00255 
00256 static const AVClass seg_class = {
00257     .class_name = "segment muxer",
00258     .item_name  = av_default_item_name,
00259     .option     = options,
00260     .version    = LIBAVUTIL_VERSION_INT,
00261 };
00262 
00263 
00264 AVOutputFormat ff_segment_muxer = {
00265     .name           = "segment",
00266     .long_name      = NULL_IF_CONFIG_SMALL("segment muxer"),
00267     .priv_data_size = sizeof(SegmentContext),
00268     .flags          = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
00269     .write_header   = seg_write_header,
00270     .write_packet   = seg_write_packet,
00271     .write_trailer  = seg_write_trailer,
00272     .priv_class     = &seg_class,
00273 };