From patchwork Thu Apr 12 13:35:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephan Holljes X-Patchwork-Id: 8423 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.1.70 with SMTP id c67csp1908278jad; Thu, 12 Apr 2018 06:41:37 -0700 (PDT) X-Google-Smtp-Source: AIpwx49nEo7vNRBpI66cA14Tjr3oUiYWgQvQR7tdHVsJ6O8jt5kzwKooeHBYICL687wJ8jTPrp8k X-Received: by 10.223.144.227 with SMTP id i90mr867954wri.100.1523540497628; Thu, 12 Apr 2018 06:41:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1523540497; cv=none; d=google.com; s=arc-20160816; b=QHqN9WIvo41UA0VI+I5n/RsaKRYJegrWRUJqH51D6dm0qYOaHnyee/JtTyKlxuh1Z6 JIAHT+PbNF/MpDL6Ro6mujCj474luO5O0f3DBBc2znuyCHVymSbX5d/gVCCES98fDxit 493ilcPQ3LE8YVPsTzp+VrEb7zSQDSEVb/onS+qL9kMnZdLhLpHVZW4LlHQU27KgJ9Or 2SL/i12H6W4bI1QQE6LcByPxPqCpHy2stBc703VUdfnGYztsSAfpivMhGwow1Jp/MLJL gX7tM6ILHsOkjW/ZsbN9U9pC2fVQTh5WO/eUDvUvmy0/S4SIicLgixUuKj9AkleF7iB2 kIAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=kLGn5CaOIjs6jsNtMwheFAdwkDOmz//OLUsUTHNXUYE=; b=xbPg7n7tbVgnuA0ZCA+xv+82fQEtGpWzMPt95VAg38fHNRfj99ULk9ljL184xTUzGm fI0Iapu3pGxm2sKqUe25RBLc3Az/3nam2C7a4hKC+itZlkQYwTWF2JP7LKFrowqsnKRj u9lkjN/gbG7SJ5TgisDvHg50Qr7IUV5GuB0TVvFINHshcJ5fnWjav9U80ofRZmXxApE8 vjGhENLAoOpy+hGl5IVUWUtgEQRGEp4CZvCS6ofTNu/Bn6hIoMqNFWG+VY1VXMnjXHBP zKhdbGJvdVAmYKggSm2OM9xEwt/ffxnXEr0BrCQ2STn0xKC+LH7RPcmyuSPuy2IfV3gx 4C3A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=bZ2jBW+2; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 36si1065391wrh.482.2018.04.12.06.41.37; Thu, 12 Apr 2018 06:41:37 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=bZ2jBW+2; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 73E2B689AAF; Thu, 12 Apr 2018 16:41:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f65.google.com (mail-wm0-f65.google.com [74.125.82.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2E7216898B5 for ; Thu, 12 Apr 2018 16:41:05 +0300 (EEST) Received: by mail-wm0-f65.google.com with SMTP id t67so11847650wmt.0 for ; Thu, 12 Apr 2018 06:41:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=h+Eo6BAhveolemrgeFCfUk66lbndCvUgYtXyQVw7XiE=; b=bZ2jBW+2vwAk/JM51NmHfjzDAunpCD+ca6hSpmSwpVuw+LEXzMc3V1JJMN8lFpjP7k 8Ck/sYp8QPlZaHBs383uOgH+nz2vUL/uVlukd9bgv/N7aoQPVK8JSqn3um3p+xP8z1QU 3o/Fog8p4d6YzAnVzY3A7EsoxhPkVNiuNBvQQGdRIbKotMxC9i6SYcymoYDNd+l83Z2q Kle7vmxANWTYxu9S/3iqC82YwDZMlts3Bt8HrSqM/7VuMrreIS94RSKvFTaf17enb+Hm oeDxjCtdEmzchG+w6HRsQ398N2xWfkiPFNEzusKPbV2bibyzeJACBIL8sfxQ7iUKIWdy Tziw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=h+Eo6BAhveolemrgeFCfUk66lbndCvUgYtXyQVw7XiE=; b=iNyA7dIv8fvZW/CPvjl3GPJQJI2xUG8xPkIIgkq3JP3t9y2W3Hv+SUYARpVRd2sdNV gd3mhfuMauR+2G47rqt7nSfZef71Xnu0yvBQkmVHAgmzJlVUh1kLPjgB9Rja3DuUUM0+ EBzK4bhOtFFGaDDpYttNgtf9inTprXrQgb2+iEB1pDXTpq2SEki5lwZFAgM3248hopda NN8eOg6QrzymOelUWDkEOCv8WJyNI8CCAs9GdZRleJ6HPoU3tsIY88gBa8PKfd4KFkh2 ByoyyxsWtkmtFmozgC4j5xX7rOvq5SsKd+/ha/lnmHPXQpGangy9be2YnZBjxfJU5tdz SUdA== X-Gm-Message-State: ALQs6tBXtzmbnDBirWll1TyS74wiSCVXeisqQOFwea/Kudi43p5gK501 NgYpS8ZawlVKVxsgeR9T2BgCKQ== X-Received: by 10.28.136.4 with SMTP id k4mr787238wmd.133.1523540172812; Thu, 12 Apr 2018 06:36:12 -0700 (PDT) Received: from localhost.localdomain (HSI-KBW-046-005-002-245.hsi8.kabel-badenwuerttemberg.de. [46.5.2.245]) by smtp.gmail.com with ESMTPSA id y101sm4588731wmh.10.2018.04.12.06.36.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 12 Apr 2018 06:36:12 -0700 (PDT) From: Stephan Holljes To: ffmpeg-devel@ffmpeg.org Date: Thu, 12 Apr 2018 15:35:46 +0200 Message-Id: <20180412133549.19939-2-klaxa1337@googlemail.com> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180412133549.19939-1-klaxa1337@googlemail.com> References: <20180412133549.19939-1-klaxa1337@googlemail.com> Subject: [FFmpeg-devel] [PATCH 1/4] ffserver: Implement refcounted segments. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Stephan Holljes MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" --- 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 +#include "segment.h" +#include + +#include +#include + + +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 + +#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