From patchwork Thu May 10 15:41:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephan Holljes X-Patchwork-Id: 8908 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:155:0:0:0:0:0 with SMTP id c82-v6csp1215813jad; Thu, 10 May 2018 08:42:00 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoTOUDTudcqTWxCsBuF+nJdbN1Qh4J8HjGvqMpY7Ynkcv97BmF2X7ZQV4HJn02vraVpSlco X-Received: by 2002:adf:86b2:: with SMTP id 47-v6mr1717134wrx.256.1525966920410; Thu, 10 May 2018 08:42:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525966920; cv=none; d=google.com; s=arc-20160816; b=mkf71K0cP23fXKsFqi1aIc1aAu7QKDy7HlsTmuAg4KuETA07nuSxFax3QT2vqA8mXV lZOwkx3hXr0rpOb2ubC/fdJZrHD2HffxILv5/I1tcmyLqR82hue/tP1SKfQsv4kx8R8J H85M7je25PVg0oUkVxRL/FrVp66Ga+iQKdILznFg8BOwyWGTvFWiucJW1qbs07AH/75j 0S6+oc+ht1l7y+LrNoGbKhLhtSoOnMv/bOsatgn4IXKkTs9GYBZOOZYcdWJDqDbxkaEu 4/7qQerUM7e/7PErilcjW2/gZfhCJSKpbigzk/ulUAE+Ny1y+0LoJ3YC3otUoyD+CSMg wjpg== 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=zv9eHQWh4+ifdAqpg5jWBtTeGy1EerzKUJHnxsgT194=; b=SCC3lOpJpO/NcWKhDIAZrsIkab7YIDGYzIHc6aCAhi65jx+QHI0DwnGskhn7pPIAZL qy4cr1XIDxe2YSYhb5iH8tr2tXouawJPGy4SNK6+W73uzoDSK277LeUJ0sEtJim40GyD B49mchRtIaz1Gia4pN5acpVorkmaNbUaqVPVM3uFYNFMY80qN2Nt0S+fWh5XWfmC03gt +FrCmh7/7dLYm/b+3052TOhtMCERzj8tig09wvBAZ+4HzXTEqLJH9Ft/vQaZx2BgeijL sbmjpmHTjVOW4vpnSYVNwlR0tegGZPZsF2dad1/2i/QDjKp31wHnJlSGIRCeegGsXK6r dy7g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=oYlQQ+0y; 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 s5-v6si972857wmf.81.2018.05.10.08.42.00; Thu, 10 May 2018 08:42:00 -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=oYlQQ+0y; 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 05A5668A833; Thu, 10 May 2018 18:41:17 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr0-f175.google.com (mail-wr0-f175.google.com [209.85.128.175]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 56A7068A81B for ; Thu, 10 May 2018 18:41:10 +0300 (EEST) Received: by mail-wr0-f175.google.com with SMTP id y15-v6so2453459wrg.11 for ; Thu, 10 May 2018 08:41:46 -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=sMaOlOuQXpW+fBvtIy5QAe3g/5JkxWSbMvcRzbucQHY=; b=oYlQQ+0y2UPa7udMNSxTeBeFd8FnOHO+9xFCiH45y6XpijAroowxM2Ll4wJYcjrFGc rhqBp0xrqme2DPiiwm4iwYnSZPBTQpK2WSszPfrdPEO1uhmnU2ZfH3CMLgCQc9CvZhtU YUZh4JtU7QPRWBWuVdoDi+S9pUfB5pagnfqWnpOmf4PcWGWpRcoT4lEPA6Ik1nDAo4oo KS++E0q4mSF7hORk8bPbYYolwwFxzGEzjB9WxqXT1CstVQLgsmzlN8eZGgdiycITFplA aHh+XYIBATxXAIVAO/kS3VaVBod/7rirhGIOipDiooq/usIB1etWUK27UYSoTYoBFhuZ Y+Lg== 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=sMaOlOuQXpW+fBvtIy5QAe3g/5JkxWSbMvcRzbucQHY=; b=LbeqCGol8Xb3Bmg4fUKGzQzi8+BBZzY1meelsiQ2PUWKjlBQndvzfcc831Buw9c4SJ uMGOQoiR0aRuGz37gLXifyASiSdCkhnB7/uXSOYAP8G4RCvmW1BJYK9bLW5wyWvfeqAj XnVCG7DGj2ggS0JDcdow6rfB3YAWtip+2FeRssD5p83cjmoLTwsYRgL1UoIbia7hUkSv mW/+BLQiPlbkpOTNz4sYdCf0/CJOJqpTvTRz+9btazvA4+1qlVA1D4JnB2l/v682Zgxo a4ON3Bxs7youxsjC5ep8MeDQ457UweycQ9SalY7rbAqdyky2co+KJajkULhgphDt9LOp OemA== X-Gm-Message-State: ALKqPwciL1Kk9H1IW8sOJ4pYCn7eLM2WMaFMgRQJbINKsMEq59+o9XDT tNNwRVlpWYOEUw0JCGEHbgLZBA== X-Received: by 2002:adf:87e1:: with SMTP id c30-v6mr1658187wrc.246.1525966905765; Thu, 10 May 2018 08:41:45 -0700 (PDT) Received: from localhost.localdomain (cebit2017.teco.kit.edu. [129.13.169.177]) by smtp.gmail.com with ESMTPSA id x63-v6sm1853472wma.25.2018.05.10.08.41.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 10 May 2018 08:41:44 -0700 (PDT) From: Stephan Holljes To: ffmpeg-devel@ffmpeg.org Date: Thu, 10 May 2018 17:41:22 +0200 Message-Id: <20180510154126.30789-2-klaxa1337@googlemail.com> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180510154126.30789-1-klaxa1337@googlemail.com> References: <20180510154126.30789-1-klaxa1337@googlemail.com> Subject: [FFmpeg-devel] [PATCH 1/5] 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" Signed-off-by: Stephan Holljes --- segment.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ segment.h | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 281 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..afb6d80 --- /dev/null +++ b/segment.c @@ -0,0 +1,167 @@ +/* + * 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 +#include "segment.h" +#include + +#include +#include + + +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 = (unsigned 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; + } + if (out_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (in_stream->sample_aspect_ratio.num) + out_stream->sample_aspect_ratio = in_stream->sample_aspect_ratio; + out_stream->avg_frame_rate = in_stream->avg_frame_rate; + out_stream->r_frame_rate = in_stream->r_frame_rate; + } + av_dict_copy(&out_stream->metadata, in_stream->metadata, 0); + } + av_dict_copy(&seg->fmt_ctx->metadata, fmt->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..90724dd --- /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 + +#define AV_BUFSIZE 4096 + +struct SegmentReadInfo { + unsigned char *buf; + int left; +}; + +struct Segment { + unsigned 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