--- segment.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ segment.h | 114 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 segment.c create mode 100644 segment.h
diff --git a/segment.c b/segment.c new file mode 100644 index 0000000..3afe5aa --- /dev/null +++ b/segment.c @@ -0,0 +1,161 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include "segment.h" +#include <pthread.h> + +#include <libavutil/opt.h> +#include <libavutil/log.h> + + +void segment_save(struct Segment *seg, const char *filename) +{ + AVFormatContext *ofmt_ctx = NULL; + int ret; + + avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename); + if (!ofmt_ctx) { + av_log(NULL, AV_LOG_ERROR, "Could not allocate output to save Segment %d.\n", seg->id); + return; + } + + if ((ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE)) < 0) { + av_log(ofmt_ctx, AV_LOG_ERROR, + "Could not open output io context to save Segment %d: %s.\n", seg->id, av_err2str(ret)); + return; + } + + avio_write(ofmt_ctx->pb, seg->buf, seg->size); + avio_flush(ofmt_ctx->pb); + avio_close(ofmt_ctx->pb); + avformat_free_context(ofmt_ctx); +} + +void segment_free(struct Segment *seg) +{ + av_log(NULL, AV_LOG_DEBUG, "Freeing segment\n"); + avformat_free_context(seg->fmt_ctx); + av_free(seg->io_ctx->buffer); + av_free(seg->io_ctx); + av_free(seg->buf); + av_free(seg->ts); + av_free(seg); +} + +void segment_ref(struct Segment *seg) +{ + pthread_mutex_lock(&seg->nb_read_lock); + seg->nb_read++; + av_log(NULL, AV_LOG_DEBUG, "%04d ref Readers: %d\n", seg->id, seg->nb_read); + pthread_mutex_unlock(&seg->nb_read_lock); +} + +void segment_unref(struct Segment *seg) +{ + pthread_mutex_lock(&seg->nb_read_lock); + seg->nb_read--; + pthread_mutex_unlock(&seg->nb_read_lock); + av_log(NULL, AV_LOG_DEBUG, "%04d unref Readers: %d\n", seg->id, seg->nb_read); + if (seg->nb_read == 0) { + segment_free(seg); + } +} + +int segment_write(void *opaque, unsigned char *buf, int buf_size) +{ + struct Segment *seg = (struct Segment*) opaque; + seg->size += buf_size; + seg->buf = (char*) av_realloc(seg->buf, seg->size); + memcpy(seg->buf + seg->size - buf_size, buf, buf_size); + return buf_size; +} + +int segment_read(void *opaque, unsigned char *buf, int buf_size) +{ + struct SegmentReadInfo *info = (struct SegmentReadInfo*) opaque; + buf_size = buf_size < info->left ? buf_size : info->left; + + /* copy internal buffer data to buf */ + memcpy(buf, info->buf, buf_size); + info->buf += buf_size; + info->left -= buf_size; + return buf_size ? buf_size : AVERROR_EOF; +} + + +void segment_close(struct Segment *seg) +{ + av_write_trailer(seg->fmt_ctx); +} + +void segment_init(struct Segment **seg_p, AVFormatContext *fmt) +{ + int ret; + int i; + AVStream *in_stream, *out_stream; + struct Segment *seg = (struct Segment*) av_malloc(sizeof(struct Segment)); + + seg->ifmt = fmt->iformat; + seg->fmt_ctx = NULL; + seg->nb_read = 0; + seg->size = 0; + seg->ts = NULL; + seg->ts_len = 0; + seg->buf = NULL; + seg->avio_buffer = (unsigned char*) av_malloc(AV_BUFSIZE); + pthread_mutex_init(&seg->nb_read_lock, NULL); + seg->io_ctx = avio_alloc_context(seg->avio_buffer, AV_BUFSIZE, 1, seg, NULL, &segment_write, NULL); + seg->io_ctx->seekable = 0; + avformat_alloc_output_context2(&seg->fmt_ctx, NULL, "matroska", NULL); + if ((ret = av_opt_set_int(seg->fmt_ctx, "flush_packets", 1, AV_OPT_SEARCH_CHILDREN)) < 0) { + av_log(seg->fmt_ctx, AV_LOG_WARNING, "Could not set flush_packets!\n"); + } + + seg->fmt_ctx->flags |= AVFMT_FLAG_GENPTS; + seg->fmt_ctx->oformat->flags &= AVFMT_NOFILE; + + av_log(seg->fmt_ctx, AV_LOG_DEBUG, "Initializing segment\n"); + + for (i = 0; i < fmt->nb_streams; i++) { + in_stream = fmt->streams[i]; + out_stream = avformat_new_stream(seg->fmt_ctx, NULL); + if (!out_stream) { + av_log(seg->fmt_ctx, AV_LOG_WARNING, "Failed allocating output stream\n"); + continue; + } + ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); + if (ret < 0) { + av_log(seg->fmt_ctx, AV_LOG_WARNING, "Failed to copy context from input to output stream codec context\n"); + continue; + } + av_dict_copy(&out_stream->metadata, in_stream->metadata, 0); + } + + seg->fmt_ctx->pb = seg->io_ctx; + ret = avformat_write_header(seg->fmt_ctx, NULL); + avio_flush(seg->io_ctx); + if (ret < 0) { + segment_close(seg); + av_log(seg->fmt_ctx, AV_LOG_WARNING, "Error occured while writing header: %s\n", av_err2str(ret)); + } + + *seg_p = seg; + av_log(seg->fmt_ctx, AV_LOG_DEBUG, "Initialized segment.\n"); + return; +} diff --git a/segment.h b/segment.h new file mode 100644 index 0000000..a283c17 --- /dev/null +++ b/segment.h @@ -0,0 +1,114 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SEGMENT_H +#define SEGMENT_H + +#include <libavformat/avformat.h> + +#define AV_BUFSIZE 4096 + +struct SegmentReadInfo { + char *buf; + int left; +}; + +struct Segment { + char *buf; + AVIOContext *io_ctx; + AVFormatContext *fmt_ctx; + AVInputFormat *ifmt; + size_t size; + int64_t *ts; + int ts_len; + int nb_read; + unsigned char *avio_buffer; + int id; + pthread_mutex_t nb_read_lock; +}; + +/** + * Save segment to a file using filename. + * Note: Currently produces incorrect files. + * + * @param seg pointer to the Segment to save + * @param filename string to use to save the Segment + */ +void segment_save(struct Segment *seg, const char *filename); + +/** + * Free Segment. Automatically called when a Segment's refcount reaches zero. + * + * @param seg pointer to the Segment to free + */ +void segment_free(struct Segment *seg); + +/** + * Increase the reference counter for a Segment. + * + * @param seg pointer to the Segment to increase the refcounter for + */ +void segment_ref(struct Segment *seg); + +/** + * Decrease the reference counter for a Segment. Calls segment_free() if the refcounter reaches zero. + * + * @param seg pointer to the Segment to unref + */ +void segment_unref(struct Segment *seg); + + +/** + * Write buf_size bytes from buf to a Segment opaque. + * + * @param opaque void pointer to the Segment to write to + * @param buf pointer to the data to write + * @param buf_size number of bytes to write + * @return number of bytes written. May be less than buf_size. + */ +int segment_write(void *opaque, unsigned char *buf, int buf_size); + +/** + * Read buf_size bytes from a Segment using a SegmentReadInfo struct and store them in buf. + * Using a SegmentReadInfo struct instead of the Segment directly is needed, because there + * are multiple readers for a single Segment and each has to keep its own reading state. + * + * @param opaque void pointer to the SegmentReadInfo struct to use for reading + * @param buf pointer to where to store the read data + * @param buf_size number of bytes to read + * @return number of bytes read. May be less than buf_size. + */ +int segment_read(void *opaque, unsigned char *buf, int buf_size); + +/** + * Write a Segment's trailer + * + * @param seg pointer to the Segment to finalize + */ +void segment_close(struct Segment *seg); + +/** + * Allocate and initialize a new segment given the AVFormatContext fmt + * + * @param seg pointer to a pointer to a Segment to be allocated and initialized + * @param fmt pointer to an AVFormatContext describing the format of the Segment + */ +void segment_init(struct Segment **seg, AVFormatContext *fmt); + + +#endif // SEGMENT_H -- 2.16.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel