[FFmpeg-devel] [PATCH 1/4] ffserver: Implement refcounted segments.

Stephan Holljes klaxa1337 at googlemail.com
Thu Apr 12 16:35:46 EEST 2018


---
 segment.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 segment.h | 104 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 247 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..0ef58a1
--- /dev/null
+++ b/segment.c
@@ -0,0 +1,143 @@
+#include <stdio.h>
+#include "segment.h"
+#include <pthread.h>
+
+#include <libavutil/opt.h>
+#include <libavutil/log.h>
+
+
+void save_segment(struct Segment *seg, const char *filename)
+{
+    FILE *out = fopen(filename, "w");
+    fwrite(seg->buf, seg->size, 1, out);
+    fclose(out);
+}
+
+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);
+    free(seg->buf);
+    free(seg->ts);
+    free(seg);
+}
+
+void segment_ref(struct Segment *seg)
+{
+    pthread_mutex_lock(&seg->nb_read_lock);
+    seg->nb_read++;
+    av_log(NULL, AV_LOG_DEBUG, "  ref Readers: %d\n", 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, "unref Readers: %d\n", seg->nb_read);
+    if (seg->nb_read == 0) {
+        segment_free(seg);
+    }
+}
+
+void segment_ts_append(struct Segment *seg, int64_t dts, int64_t pts)
+{
+    seg->ts = (int64_t*) realloc(seg->ts, sizeof(int64_t) * 2  * (seg->ts_len + 2));
+    seg->ts[seg->ts_len] = dts;
+    seg->ts[seg->ts_len + 1] = pts;
+    seg->ts_len += 2;
+    return;
+}
+
+int segment_write(void *opaque, unsigned char *buf, int buf_size)
+{
+    struct Segment *seg = (struct Segment*) opaque;
+    seg->size += buf_size;
+    seg->buf = (char*) 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;
+    AVCodecContext *codec_ctx;
+    struct Segment *seg = (struct Segment*) 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(NULL, 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(NULL, AV_LOG_DEBUG, "Initializing segment\n");
+
+    for (i = 0; i < fmt->nb_streams; i++) {
+        in_stream = fmt->streams[i];
+        codec_ctx = avcodec_alloc_context3(NULL);
+        avcodec_parameters_to_context(codec_ctx, in_stream->codecpar);
+        out_stream = avformat_new_stream(seg->fmt_ctx, codec_ctx->codec);
+        avcodec_free_context(&codec_ctx);
+        if (!out_stream) {
+            av_log(NULL, AV_LOG_WARNING, "Failed allocating output stream\n");
+            continue;
+        }
+        ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
+        if (ret < 0) {
+            av_log(NULL, 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(NULL, AV_LOG_WARNING, "Error occured while writing header: %s\n", av_err2str(ret));
+    }
+
+
+    av_log(NULL, AV_LOG_DEBUG, "Initialized segment.\n");
+
+    *seg_p = seg;
+
+    return;
+}
diff --git a/segment.h b/segment.h
new file mode 100644
index 0000000..fecbc66
--- /dev/null
+++ b/segment.h
@@ -0,0 +1,104 @@
+#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;
+    size_t ts_len;
+    //FILE *stream;
+    int nb_read;
+    unsigned char *avio_buffer;
+    int id;
+    pthread_mutex_t nb_read_lock;
+};
+
+/**
+ * Save segment to a file using filename
+ *
+ * @param seg pointer to the Segment to save
+ * @param filename string to use to save the Segment
+ */
+void save_segment(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);
+
+/**
+ * Append a dts and pts pair to a Segment. This should be called after each frame is added.
+ *
+ * @param seg pointer to the Segment to add timestamps to
+ * @param dts dts for the last added frame
+ * @param pts pts for the last added frame
+ */
+void segment_ts_append(struct Segment *seg, int64_t dts, int64_t pts);
+
+/**
+ * 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