[FFmpeg-devel] [PATCH 1/4] ffserver: Implement refcounted segments.
Stephan Holljes
klaxa1337 at googlemail.com
Tue Apr 17 04:52:30 EEST 2018
---
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
More information about the ffmpeg-devel
mailing list