From patchwork Sun Oct 8 20:01:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 5476 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.161.90 with SMTP id m26csp1906287jah; Sun, 8 Oct 2017 13:04:12 -0700 (PDT) X-Received: by 10.28.137.193 with SMTP id l184mr6081478wmd.24.1507493052665; Sun, 08 Oct 2017 13:04:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507493052; cv=none; d=google.com; s=arc-20160816; b=DmdcE6nCd9tJwdgHSaUPrJtJJaoY+nBGsSFL2bJBzUljbuIhVC9hPtCVuCQONp724t CBox0Os1TsFjli+DyeQhL3TS1mSIVYOngYmAVp030Ecrl/Ta8aaMclnnBkuviRa53/uY 0sqEKIBQ2ZqQyUk3b6SglZLeDquVvwA+LEjlNmnp4SmJimYUIpnhMYsKoc3KDwIlhvVx OGwj3ku3yHxxktKZs1J98kcHLcw8VLuVOT7KTsCeYf/UPy0CeqOROK7NAbmLl92bsO1G YMQT5HZ9RXlQIn4uxwJ90vsqcMikGiHqsmU8EdSrBh0zsSXCqEPGYQA4+m/RG1ueU1wm W1Ng== 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: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=jB0+AcCVISpoJtvak5/e3eBCmZuz2Qse6uoCoSLsdAk=; b=XP9h/u5iIWLvFsrt8GIhGfQ5gsq2DQIrhmTWUOJk1T7u9VSL5W+NSgVjaNHKwkhOaw PLoJ8sXs78ZAcJ+NIUfhCI6NZUSgS4EN7DbWt9beLv7SS4TBuBTFXuD5n13/5f67mspz /0V8QZQcLFzYlGhoQ6nJGPTm7e9+NDmgHfnr7I45C2Ivp17/Gln68Hspc0T4oENoxtJX JDeRpczpLEgcyfG07DfKtcj8xqrvbGb6K/lHzUpQZzdJ5Z1wcv4GhrUQvxTXhSIdheBb /Olkn4EqdbAZcXytd6JmCl6EcnjVTGFMYREQuCKezUgTE51ALbac5OsU9+Y/MqQo5A7l 1RZA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=WsUFUj0K; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 64si5237333wmx.202.2017.10.08.13.04.12; Sun, 08 Oct 2017 13:04:12 -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=@jkqxz-net.20150623.gappssmtp.com header.s=20150623 header.b=WsUFUj0K; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 360DD689E67; Sun, 8 Oct 2017 23:02:12 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f41.google.com (mail-wm0-f41.google.com [74.125.82.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 25ABD689DB2 for ; Sun, 8 Oct 2017 23:02:09 +0300 (EEST) Received: by mail-wm0-f41.google.com with SMTP id q132so18490725wmd.2 for ; Sun, 08 Oct 2017 13:02:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references; bh=47tbUkZ0h+5B7cfhn6xuzRIAqvSHW6tAybvqCERAuMk=; b=WsUFUj0K5Nji1vWDcowVLrzLfFJdAOINdji5l2PipGPqBokz78yYaefZqWHupX3P9k ZbGjVGF1hwutRwthGUaP+NeFpSejHa/U4wu8FjtG5XYy6qGlD8BdLq2PKPfzkjGHN0Pd qyM8rNMp1oMsPvQmczFN/CZlMiYNmzJutsXV/KGGYUiseqCXb256gZLWArkC5WqjAad5 LHYycCbyGQMFZ6flbujyg42R9EOxiu6tGqxBE4R+vlCtSWrBLhL18j6evTjfRPSCFyyr 4Jt/v+Z2yOtWxaWmCbCzHBMsS86ijByvAA+n/t1OqVHM9hLawTQqWMW8cezxMHy/5Oc5 hWvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=47tbUkZ0h+5B7cfhn6xuzRIAqvSHW6tAybvqCERAuMk=; b=i07Q4e2aBPDHWlsjgzCmEEOXsdHM59zBqDGZDwgE7CpK0Y/OmyRFgCzBYfZ81kH60t NP6Y+Uq5c72hTbtvksmD1663XQkeKhW/1aX4RMj+oU2xZrRMY8a6Q2NhE/OL47Baz/2c snQlJlmOUgx/OJrhSsChnXqsWWushw2LpIzmLv82t6OfxloIqwtqzPmt3th4sXTR7r9E txQwjbBnRTwAWNzSEvrtDhMt/TUG+SXi2LJl65Mx5ILHt6DW4NHbJdV0yfFDqzYgz566 wB9kt0cuQqfPQ6/ljd36TT5mtWrIXBBw+0KqVHHucBgLUja7PpbtJLQXTWCzLtefBrdq qyUg== X-Gm-Message-State: AMCzsaVV0BNgPI6OfzuIeQp9Hg9azRVjkE48a2gMmfUijn3XzMCyDkUq Akp4QBE6EWy4SZ1xl5SadkIL7UTO X-Google-Smtp-Source: AOwi7QAdKgcoCrYpOCHZ8gMOcB0FaYdvqi1ny/+L4kparcx6BvOkzRIy7F/NuMtXWjwmVATSi1FKZA== X-Received: by 10.223.134.93 with SMTP id 29mr8209530wrw.60.1507492929971; Sun, 08 Oct 2017 13:02:09 -0700 (PDT) Received: from rywe.jkqxz.net (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id x49sm2897856wrb.25.2017.10.08.13.02.09 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 08 Oct 2017 13:02:09 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sun, 8 Oct 2017 21:01:46 +0100 Message-Id: <20171008200154.4873-13-sw@jkqxz.net> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171008200154.4873-1-sw@jkqxz.net> References: <20171008200154.4873-1-sw@jkqxz.net> Subject: [FFmpeg-devel] [PATCH 12/20] lavc: Add mpeg2_metadata bitstream filter 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" (cherry picked from commit b78c30d7ec26af67c00ce2002709a189f6a87a7e) --- configure | 1 + doc/bitstream_filters.texi | 36 +++++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/mpeg2_metadata_bsf.c | 312 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 351 insertions(+) create mode 100644 libavcodec/mpeg2_metadata_bsf.c diff --git a/configure b/configure index f0e5150c40..68c3243ce7 100755 --- a/configure +++ b/configure @@ -2909,6 +2909,7 @@ h264_metadata_bsf_select="cbs_h264" h264_redundant_pps_bsf_select="cbs_h264" hevc_metadata_bsf_select="cbs_h265" mjpeg2jpeg_bsf_select="jpegtables" +mpeg2_metadata_bsf_select="cbs_mpeg2" trace_headers_bsf_select="cbs_h264 cbs_h265 cbs_mpeg2" # external libraries diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 71c307f2e2..8a8756bdcc 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -324,6 +324,42 @@ See also the @ref{text2movsub} filter. Decompress non-standard compressed MP3 audio headers. +@section mpeg2_metadata + +Modify metadata embedded in an MPEG-2 stream. + +@table @option +@item display_aspect_ratio +Set the display aspect ratio in the stream. + +The following fixed values are supported: +@table @option +@item 4/3 +@item 16/9 +@item 221/100 +@end table +Any other value will result in square pixels being signalled instead +(see H.262 section 6.3.3 and table 6-3). + +@item frame_rate +Set the frame rate in the stream. This is constructed from a table +of known values combined with a small multiplier and divisor - if +the supplied value is not exactly representable, the nearest +representable value will be used instead (see H.262 section 6.3.3 +and table 6-4). + +@item video_format +Set the video format in the stream (see H.262 section 6.3.6 and +table 6-6). + +@item colour_primaries +@item transfer_characteristics +@item matrix_coefficients +Set the colour description in the stream (see H.262 section 6.3.6 +and tables 6-7, 6-8 and 6-9). + +@end table + @section mpeg4_unpack_bframes Unpack DivX-style packed B-frames. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index a0b23a794e..6d6fc36f15 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1027,6 +1027,7 @@ OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += mpeg4_unpack_bframes_bsf.o OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += movsub_bsf.o OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF) += mp3_header_decompress_bsf.o \ mpegaudiodata.o +OBJS-$(CONFIG_MPEG2_METADATA_BSF) += mpeg2_metadata_bsf.o OBJS-$(CONFIG_NOISE_BSF) += noise_bsf.o OBJS-$(CONFIG_NULL_BSF) += null_bsf.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 6e6b894e7f..7b0cb5032a 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -38,6 +38,7 @@ extern const AVBitStreamFilter ff_imx_dump_header_bsf; extern const AVBitStreamFilter ff_mjpeg2jpeg_bsf; extern const AVBitStreamFilter ff_mjpega_dump_header_bsf; extern const AVBitStreamFilter ff_mp3_header_decompress_bsf; +extern const AVBitStreamFilter ff_mpeg2_metadata_bsf; extern const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf; extern const AVBitStreamFilter ff_mov2textsub_bsf; extern const AVBitStreamFilter ff_noise_bsf; diff --git a/libavcodec/mpeg2_metadata_bsf.c b/libavcodec/mpeg2_metadata_bsf.c new file mode 100644 index 0000000000..f1f84e6cd9 --- /dev/null +++ b/libavcodec/mpeg2_metadata_bsf.c @@ -0,0 +1,312 @@ +/* + * 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 "libavutil/avstring.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "cbs.h" +#include "cbs_mpeg2.h" +#include "mpeg12.h" + +typedef struct MPEG2MetadataContext { + const AVClass *class; + + CodedBitstreamContext *cbc; + CodedBitstreamFragment fragment; + + MPEG2RawExtensionData sequence_display_extension; + + AVRational display_aspect_ratio; + + AVRational frame_rate; + + int video_format; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + + int mpeg1_warned; +} MPEG2MetadataContext; + + +static int mpeg2_metadata_update_fragment(AVBSFContext *bsf, + CodedBitstreamFragment *frag) +{ + MPEG2MetadataContext *ctx = bsf->priv_data; + MPEG2RawSequenceHeader *sh = NULL; + MPEG2RawSequenceExtension *se = NULL; + MPEG2RawSequenceDisplayExtension *sde = NULL; + int i, se_pos, add_sde = 0; + + for (i = 0; i < frag->nb_units; i++) { + if (frag->units[i].type == MPEG2_START_SEQUENCE_HEADER) { + sh = frag->units[i].content; + } else if (frag->units[i].type == MPEG2_START_EXTENSION) { + MPEG2RawExtensionData *ext = frag->units[i].content; + if (ext->extension_start_code_identifier == + MPEG2_EXTENSION_SEQUENCE) { + se = &ext->data.sequence; + se_pos = i; + } else if (ext->extension_start_code_identifier == + MPEG2_EXTENSION_SEQUENCE_DISPLAY) { + sde = &ext->data.sequence_display; + } + } + } + + if (!sh || !se) { + // No sequence header and sequence extension: not an MPEG-2 video + // sequence. + if (sh && !ctx->mpeg1_warned) { + av_log(bsf, AV_LOG_WARNING, "Stream contains a sequence " + "header but not a sequence extension: maybe it's " + "actually MPEG-1?\n"); + ctx->mpeg1_warned = 1; + } + return 0; + } + + if (ctx->display_aspect_ratio.num && ctx->display_aspect_ratio.den) { + int num, den; + + av_reduce(&num, &den, ctx->display_aspect_ratio.num, + ctx->display_aspect_ratio.den, 65535); + + if (num == 4 && den == 3) + sh->aspect_ratio_information = 2; + else if (num == 16 && den == 9) + sh->aspect_ratio_information = 3; + else if (num == 221 && den == 100) + sh->aspect_ratio_information = 4; + else + sh->aspect_ratio_information = 1; + } + + if (ctx->frame_rate.num && ctx->frame_rate.den) { + int code, ext_n, ext_d; + + ff_mpeg12_find_best_frame_rate(ctx->frame_rate, + &code, &ext_n, &ext_d, 0); + + sh->frame_rate_code = code; + se->frame_rate_extension_n = ext_n; + se->frame_rate_extension_d = ext_d; + } + + if (ctx->video_format >= 0 || + ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + if (!sde) { + add_sde = 1; + ctx->sequence_display_extension.extension_start_code = + MPEG2_START_EXTENSION; + ctx->sequence_display_extension.extension_start_code_identifier = + MPEG2_EXTENSION_SEQUENCE_DISPLAY; + sde = &ctx->sequence_display_extension.data.sequence_display; + + *sde = (MPEG2RawSequenceDisplayExtension) { + .video_format = 5, + + .colour_description = 0, + .colour_primaries = 2, + .transfer_characteristics = 2, + .matrix_coefficients = 2, + + .display_horizontal_size = + se->horizontal_size_extension << 12 | sh->horizontal_size_value, + .display_vertical_size = + se->vertical_size_extension << 12 | sh->vertical_size_value, + }; + } + + if (ctx->video_format >= 0) + sde->video_format = ctx->video_format; + + if (ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + sde->colour_description = 1; + + if (ctx->colour_primaries >= 0) + sde->colour_primaries = ctx->colour_primaries; + else if (add_sde) + sde->colour_primaries = 2; + + if (ctx->transfer_characteristics >= 0) + sde->transfer_characteristics = ctx->transfer_characteristics; + else if (add_sde) + sde->transfer_characteristics = 2; + + if (ctx->matrix_coefficients >= 0) + sde->matrix_coefficients = ctx->matrix_coefficients; + else if (add_sde) + sde->matrix_coefficients = 2; + } + } + + if (add_sde) { + int err; + + err = ff_cbs_insert_unit_content(ctx->cbc, frag, se_pos + 1, + MPEG2_START_EXTENSION, + &ctx->sequence_display_extension); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert new sequence " + "display extension.\n"); + return err; + } + } + + return 0; +} + +static int mpeg2_metadata_filter(AVBSFContext *bsf, AVPacket *out) +{ + MPEG2MetadataContext *ctx = bsf->priv_data; + AVPacket *in = NULL; + CodedBitstreamFragment *frag = &ctx->fragment; + int err; + + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + goto fail; + + err = ff_cbs_read_packet(ctx->cbc, frag, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + err = mpeg2_metadata_update_fragment(bsf, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to update frame fragment.\n"); + goto fail; + } + + err = ff_cbs_write_packet(ctx->cbc, out, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + + err = av_packet_copy_props(out, in); + if (err < 0) { + av_packet_unref(out); + goto fail; + } + + err = 0; +fail: + ff_cbs_fragment_uninit(ctx->cbc, frag); + + av_packet_free(&in); + + return err; +} + +static int mpeg2_metadata_init(AVBSFContext *bsf) +{ + MPEG2MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *frag = &ctx->fragment; + int err; + + err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_MPEG2VIDEO, bsf); + if (err < 0) + return err; + + if (bsf->par_in->extradata) { + err = ff_cbs_read_extradata(ctx->cbc, frag, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + goto fail; + } + + err = mpeg2_metadata_update_fragment(bsf, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to update metadata fragment.\n"); + goto fail; + } + + err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); + goto fail; + } + } + + err = 0; +fail: + ff_cbs_fragment_uninit(ctx->cbc, frag); + return err; +} + +static void mpeg2_metadata_close(AVBSFContext *bsf) +{ + MPEG2MetadataContext *ctx = bsf->priv_data; + ff_cbs_close(&ctx->cbc); +} + +#define OFFSET(x) offsetof(MPEG2MetadataContext, x) +static const AVOption mpeg2_metadata_options[] = { + { "display_aspect_ratio", "Set display aspect ratio (table 6-3)", + OFFSET(display_aspect_ratio), AV_OPT_TYPE_RATIONAL, + { .dbl = 0.0 }, 0, 65535 }, + + { "frame_rate", "Set frame rate", + OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, + { .dbl = 0.0 }, 0, UINT_MAX }, + + { "video_format", "Set video format (table 6-6)", + OFFSET(video_format), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 7 }, + { "colour_primaries", "Set colour primaries (table 6-7)", + OFFSET(colour_primaries), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "transfer_characteristics", "Set transfer characteristics (table 6-8)", + OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "matrix_coefficients", "Set matrix coefficients (table 6-9)", + OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + + { NULL } +}; + +static const AVClass mpeg2_metadata_class = { + .class_name = "mpeg2_metadata_bsf", + .item_name = av_default_item_name, + .option = mpeg2_metadata_options, + .version = LIBAVCODEC_VERSION_MAJOR, +}; + +static const enum AVCodecID mpeg2_metadata_codec_ids[] = { + AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_mpeg2_metadata_bsf = { + .name = "mpeg2_metadata", + .priv_data_size = sizeof(MPEG2MetadataContext), + .priv_class = &mpeg2_metadata_class, + .init = &mpeg2_metadata_init, + .close = &mpeg2_metadata_close, + .filter = &mpeg2_metadata_filter, + .codec_ids = mpeg2_metadata_codec_ids, +};