From patchwork Sat Jul 18 00:19:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Rheinhardt X-Patchwork-Id: 21161 Delivered-To: andriy.gelman@gmail.com Received: by 2002:a25:80ca:0:0:0:0:0 with SMTP id c10csp43035ybm; Fri, 17 Jul 2020 17:21:15 -0700 (PDT) X-Google-Smtp-Source: ABdhPJztmy9zd7UKQxMZjar4Qx8/CaIBgMTVmUf4tB8VLD62wrPrqLN9ANbVRdSDhfg4K+jNQI+E X-Received: by 2002:adf:f60c:: with SMTP id t12mr13555154wrp.198.1595031675559; Fri, 17 Jul 2020 17:21:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1595031675; cv=none; d=google.com; s=arc-20160816; b=HPQQjr6h75J4uEqjefhBBKRBq2AqTDFL3jhMeJU8zm3jMwnKLgbxLM9MjBwzhHhtBN lTlRVy2u9pi8q/NlHPD+kW5UEsZZYgRoS9edwAJoZrOLPkc2ugCv/SzS++FxQZLPM2eo MpHAsDcUjvy354su7JsSNQH2AHulKu5kY+muAnY2qG/mc7pMd8aohfqxOkOVB9UDZHXm HsL4YpSE48ofq9FOf6lWLm3+S9NhEAYKTpNMEuWkRk60qgQ1JMa9h1djBlc2Cj8hKxyj GO9Rb3ghp6UnZcSeW6ZLrQuroy4kA+rVkFUN7PMzvQ7niuMM0/IcCZREACb6r3Lk+gl4 ADug== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=w0Nt5UYdFzJqo/vR7PhvYVgYVhOKkCVt9wliE8nQGSg=; b=pdFtnCiQdUS4ihR5VAueQeG0kbTFYhUf1Y6ZWavzvqn7acGz/VFE4I6OvSaNYmTBuh KNNZoqklSbkBO8fPjwgQ9ZxPUr4Uc74PjCWdHO7EvzFj1Ro1rXxtjnj4Qy4F1bQrCCAF u1IHoa+Tb4QrxFLLPCsFyo/SAuumXHMcnTJ27MEHo+WOg65qYBcKzLCqKOnW2TEmgOog FksYDxRrSq5YL68gIxIHpKJs/RdgPAVy6kV3gjpqYahOzCBF4KnU4zj023Y69TwxE4c4 5uQVaK795tvr+PmCx5PlDdWFhzVhhsoMa9zOZ4Puwtw3n2gGbiaHOxvxSTezT0DaNfM3 /ZQg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=eISCVCn1; 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=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id q19si7852459wmj.3.2020.07.17.17.21.15; Fri, 17 Jul 2020 17:21:15 -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=@gmail.com header.s=20161025 header.b=eISCVCn1; 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=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9B5D668B713; Sat, 18 Jul 2020 03:20:07 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f68.google.com (mail-ej1-f68.google.com [209.85.218.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DB35968B711 for ; Sat, 18 Jul 2020 03:19:58 +0300 (EEST) Received: by mail-ej1-f68.google.com with SMTP id rk21so12708135ejb.2 for ; Fri, 17 Jul 2020 17:19:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=PuEivWQ6PbT4mMghnpL52GNPgQOQwk9i6NrIsmWHdcs=; b=eISCVCn1ohQf57qjQ1Gkq8vBmrEjyJQeW1mymVaykCVsFATBvvPz1DbZBkRUKlNv3G zI342E2cMln1imP6dFCWgWzEc0OGLHpNYcqwmfisYWJXSG+Cq77DYFrq9wJjnv1NR5Rw l+5q21Fcofa9VnVzhuVCxnbionfGMxyOAquvZw6rksVedeOoaUOLMd33txs6BAweLicj 6rbqhiJP52z9z3reh2hihX8ZYeNHQbWZNjq8fHlJzJKbAZ4sE6bs+lKmWGv7puHSJNRs 9ZBjO2D3iKDUHOBj52BGX7gp6WaU5UQHLGwuBytYzVznkbxOuvdyQS1QsCuFwRo/jdS8 K28g== 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:mime-version:content-transfer-encoding; bh=PuEivWQ6PbT4mMghnpL52GNPgQOQwk9i6NrIsmWHdcs=; b=lISieWftNlkArS4avBCv7pYt2mTBL4Jyy9o6pYjDb2B0uBVgZ7uHpWgj41cFdwf4/F Kuubk1dA9HzpazsoqDLavC+UANGLo4G37FHCvxdRnvMR2lebIRnpD1y8Y7KPjQ0ebQUk H4FtJI7dYVCG7k3ES0bVUgiuzg+Ea9bp0IX50Q/MzFcQRt+IbY3s47KBSkflkT+RELFz VkAJvO2vo3fRy4OMm/NS2eD405WQdSwgvIlKUdnZF3dm/Xp/m34nRFQBakx5DrtLgm78 Qeh0uO7wTRJIzlkijLSIM2H2F7WfdlRXixDApg4/+GaY77lNa9Hlh30W/NE5dvQoWaYv Gytw== X-Gm-Message-State: AOAM5337Ig5zeoPfbM5BHAXLVgaW8Kh4eCuKvmE7tejw74l9H7ctAQbe bIf6FiTWloSd1gpbsmxbtzzEuzAs X-Received: by 2002:a17:906:cc0e:: with SMTP id ml14mr10808535ejb.432.1595031597679; Fri, 17 Jul 2020 17:19:57 -0700 (PDT) Received: from sblaptop.fritz.box (ipbcc10296.dynamic.kabel-deutschland.de. [188.193.2.150]) by smtp.gmail.com with ESMTPSA id y7sm9299001ejd.73.2020.07.17.17.19.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jul 2020 17:19:56 -0700 (PDT) From: Andreas Rheinhardt To: ffmpeg-devel@ffmpeg.org Date: Sat, 18 Jul 2020 02:19:28 +0200 Message-Id: <20200718001928.10603-10-andreas.rheinhardt@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200718000636.2167-1-andreas.rheinhardt@gmail.com> References: <20200718000636.2167-1-andreas.rheinhardt@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 11/11] avformat/segafilmenc: Avoid seek when writing header 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: Andreas Rheinhardt Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: VJQZ2gPEiKon Content-Length: 13609 Up until now, the Sega FILM muxer would first write all the packet data, then shift the data (in the muxer's write_trailer function) by the amount necessary to write the header at the front (which entails a seek to the front), then seek back to the beginning and actually write the header. This commit changes this: The dynamic buffer that is used to write the sample table (containing information about each sample in the file) is now used to write the complete header. This is possible because the size of everything in the header except the sample table is known in advance. Said buffer can then be used as one of the two temporary buffers used for shifting which also reduces the amount one has to allocate for this. Thereby the header will be written when shifting, so that the second seek to the beginning is unnecessary. Signed-off-by: Andreas Rheinhardt --- libavformat/segafilmenc.c | 116 ++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/libavformat/segafilmenc.c b/libavformat/segafilmenc.c index 11ed279a05..42249d4eff 100644 --- a/libavformat/segafilmenc.c +++ b/libavformat/segafilmenc.c @@ -29,7 +29,9 @@ * http://wiki.multimedia.cx/index.php?title=Sega_FILM */ +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" #include "avformat.h" #include "internal.h" #include "avio_internal.h" @@ -160,11 +162,13 @@ static int film_init(AVFormatContext *format_context) } if ((ret = avio_open_dyn_buf(&film->header)) < 0) return ret; + ffio_fill(film->header, 0, 16 + 32 + 16); return 0; } -static int shift_data(AVFormatContext *format_context, int64_t shift_size) +static int write_header(AVFormatContext *format_context, uint8_t *header, + unsigned header_size) { int ret = 0; int64_t pos, pos_end; @@ -173,11 +177,12 @@ static int shift_data(AVFormatContext *format_context, int64_t shift_size) int read_size[2]; AVIOContext *read_pb; - buf = av_malloc(shift_size * 2); + buf = av_malloc(header_size); if (!buf) return AVERROR(ENOMEM); read_buf[0] = buf; - read_buf[1] = buf + shift_size; + read_buf[1] = header; + read_size[1] = header_size; /* Write the header at the beginning of the file, shifting all content as necessary; * based on the approach used by MOV faststart. */ @@ -190,25 +195,20 @@ static int shift_data(AVFormatContext *format_context, int64_t shift_size) return ret; } - /* mark the end of the shift to up to the last data we wrote, and get ready - * for writing */ - pos_end = avio_tell(format_context->pb); - avio_seek(format_context->pb, shift_size, SEEK_SET); + /* Mark the end of the shift to up to the last data we are going to write, + * and get ready for writing */ + pos_end = avio_tell(format_context->pb) + header_size; + pos = avio_seek(format_context->pb, 0, SEEK_SET); /* start reading at where the new header will be placed */ avio_seek(read_pb, 0, SEEK_SET); - pos = avio_tell(read_pb); -#define READ_BLOCK do { \ - read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], shift_size); \ - read_buf_id ^= 1; \ -} while (0) - - /* shift data by chunk of at most shift_size */ - READ_BLOCK; + /* shift data by chunk of at most header_size */ do { int n; - READ_BLOCK; + read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], + header_size); + read_buf_id ^= 1; n = read_size[read_buf_id]; if (n <= 0) break; @@ -224,81 +224,75 @@ static int shift_data(AVFormatContext *format_context, int64_t shift_size) static int film_write_header(AVFormatContext *format_context) { int ret = 0; - unsigned sample_table_size, stabsize, headersize, packet_count; - AVIOContext *pb = format_context->pb; + unsigned stabsize, headersize, packet_count; FILMOutputContext *film = format_context->priv_data; AVStream *video = NULL; - uint8_t *sample_table; + uint8_t *header, *ptr; /* Calculate how much we need to reserve for the header; * this is the amount the rest of the data will be shifted up by. */ - sample_table_size = avio_get_dyn_buf(film->header, &sample_table); - packet_count = sample_table_size / 16; - sample_table_size = packet_count * 16; - stabsize = 16 + sample_table_size; + headersize = avio_get_dyn_buf(film->header, &header); + if (headersize < 64) { + av_assert1(film->header->error < 0); + return film->header->error; + } + packet_count = (headersize - 64) / 16; + stabsize = 16 + 16 * packet_count; headersize = 16 + /* FILM header base */ 32 + /* FDSC chunk */ stabsize; - ret = shift_data(format_context, headersize); - if (ret < 0) - return ret; - /* Seek back to the beginning to start writing the header now */ - avio_seek(pb, 0, SEEK_SET); - - /* First, write the FILM header; this is very simple */ - - ffio_wfourcc(pb, "FILM"); - avio_wb32(pb, 48 + stabsize); + /* Write the header at the position in the buffer reserved for it. + * First, write the FILM header; this is very simple */ + ptr = header; + bytestream_put_be32(&ptr, MKBETAG('F', 'I', 'L', 'M')); + bytestream_put_be32(&ptr, headersize); /* This seems to be okay to hardcode, since this muxer targets 1.09 features; * videos produced by this muxer are readable by 1.08 and lower players. */ - ffio_wfourcc(pb, "1.09"); - /* I have no idea what this field does, might be reserved */ - avio_wb32(pb, 0); + bytestream_put_be32(&ptr, MKBETAG('1', '.', '0', '9')); + /* I have no idea what the next four bytes do, might be reserved */ + ptr += 4; /* Next write the FDSC (file description) chunk */ - ffio_wfourcc(pb, "FDSC"); - avio_wb32(pb, 0x20); /* Size of FDSC chunk */ + bytestream_put_be32(&ptr, MKBETAG('F', 'D', 'S', 'C')); + bytestream_put_be32(&ptr, 0x20); /* Size of FDSC chunk */ video = format_context->streams[film->video_index]; /* The only two supported codecs; raw video is rare */ switch (video->codecpar->codec_id) { case AV_CODEC_ID_CINEPAK: - ffio_wfourcc(pb, "cvid"); + bytestream_put_be32(&ptr, MKBETAG('c', 'v', 'i', 'd')); break; case AV_CODEC_ID_RAWVIDEO: - ffio_wfourcc(pb, "raw "); + bytestream_put_be32(&ptr, MKBETAG('r', 'a', 'w', ' ')); break; } - avio_wb32(pb, video->codecpar->height); - avio_wb32(pb, video->codecpar->width); - avio_w8(pb, 24); /* Bits per pixel - observed to always be 24 */ + bytestream_put_be32(&ptr, video->codecpar->height); + bytestream_put_be32(&ptr, video->codecpar->width); + bytestream_put_byte(&ptr, 24); /* Bits per pixel - observed to always be 24 */ if (film->audio_index > -1) { AVStream *audio = format_context->streams[film->audio_index]; int audio_codec = get_audio_codec_id(audio->codecpar->codec_id); - avio_w8(pb, audio->codecpar->channels); /* Audio channels */ - avio_w8(pb, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */ - avio_w8(pb, audio_codec); /* Compression - 0 is PCM, 2 is ADX */ - avio_wb16(pb, audio->codecpar->sample_rate); /* Audio sampling rate */ + bytestream_put_byte(&ptr, audio->codecpar->channels); /* Audio channels */ + bytestream_put_byte(&ptr, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */ + bytestream_put_byte(&ptr, audio_codec); /* Compression - 0 is PCM, 2 is ADX */ + bytestream_put_be16(&ptr, audio->codecpar->sample_rate); /* Audio sampling rate */ } else { - /* Set all these fields to 0 if there's no audio */ - avio_w8(pb, 0); - avio_w8(pb, 0); - avio_w8(pb, 0); - avio_wb16(pb, 0); + /* If there is no audio, all the audio fields should be set to zero. + * ffio_fill() already did this for us. */ + ptr += 1 + 1 + 1 + 2; } /* I have no idea what this pair of fields does either, might be reserved */ - avio_wb32(pb, 0); - avio_wb16(pb, 0); + ptr += 4 + 2; /* Finally, write the STAB (sample table) chunk */ - ffio_wfourcc(pb, "STAB"); - avio_wb32(pb, stabsize); + bytestream_put_be32(&ptr, MKBETAG('S', 'T', 'A', 'B')); + bytestream_put_be32(&ptr, stabsize); /* Framerate base frequency. Here we're assuming that the frame rate is even. * In real world Sega FILM files, there are usually a couple of approaches: * a) framerate base frequency is the same as the framerate, and ticks @@ -308,12 +302,14 @@ static int film_write_header(AVFormatContext *format_context) * The latter occurs even in cases where the frame rate is even; for example, in * Lunar: Silver Star Story, the base frequency is 600 and each frame, the ticks * are incremented by 25 for an evenly spaced framerate of 24fps. */ - avio_wb32(pb, av_q2d(av_inv_q(video->time_base))); + bytestream_put_be32(&ptr, av_q2d(av_inv_q(video->time_base))); - avio_wb32(pb, packet_count); + bytestream_put_be32(&ptr, packet_count); - /* Finally, write out each packet's data to the header */ - avio_write(pb, sample_table, sample_table_size); + /* Finally, shift the data and write out the header. */ + ret = write_header(format_context, header, headersize); + if (ret < 0) + return ret; return 0; }