From patchwork Fri Oct 6 15:19:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Colwell X-Patchwork-Id: 5438 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.161.90 with SMTP id m26csp73831jah; Fri, 6 Oct 2017 08:28:41 -0700 (PDT) X-Received: by 10.223.151.51 with SMTP id r48mr2462256wrb.164.1507303721370; Fri, 06 Oct 2017 08:28:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507303721; cv=none; d=google.com; s=arc-20160816; b=g+9/ja+tArFpjydsBk5FEicwryOqDfGdNJ4TxxoJLsyfoo5pLSd6OM0wlbGDXF2Rk+ QFbfA76uSVKMpZ9gto/hzHAQ/FMxDziqExi1YdEPtdxj9vkir0AZFn7a1Qs8Jt6G+LjE 4TEQBTvvc8ETqdgcHFwtp3fZW8gHBMDCoVTBi5J0FX4CtMY3nuNGrjPCj53trfgNuELW d4Fyrmj54bdt0t9HwR9ZKM4nC/ZL4Du30Trz0XqLsmW+Ca9CetUTPN14t4kUQ24jJt70 5iOqgCOSJsCT1wu3hY3xrlU5JIKuUSDfgwR3YeR3SZ/5Ly8YADcLGul2e0MGgwQVRyaP iLVw== 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:mime-version:dkim-signature:delivered-to :arc-authentication-results; bh=pcCldeIEzBj/htRlhVmbrEZAGttTxbSQjxZc4bqRVHA=; b=NouaAhGpf8vC57t0kIqUKf5CJHqBezBb1qSsirOGoyN0RaEh62zIynq+6mziwuVqeU xHW9Yh/0GTt8MdVxPCKCZNj+hkP3ACf8rpOnu4p1SW4etH5T8XaQPalzm/LPiBrn9COk RK49WTihWMHGaViE5M3Mb4snycMwFSpDtVBNzr3RPK12HL9yH96DVZyrUSCK2G8xAPdE 1GGWdslLOxqkfyCLfsTwErp++jROqYCXhdIVx4eRzZz8giQ/ww8JofOnBr2V9u+8Xoo3 2LFjjLMXbFyv5RbSguRFSXxFZ02TxUgKRzREXfyiwXrfjJjQw/W2Oj0XXpT/qxZN3CIn 9jjw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20161025 header.b=KmcWrDDR; 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 s95si1591880wrc.364.2017.10.06.08.28.40; Fri, 06 Oct 2017 08:28:41 -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 header.s=20161025 header.b=KmcWrDDR; 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 B28F368A4D8; Fri, 6 Oct 2017 18:28:36 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt0-f171.google.com (mail-qt0-f171.google.com [209.85.216.171]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 91F5B68A49F for ; Fri, 6 Oct 2017 18:28:30 +0300 (EEST) Received: by mail-qt0-f171.google.com with SMTP id 6so22984765qtw.3 for ; Fri, 06 Oct 2017 08:28:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=s+ZjPFnzbTR5C1ySe2sLnK/eBKInZQ1aYLPXX9OD+gA=; b=KmcWrDDRpn7TQ4tq08Z0RJEYIlE+jJBBIe17pgyZK/ED8zFirfiIQGm2yuZ3kzynbI FVuQokPRrcsJ8alrYh6alo1rfdQbKo0pjJbssT/mS+mzu4CPFtKHQR3XkYd6u2gB0w2S Bsijwcmq6luBae2lL25d+DeqDqOYSQsECgSRamSnAr8NCkwmY1JrcWKgJL+zH1PFfAQH BLsNPuahzwIkWkNe6tb0BKHU+NlBGtr5ay/mi0gJubtXe6CJmThLGHGgaLGROkI1w+eg nLKAo5CcUV1zrRVavODPTo7xcXbZLSRRo/RdsLVZNIh0q6TRM6PVlvpt1yJy5rKO7JYD S16g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=s+ZjPFnzbTR5C1ySe2sLnK/eBKInZQ1aYLPXX9OD+gA=; b=OaUZ5giDH04oGsWjplEItjCM8PQX5fGOsu7JCVK6bFqdJVMVWqDZU0bu1ufu1NMm4D Ta8o7z+rC1G4RO4CoPImr4MPf82vFj21He7AS7X11HUatNVaeGpF4jP1g32HxL1zAqSx jaWQXfGAR5COOjYPEB1OMw4ddHOZSyDAWuMwSo5M+MKtJNKaJvkjzSFkWkAS3+K5zJFa WHd2EHiCqPin0ukXdwBaFMuG3SC+k/qpFkl82Vw/659fQJBGk9QJzgPbdtDHZp0AJZZ3 7HQ4lJC0pG18vDhEOVu8/bUXKt2NVApZvB4yPS07P0Foji1cB+eI8ZdwxgvleN1+e4en +10w== X-Gm-Message-State: AMCzsaVgKvG5iW2crlaBf6pIyXuljNJViP4+U4Cx9ypXbZjIu221+Bll nPumjOVcaI1orrQsMSCnQssOKbfE4x8LsBF1g3dUMfT0 X-Google-Smtp-Source: AOwi7QDqAhh6fyFFtz5mEFNyMLXLoMpsaQlZ6ipj4Io5PBsmJ6zGAXYENHepmFzMzJk0ruPJe5CJjZlQn0lY4WXUo1Q= X-Received: by 10.237.60.101 with SMTP id u34mr3285684qte.153.1507303211474; Fri, 06 Oct 2017 08:20:11 -0700 (PDT) MIME-Version: 1.0 From: Aaron Colwell Date: Fri, 06 Oct 2017 15:19:59 +0000 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] [PATCH] ffmpeg: Add spherical_mapping command-line option. 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" Allows spherical mapping metadata to be injected into files. From 6a86e9766708b9b74e4ae0ec6928a81df4041afc Mon Sep 17 00:00:00 2001 From: Aaron Colwell Date: Fri, 6 Oct 2017 08:14:15 -0700 Subject: [PATCH] ffmpeg: Add spherical_mapping command-line option. Allows spherical mapping metadata to be injected into files. --- fftools/ffmpeg.c | 12 +++- fftools/ffmpeg.h | 6 ++ fftools/ffmpeg_opt.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 176 insertions(+), 2 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 1d248bc269..8c35090b9e 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -3092,6 +3092,10 @@ static int init_output_stream_streamcopy(OutputStream *ost) const AVPacketSideData *sd_src = &ist->st->side_data[i]; uint8_t *dst_data; + if ((sd_src->type == AV_PKT_DATA_SPHERICAL && ost->spherical_mapping_overridden) || + (sd_src->type == AV_PKT_DATA_STEREO3D && ost->stereo3d_overridden)) + continue; + dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); if (!dst_data) return AVERROR(ENOMEM); @@ -3528,7 +3532,13 @@ static int init_output_stream(OutputStream *ost, char *error, int error_len) int i; for (i = 0; i < ist->st->nb_side_data; i++) { AVPacketSideData *sd = &ist->st->side_data[i]; - uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); + uint8_t *dst; + + if ((sd->type == AV_PKT_DATA_SPHERICAL && ost->spherical_mapping_overridden) || + (sd->type == AV_PKT_DATA_STEREO3D && ost->stereo3d_overridden)) + continue; + + dst = av_stream_new_side_data(ost->st, sd->type, sd->size); if (!dst) return AVERROR(ENOMEM); memcpy(dst, sd->data, sd->size); diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index f6c76bcc55..91ca6c2926 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -45,6 +45,8 @@ #include "libavutil/hwcontext.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" +#include "libavutil/spherical.h" +#include "libavutil/stereo3d.h" #include "libavutil/threadmessage.h" #include "libswresample/swresample.h" @@ -237,6 +239,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 { @@ -487,6 +491,8 @@ typedef struct OutputStream { int top_field_first; int rotate_overridden; double rotate_override_value; + int spherical_mapping_overridden; + int stereo3d_overridden; AVRational frame_aspect_ratio; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 100fa76e46..4bc5104ce5 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -40,6 +40,8 @@ #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" +#include "libavutil/spherical.h" +#include "libavutil/stereo3d.h" #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass" @@ -1585,12 +1587,158 @@ static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc, } } +static int set_spherical_mapping(const char* opt, OutputStream *ost) { + 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; + + int stereo_mode; + } 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", 0, 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", 0, 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 }, + { "stereo_mode", "stereo_mode", OFFSET(stereo_mode), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, AV_STEREO3D_TOPBOTTOM, FLAGS, "stereo_mode" }, + { "top-bottom", "Top/Bottom stereo mode", 0, AV_OPT_TYPE_CONST, + { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, FLAGS, "stereo_mode" }, + { "left-right", "Left/Right stereo mode", 0, AV_OPT_TYPE_CONST, + { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, FLAGS, "stereo_mode" }, + { 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 + }; + + AVStream* st = ost->st; + 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); + } + + ost->spherical_mapping_overridden = 1; + + if (ctx.stereo_mode != -1) { + AVStereo3D* stereo3d = av_stereo3d_alloc(); + + if (!stereo3d) + return AVERROR(ENOMEM); + + stereo3d->type = (enum AVStereo3DType)(ctx.stereo_mode); + ret = av_stream_add_side_data(st, AV_PKT_DATA_STEREO3D, stereo3d, sizeof(*stereo3d)); + + if (ret < 0) { + av_freep(&stereo3d); + return ret; + } + + ost->stereo3d_overridden = 1; + } + } + + 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; @@ -1617,6 +1765,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, ost) < 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; @@ -3642,6 +3796,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.14.2.920.gcf0c67979c-goog