From patchwork Sun May 13 00:07:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephan Holljes X-Patchwork-Id: 8935 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:155:0:0:0:0:0 with SMTP id c82-v6csp2440615jad; Sat, 12 May 2018 17:13:51 -0700 (PDT) X-Google-Smtp-Source: AB8JxZpVacWTF93d7KA03TpNa1bFO8+xJ6lmO5frauNuClM72YMt45rD1W4WZzjDzszOJZwnClEk X-Received: by 2002:adf:88e8:: with SMTP id g37-v6mr2768686wrg.62.1526170431245; Sat, 12 May 2018 17:13:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526170431; cv=none; d=google.com; s=arc-20160816; b=sV9XzYMGiO0zPGACJklsfaMJ4B8oRHZYiZfLtEFd4eKlxMCNuyMD2FqBN7d51orBme x6AvSNAGfk/iBVfJQAz8Iwe6i7wMl+EWO2c33wpMiUNiCveiT2bD3uJrpEOIDdqx1YVU vzjUAuWFghG46wETGjBS5sFqDeCYhTstgwNcUdd6G7J3/kOxThLtdGYZPhDHFcdqVJtB 0eJKsL7SV+BqvnCChWjaDsShO6Ho1U5Ekf/ddP7xCvaFC3IL9j2qQAiDWu4QvRAvJMgN /uTWhPmAMjnM0yHa27iivTg1R/B/oDd0E/N9yF731HEz56SCL+lqOeGUg5m9JLxDzqp8 ilvQ== 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=P7EAoFXTGfC++qrBT0jM1g0JW+l+QK9rad7vlhNfjJA=; b=rRvPgMf3FleEThjWcE0NcNnzFoVues8d2qhMTXbtGAOhsx/rXS6afmzXtAz31J0GzZ tiZnyr2ds3zaNpHLVfbHhqxEA96b51vKPljsmlsFOwjhpNWPHjMYnbQ7h7aDfKNJ+cJ/ WwhlriGeDNyYPkI62fAhNCBcDTbnPBa35IDWtbLbzKAuGuwtxONZBgkQu8IyDrzy76w9 oLJJKhSNDH3tXMSpDKW3VclFB4HJvJVA7qkeoJj/oeCYO/NrsGPIziFjGGLconJooC7d SKOQR9LVB06a7HMHmPGrwbT6ponpYa34Zm30y3Um5ytYfyZLMq4JiYTvnAGY7ymO5/1A 5qmg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=MWNMqEGu; 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 62-v6si5457420wrg.174.2018.05.12.17.13.50; Sat, 12 May 2018 17:13:51 -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=MWNMqEGu; 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 9425568A637; Sun, 13 May 2018 03:13:12 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f52.google.com (mail-wm0-f52.google.com [74.125.82.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 906C368A601 for ; Sun, 13 May 2018 03:13:05 +0300 (EEST) Received: by mail-wm0-f52.google.com with SMTP id f8-v6so9045652wmc.4 for ; Sat, 12 May 2018 17:13:42 -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=wl9gXtMIlkSLqNwCpxy/kCwB8GpKLenWwuOMkwY9Lxg=; b=MWNMqEGuo3gW7s+6dMOb5M8nSTsd1xT2TzVK6nytARwoAnFUqk5Yj0Ohy4BCtSuMoW /6PccrvSvnmTIuXSO2GAZVgqzWX8CAO2bja7ba2WpWtBY+gQMTKIhnd6R5g1uRmdclLT X1wTIlaj+nKorQx79Z3CYWVfhl4iyy8BqJX0LfRHDA8McGJXGwnGKN4iT0I7H0nW5WxF 3wubjFXc3knugqDdju9KyElEvVuxOXqcbFaLFJHf5qTpjQnTHh+B9Y5WK4zlE2yu+0nk LOGa6fxo3yvLdzRyGytVaWsWtsyiDEWm4OYbiFShk89eC0YHokO+fz0wCZMGL2A7hoOu Mjkw== 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=wl9gXtMIlkSLqNwCpxy/kCwB8GpKLenWwuOMkwY9Lxg=; b=K5J6G5SQH8wtQMyz/DYY/BEmffsinZ3eacBFxQogRFWOgIHnW/JZ9L0pZQ7L6s7z8G BhRIOOoWRiAH4nWsZOrC63GO/WrMGQGS/noQCde1t3M4oBLvTyfINOEg7hK2d6qfrnUT 8ZzfwuAUPwNzyU9ej4Z+NoXjXqUTxYWWyWh2IZ/IIDKKjka9mKEwo/eFcDYzvvBhV1Ck LYR0wbFkU7aYJhQVA6LAYJL2CHojZVEf1/KA6ebXi29Fs4w4w11I0blfTCXj1U2nk5Os 6p4U1zmuIc180qw7kQdAlM5NvntsU7W4vpqm/TlWfwrX0xe5hcgCoefqqL4KSNLgl9ZA 2PwA== X-Gm-Message-State: ALKqPwd/uoB7/pqmfSAToJQQp40dcrv8jf852gBNifwL/JWut0+E6UW9 OpONq7PyMUicCnb28/YMAfiybA== X-Received: by 2002:a1c:8f8f:: with SMTP id r137-v6mr1898680wmd.103.1526170082428; Sat, 12 May 2018 17:08:02 -0700 (PDT) Received: from localhost.localdomain ([46.5.2.0]) by smtp.gmail.com with ESMTPSA id r14-v6sm8245469wra.41.2018.05.12.17.08.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 12 May 2018 17:08:01 -0700 (PDT) From: Stephan Holljes To: ffmpeg-devel@ffmpeg.org Date: Sun, 13 May 2018 02:07:36 +0200 Message-Id: <20180513000740.12548-2-klaxa1337@googlemail.com> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180513000740.12548-1-klaxa1337@googlemail.com> References: <20180510154126.30789-1-klaxa1337@googlemail.com> <20180513000740.12548-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 | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ segment.h | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 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..c40d1ad --- /dev/null +++ b/segment.c @@ -0,0 +1,168 @@ +/* + * 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 = av_find_input_format("matroska"); + 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 | AVFMT_FLAG_AUTO_BSF; + + 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; + } + out_stream->codecpar->codec_tag = 0; + 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