From patchwork Mon Jun 5 19:32:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Colwell X-Patchwork-Id: 3850 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.10.2 with SMTP id 2csp1511476vsk; Mon, 5 Jun 2017 12:38:58 -0700 (PDT) X-Received: by 10.28.227.67 with SMTP id a64mr1639938wmh.43.1496691538841; Mon, 05 Jun 2017 12:38:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1496691538; cv=none; d=google.com; s=arc-20160816; b=tpPp7Htz55Vuxr7YJcDK9AZ98cyWATsA9m8dR9eMd1qWEQ8MjDObSUUYhtRAqTjeOs Gx+fKUSMws2G7eunxL5K03fRK7BCjPG4NyM9BaMU0w5cGbCqCRaGUMGTLVX1JIasooJD pE98w7yKeIa4SHBTSKmoiHqLtTdqJdT5XrP6tvyz78yc4RQ6YXmpDcz2H2xr9XLLU4bJ 4VvsoCbApLg5x08AN/qZwXL8BAvb4XWBFpgY03xFRRCHAK4E6juHbjr9Q6ppVpk6UDnJ WQzZ8mkVSOZyhVYp6549WXkUZha2Dl6wCWoIcWhlW24oakom/AjxrQ3NV7AZJBWCVkLu ruWQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:to :message-id:date:from:in-reply-to:references:mime-version :dkim-signature:delivered-to:arc-authentication-results; bh=HpESgfA4J7hgeFvR8plijFd/71yJz3dIjwwPOkAGb8w=; b=wug7pqiwDP1Y3mWu6QP4Sk9iHY01PjUePYYnTOUrrhyL8Asr7tArqY2q+eNFdC44z7 gPA3WLzbCds7QkYcTI1m/XFYapQp2x/1p8+J5Wt3j8jsO+GfooDA9z3IV4+3ySpHnqoj Ap79M10VfXR8nVLV05/9CsHEbUcNEr6I9LWc8rOI3xgbdX/F1P1p86OM22yWjVSZRDO3 7Z87ZS8XTLVfgP3p/auDX4RwA9W1dFUuXTxjfr6TUzpWDE7q4EScLRfK1hGqHFq3CCCt 6ApgT+ul1FJWd5tupxzvu3POjbzmgI/Fror2Sl6V6xnduifI8G8DqDT86TGG+WO18pxd dvVA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com; 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 61si8209647wrp.7.2017.06.05.12.38.58; Mon, 05 Jun 2017 12:38:58 -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=@google.com; 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 33481689DD5; Mon, 5 Jun 2017 22:38:48 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-oi0-f52.google.com (mail-oi0-f52.google.com [209.85.218.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C956D689D91 for ; Mon, 5 Jun 2017 22:38:41 +0300 (EEST) Received: by mail-oi0-f52.google.com with SMTP id h4so167978786oib.3 for ; Mon, 05 Jun 2017 12:38:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=dy5BP5AnsPxlzFUqTD9q3Ev0a3xh2fHBZM1Etyn4prg=; b=cD/m2wYSwTS8Xb2Y/VXH78Z0I5KBN3SKc/LfZ7kyQHaQY6CYm28N6h0Fcy7RiHSp35 FQrD0LD3GtazWI/8BzqB9HWGwUbeqrXZvTKHvlk7JQSt/1Isv6Y6wnB2ewje9Cmv3Vsa Wqb3K1+nl8NcgowYn4www5zoO7eT+2b8nl+8sLynBfLykfSNwFQbwHAoc9JzBrHnAhqH 8s1QRUOkkNeJBYNXyRdI7escilhQaIBI1GBZlGdxFp3UflJqJxDoxaTpsE0pDlmgtkxq YkXUFfemIjSvcyjITqUvIoRC2no6bhQxU+Q1lp3ih07y1ohogLoeV1JZe7uzXDGakQMp OTgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=dy5BP5AnsPxlzFUqTD9q3Ev0a3xh2fHBZM1Etyn4prg=; b=VEbS0Zfk3Hr3vwusA2o6ya8euEsznWcXCI7czbwT9T6a+k0qZAEj4qYAfcLN4hIZJ/ U+h0p0CUdR5S69pve4HceztKfvFGnh9M0wuYr93oLd+2stWm+oFnf1qMvj3pqWaqgErF vcHzArbIWTrJhIVNZmLGx733PrrhZrpQWaih0YrIANK4fuwPhhzASGn5zYY+gQQ1xAmQ FF8Z6I2VD+fkKw0CPe4a4fQgm5Dh5enqKLgglTsEFnKAeBKGmfS0OK1LoLmydCUNp/Me W7FS1DK9mC9BIwT3MRY+jaEZmAGRhvjLvhJVXrxddJBaMgh51p/lTbwwNm0DtBvcewN7 sdHg== X-Gm-Message-State: AODbwcAxaVyC67gCi6TfwFUWFOUYAonjUfgJlGgTRFEWGAnc3RAh75eH 6IPWDedTrYxXjUirisguoAxWPP5/dFfZSAY= X-Received: by 10.202.83.206 with SMTP id h197mr3919399oib.150.1496691174334; Mon, 05 Jun 2017 12:32:54 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Aaron Colwell Date: Mon, 05 Jun 2017 19:32:42 +0000 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: Re: [FFmpeg-devel] [PATCH] Add spherical_mapping command-line argument to ffmpeg. 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Comments below.. On Mon, Jun 5, 2017 at 12:02 PM Vittorio Giovara wrote: > Hey Aaron > > This allows adding AVSphericalMapping information to files > > that don't already have it. > > --- > > ffmpeg.h | 3 ++ > > ffmpeg_opt.c | 29 ++++++++++++- > > libavutil/spherical.c | 113 > ++++++++++++++++++++++++++++++++++++++++++++++++++ > > libavutil/spherical.h | 14 +++++++ > > 4 files changed, 158 insertions(+), 1 deletion(-) > > > > diff --git a/ffmpeg.h b/ffmpeg.h > > index a806445e0d..43a28d874f 100644 > > --- a/ffmpeg.h > > +++ b/ffmpeg.h > > @@ -44,6 +44,7 @@ > > #include "libavutil/fifo.h" > > #include "libavutil/pixfmt.h" > > #include "libavutil/rational.h" > > +#include "libavutil/spherical.h" > > #include "libavutil/threadmessage.h" > > > > #include "libswresample/swresample.h" > > @@ -228,6 +229,8 @@ typedef struct OptionsContext { > > int nb_time_bases; > > SpecifierOpt *enc_time_bases; > > int nb_enc_time_bases; > > + SpecifierOpt *spherical_mappings; > > + int nb_spherical_mappings; > > } OptionsContext; > > > > typedef struct InputFilter { > > diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c > > index c997ea8faf..666b3791b7 100644 > > --- a/ffmpeg_opt.c > > +++ b/ffmpeg_opt.c > > @@ -1514,12 +1514,29 @@ static void > check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, > > } > > } > > > > +static int set_spherical_mapping(const char* opt, AVStream* st) { > > + size_t spherical_mapping_size = 0; > > + AVSphericalMapping *spherical_mapping = NULL; > > + > > + int ret = av_spherical_parse_option(opt, &spherical_mapping, > > + &spherical_mapping_size); > > + if (ret >= 0) { > > + ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, > spherical_mapping, spherical_mapping_size); > > + > > + if (ret < 0) { > > + av_freep(&spherical_mapping); > > + } > > + } > > + > > + return ret; > > +} > > + > > static OutputStream *new_video_stream(OptionsContext *o, > AVFormatContext *oc, int source_index) > > { > > AVStream *st; > > OutputStream *ost; > > AVCodecContext *video_enc; > > - char *frame_rate = NULL, *frame_aspect_ratio = NULL; > > + char *frame_rate = NULL, *frame_aspect_ratio = NULL, > *spherical_mapping = NULL; > > > > ost = new_output_stream(o, oc, AVMEDIA_TYPE_VIDEO, source_index); > > st = ost->st; > > @@ -1546,6 +1563,12 @@ static OutputStream > *new_video_stream(OptionsContext *o, AVFormatContext *oc, in > > > > MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, > st); > > MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, > st); > > + MATCH_PER_STREAM_OPT(spherical_mappings, str, spherical_mapping, > oc, st); > > + > > + if (spherical_mapping && set_spherical_mapping(spherical_mapping, > st) < 0) { > > + av_log(NULL, AV_LOG_FATAL, "Invalid spherical_mapping: %s\n", > spherical_mapping); > > + exit_program(1); > > + } > > > > if (!ost->stream_copy) { > > const char *p = NULL; > > @@ -3569,6 +3592,10 @@ const OptionDef options[] = { > > "automatically insert correct rotate filters" }, > > { "hwaccel_lax_profile_check", OPT_BOOL | OPT_EXPERT, > { &hwaccel_lax_profile_check}, > > "attempt to decode anyway if HW accelerated decoder's supported > profiles do not exactly match the stream" }, > > + { "spherical_mapping", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC > | > > + OPT_OUTPUT, > { .off = OFFSET(spherical_mappings) }, > > + "set spherical mapping for video stream", "spherical_mapping" }, > > + > > > > /* audio options */ > > { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | > OPT_OUTPUT, { .func_arg = opt_audio_frames }, > > this part looks ok > > > diff --git a/libavutil/spherical.c b/libavutil/spherical.c > > index 4be55f36cf..508584d61f 100644 > > --- a/libavutil/spherical.c > > +++ b/libavutil/spherical.c > > @@ -19,6 +19,7 @@ > > */ > > > > #include "mem.h" > > +#include "opt.h" > > #include "spherical.h" > > > > AVSphericalMapping *av_spherical_alloc(size_t *size) > > @@ -77,3 +78,115 @@ int av_spherical_from_name(const char *name) > > > > return -1; > > } > > + > > +static const char *spherical_mapping_context_to_name(void *ptr) > > +{ > > + return "spherical_mapping"; > > +} > > + > > +typedef struct { > > + const AVClass* spherical_class; > > + int projection; > > + > > + double yaw; > > + double pitch; > > + double roll; > > + > > + int64_t bound_left; > > + int64_t bound_top; > > + int64_t bound_right; > > + int64_t bound_bottom; > > + > > + int64_t padding; > > +} SphericalMappingContext; > > + > > +#define OFFSET(x) offsetof(SphericalMappingContext, x) > > +#define DEFAULT 0 > > +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM > > + > > +static const AVOption spherical_mapping_options[] = { > > + { "projection", "projection", OFFSET(projection), AV_OPT_TYPE_INT, > > + { .i64 = -1 }, -1, AV_SPHERICAL_EQUIRECTANGULAR_TILE, FLAGS, > "projection" }, > > + { "equirectangular", "equirectangular projection", > OFFSET(projection), AV_OPT_TYPE_CONST, > > + { .i64 = AV_SPHERICAL_EQUIRECTANGULAR }, INT_MIN, INT_MAX, > FLAGS, "projection" }, > > + { "cubemap", "cubemap projection", 0, AV_OPT_TYPE_CONST, > > + { .i64 = AV_SPHERICAL_CUBEMAP }, INT_MIN, INT_MAX, FLAGS, > "projection" }, > > + { "equirectangular_tile", "tiled equirectangular projection", > OFFSET(projection), AV_OPT_TYPE_CONST, > > + { .i64 = AV_SPHERICAL_EQUIRECTANGULAR_TILE }, INT_MIN, INT_MAX, > FLAGS, "projection" }, > > + { "yaw", "initial yaw orientation in degrees", OFFSET(yaw), > AV_OPT_TYPE_DOUBLE, > > + { .dbl = 0.0 }, -180.0, 180.0, FLAGS }, > > + { "pitch", "initial pitch orientation in degrees", OFFSET(pitch), > AV_OPT_TYPE_DOUBLE, > > + { .dbl = 0.0 }, -90.0, 90.0, FLAGS }, > > + { "roll", "initial roll orientation in degrees", OFFSET(roll), > AV_OPT_TYPE_DOUBLE, > > + { .dbl = 0.0 }, -180.0, 180.0, FLAGS }, > > + { "bound_left", "tiled equirectangular left bound", > OFFSET(bound_left), AV_OPT_TYPE_INT64, > > + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, > > + { "bound_top", "tiled equirectangular top bound", > OFFSET(bound_top), AV_OPT_TYPE_INT64, > > + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, > > + { "bound_right", "tiled equirectangular right bound", > OFFSET(bound_right), AV_OPT_TYPE_INT64, > > + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, > > + { "bound_bottom", "tiled equirectangular bottom bound", > OFFSET(bound_bottom), AV_OPT_TYPE_INT64, > > + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, > > + { "padding", "cubemap padding in pixels", OFFSET(padding), > AV_OPT_TYPE_INT64, > > + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, > > + { NULL } > > +}; > > + > > +static const AVClass av_spherical_mapping_class = { > > + .class_name = "AVSphericalMapping", > > + .item_name = spherical_mapping_context_to_name, > > + .option = spherical_mapping_options, > > + .version = LIBAVUTIL_VERSION_INT, > > +}; > > + > > +int av_spherical_parse_option(const char* opts, AVSphericalMapping > **spherical_mapping, > > + size_t *spherical_mapping_size) { > > + SphericalMappingContext ctx = { > > + .spherical_class = &av_spherical_mapping_class > > + }; > > + int ret; > > + > > + if (!opts) > > + return AVERROR(EINVAL); > > + > > + av_opt_set_defaults(&ctx); > > + ret = av_set_options_string(&ctx, opts, "=", ","); > > + if (ret < 0) > > + return ret; > > + > > + if (ctx.projection == -1) { > > + av_log(NULL, AV_LOG_ERROR, "projection must be specified\n"); > > + return AVERROR(EINVAL); > > + } > > + > > + if (ctx.padding > 0 && ctx.projection != AV_SPHERICAL_CUBEMAP) { > > + av_log(NULL, AV_LOG_ERROR, "padding only allowed for > AV_SPHERICAL_CUBEMAP projection.\n"); > > + return AVERROR(EINVAL); > > + } > > + > > + if ((ctx.bound_left > 0 || ctx.bound_top > 0 || ctx.bound_right > 0 > || > > + ctx.bound_bottom > 0) && ctx.projection != > AV_SPHERICAL_EQUIRECTANGULAR_TILE) { > > + av_log(NULL, AV_LOG_ERROR, "bounds only allowed for > AV_SPHERICAL_EQUIRECTANGULAR_TILE projection.\n"); > > + return AVERROR(EINVAL); > > + } > > + > > + *spherical_mapping = av_spherical_alloc(spherical_mapping_size); > > + if (!*spherical_mapping) > > + return AVERROR(ENOMEM); > > + > > + (*spherical_mapping)->projection = (enum > AVSphericalProjection)ctx.projection; > > + (*spherical_mapping)->yaw = (int32_t)(ctx.yaw * (1 << 16)); > > + (*spherical_mapping)->pitch = (int32_t)(ctx.pitch * (1 << 16)); > > + (*spherical_mapping)->roll = (int32_t)(ctx.roll * (1 << 16)); > > + > > + if (ctx.projection == AV_SPHERICAL_CUBEMAP) { > > + (*spherical_mapping)->padding = (uint32_t)ctx.padding; > > + } else if (ctx.projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) { > > + (*spherical_mapping)->bound_left = (uint32_t)ctx.bound_left; > > + (*spherical_mapping)->bound_top = (uint32_t)ctx.bound_top; > > + (*spherical_mapping)->bound_right = (uint32_t)ctx.bound_right; > > + (*spherical_mapping)->bound_bottom = (uint32_t)ctx.bound_bottom; > > + } > > + > > + return ret; > > +} > > diff --git a/libavutil/spherical.h b/libavutil/spherical.h > > index cef759cf27..643cff7f44 100644 > > --- a/libavutil/spherical.h > > +++ b/libavutil/spherical.h > > @@ -190,6 +190,20 @@ typedef struct AVSphericalMapping { > > */ > > AVSphericalMapping *av_spherical_alloc(size_t *size); > > > > +/** > > + * Parses a command-line option into an AVSphericalMapping object. > > + * > > + * @param opts String containing comma separated list of key=value > pairs. > > + * @param spherical_mapping Output parameter for the AVSphericalMapping > created by this function. > > + * @param spherical_mapping_size Output parameter for indicating the > size of the struct > > + * referenced by *spherical_mapping. > > + * > > + * @return 0 on success and *spherical_mapping and > *spherical_mapping_size contain valid information. > > + * <0 on failure and *spherical_mapping and > *spherical_mapping_size state is undefined. > > + */ > > +int av_spherical_parse_option(const char* opt, AVSphericalMapping > **spherical_mapping, > > + size_t *spherical_mapping_size); > > + > > /** > > * Convert the @ref bounding fields from an AVSphericalVideo > > * from 0.32 fixed point to pixels. > > This is the part that I have a problem with. The code itself is > totally fine, but I do believe that this API should not be exposed > publicly. > It's too custom specific and binds the ffmpeg option syntax to > libavutil while they are two separate layers that need to stay > separated. > It should be either something more generic so that other side data can > attach to it, or it should have its own proper syntax and AVOption > type. > For the task at hand though I believe it would be sufficient to move > option parsing in ffmpeg_opt.c > Fair enough. I was on the fence about this but didn't feel strongly either way. I've attached a new patch that only contains changes in ffmpeg.h and ffmpeg_opt.c. Thanks for the quick review. Aaron > > Cheers > -- > Vittorio > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > From fd520ed807ddd843d9848aaf57695af3e0abf97e Mon Sep 17 00:00:00 2001 From: Aaron Colwell Date: Fri, 2 Jun 2017 16:11:21 -0700 Subject: [PATCH] Add spherical_mapping command-line argument to ffmpeg. This allows adding AVSphericalMapping information to files that don't already have it. --- ffmpeg.h | 3 ++ ffmpeg_opt.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/ffmpeg.h b/ffmpeg.h index a806445e0d..43a28d874f 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -44,6 +44,7 @@ #include "libavutil/fifo.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" +#include "libavutil/spherical.h" #include "libavutil/threadmessage.h" #include "libswresample/swresample.h" @@ -228,6 +229,8 @@ typedef struct OptionsContext { int nb_time_bases; SpecifierOpt *enc_time_bases; int nb_enc_time_bases; + SpecifierOpt *spherical_mappings; + int nb_spherical_mappings; } OptionsContext; typedef struct InputFilter { diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index c997ea8faf..319a350c44 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -40,6 +40,7 @@ #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" +#include "libavutil/spherical.h" #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass" @@ -1514,12 +1515,130 @@ static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, } } +static int set_spherical_mapping(const char* opt, AVStream* st) { + typedef struct { + const AVClass* spherical_class; + int projection; + + double yaw; + double pitch; + double roll; + + int64_t bound_left; + int64_t bound_top; + int64_t bound_right; + int64_t bound_bottom; + + int64_t padding; + } SphericalMappingContext; + +#define OFFSET(x) offsetof(SphericalMappingContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM + + static const AVOption opts[] = { + { "projection", "projection", OFFSET(projection), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, AV_SPHERICAL_EQUIRECTANGULAR_TILE, FLAGS, "projection" }, + { "equirectangular", "equirectangular projection", OFFSET(projection), AV_OPT_TYPE_CONST, + { .i64 = AV_SPHERICAL_EQUIRECTANGULAR }, INT_MIN, INT_MAX, FLAGS, "projection" }, + { "cubemap", "cubemap projection", 0, AV_OPT_TYPE_CONST, + { .i64 = AV_SPHERICAL_CUBEMAP }, INT_MIN, INT_MAX, FLAGS, "projection" }, + { "equirectangular_tile", "tiled equirectangular projection", OFFSET(projection), AV_OPT_TYPE_CONST, + { .i64 = AV_SPHERICAL_EQUIRECTANGULAR_TILE }, INT_MIN, INT_MAX, FLAGS, "projection" }, + { "yaw", "initial yaw orientation in degrees", OFFSET(yaw), AV_OPT_TYPE_DOUBLE, + { .dbl = 0.0 }, -180.0, 180.0, FLAGS }, + { "pitch", "initial pitch orientation in degrees", OFFSET(pitch), AV_OPT_TYPE_DOUBLE, + { .dbl = 0.0 }, -90.0, 90.0, FLAGS }, + { "roll", "initial roll orientation in degrees", OFFSET(roll), AV_OPT_TYPE_DOUBLE, + { .dbl = 0.0 }, -180.0, 180.0, FLAGS }, + { "bound_left", "tiled equirectangular left bound", OFFSET(bound_left), AV_OPT_TYPE_INT64, + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, + { "bound_top", "tiled equirectangular top bound", OFFSET(bound_top), AV_OPT_TYPE_INT64, + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, + { "bound_right", "tiled equirectangular right bound", OFFSET(bound_right), AV_OPT_TYPE_INT64, + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, + { "bound_bottom", "tiled equirectangular bottom bound", OFFSET(bound_bottom), AV_OPT_TYPE_INT64, + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, + { "padding", "cubemap padding in pixels", OFFSET(padding), AV_OPT_TYPE_INT64, + { .i64 = 0 }, 0, UINT_MAX, FLAGS }, + { NULL } + }; +#undef OFFSET +#undef FLAGS + + static const AVClass spherical_mapping_class = { + .class_name = "", + .item_name = av_default_item_name, + .option = opts, + .version = LIBAVUTIL_VERSION_INT, + }; + + SphericalMappingContext ctx = { + .spherical_class = &spherical_mapping_class + }; + + size_t spherical_mapping_size = 0; + AVSphericalMapping *spherical_mapping = NULL; + int ret; + + if (!opt) + return AVERROR(EINVAL); + + av_opt_set_defaults(&ctx); + ret = av_set_options_string(&ctx, opt, "=", ","); + if (ret < 0) + return ret; + + if (ctx.projection == -1) { + av_log(NULL, AV_LOG_ERROR, "projection must be specified\n"); + return AVERROR(EINVAL); + } + + if (ctx.padding > 0 && ctx.projection != AV_SPHERICAL_CUBEMAP) { + av_log(NULL, AV_LOG_ERROR, "padding only allowed for AV_SPHERICAL_CUBEMAP projection.\n"); + return AVERROR(EINVAL); + } + + if ((ctx.bound_left > 0 || ctx.bound_top > 0 || ctx.bound_right > 0 || + ctx.bound_bottom > 0) && ctx.projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE) { + av_log(NULL, AV_LOG_ERROR, "bounds only allowed for AV_SPHERICAL_EQUIRECTANGULAR_TILE projection.\n"); + return AVERROR(EINVAL); + } + + spherical_mapping = av_spherical_alloc(spherical_mapping_size); + if (!spherical_mapping) + return AVERROR(ENOMEM); + + spherical_mapping->projection = (enum AVSphericalProjection)ctx.projection; + spherical_mapping->yaw = (int32_t)(ctx.yaw * (1 << 16)); + spherical_mapping->pitch = (int32_t)(ctx.pitch * (1 << 16)); + spherical_mapping->roll = (int32_t)(ctx.roll * (1 << 16)); + + if (ctx.projection == AV_SPHERICAL_CUBEMAP) { + spherical_mapping->padding = (uint32_t)ctx.padding; + } else if (ctx.projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) { + spherical_mapping->bound_left = (uint32_t)ctx.bound_left; + spherical_mapping->bound_top = (uint32_t)ctx.bound_top; + spherical_mapping->bound_right = (uint32_t)ctx.bound_right; + spherical_mapping->bound_bottom = (uint32_t)ctx.bound_bottom; + } + + if (ret >= 0) { + ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, spherical_mapping, spherical_mapping_size); + + if (ret < 0) { + av_freep(&spherical_mapping); + } + } + + return ret; +} + static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, int source_index) { AVStream *st; OutputStream *ost; AVCodecContext *video_enc; - char *frame_rate = NULL, *frame_aspect_ratio = NULL; + char *frame_rate = NULL, *frame_aspect_ratio = NULL, *spherical_mapping = NULL; ost = new_output_stream(o, oc, AVMEDIA_TYPE_VIDEO, source_index); st = ost->st; @@ -1546,6 +1665,12 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); + MATCH_PER_STREAM_OPT(spherical_mappings, str, spherical_mapping, oc, st); + + if (spherical_mapping && set_spherical_mapping(spherical_mapping, st) < 0) { + av_log(NULL, AV_LOG_FATAL, "Invalid spherical_mapping: %s\n", spherical_mapping); + exit_program(1); + } if (!ost->stream_copy) { const char *p = NULL; @@ -3569,6 +3694,10 @@ const OptionDef options[] = { "automatically insert correct rotate filters" }, { "hwaccel_lax_profile_check", OPT_BOOL | OPT_EXPERT, { &hwaccel_lax_profile_check}, "attempt to decode anyway if HW accelerated decoder's supported profiles do not exactly match the stream" }, + { "spherical_mapping", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | + OPT_OUTPUT, { .off = OFFSET(spherical_mappings) }, + "set spherical mapping for video stream", "spherical_mapping" }, + /* audio options */ { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, -- 2.13.0.506.g27d5fe0cd-goog