[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