[FFmpeg-devel] [PATCH 1/1] This change adds an encoder for Camera metadata motion. This is a type of sensor data associated with video, such as GPS, acceleration, gyro, and camera orientation. It does not encode video itself, but rather, this metadata.

Louis O'Bryan lboextdev at gmail.com
Fri Jul 7 03:36:39 EEST 2017


From: Louis O'Bryan <louiso at google.com>

Signed-off-by: Louis O'Bryan <louiso at google.com>
---
 Changelog               |   1 +
 doc/general.texi        |   2 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   3 +
 libavcodec/avcodec.h    |   1 +
 libavcodec/cammenc.c    | 299 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/codec_desc.c |   6 +
 libavcodec/version.h    |   4 +-
 libavformat/isom.c      |   6 +
 libavformat/movenc.c    | 200 +++++++++++++++++---------------
 10 files changed, 431 insertions(+), 92 deletions(-)
 create mode 100644 libavcodec/cammenc.c

diff --git a/Changelog b/Changelog
index a8726c6736..5f98385b53 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
 releases are sorted from youngest to oldest.
 
 version <next>:
+- Camera metadata motion encoder
 - deflicker video filter
 - doubleweave video filter
 - lumakey video filter
diff --git a/doc/general.texi b/doc/general.texi
index 8f582d586f..06996c81e8 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -912,6 +912,8 @@ following image formats are supported:
     @tab part of LCL, encoder experimental
 @item Zip Motion Blocks Video  @tab   X @tab  X
     @tab Encoder works only in PAL8.
+ at item Camera metadata motion    @tab X  @tab
+    @tab Encoder for camera sensor data.
 @end multitable
 
 @code{X} means that encoding (resp. decoding) is supported.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index b440a00746..306cc793ee 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -680,6 +680,7 @@ OBJS-$(CONFIG_ZLIB_DECODER)            += lcldec.o
 OBJS-$(CONFIG_ZLIB_ENCODER)            += lclenc.o
 OBJS-$(CONFIG_ZMBV_DECODER)            += zmbv.o
 OBJS-$(CONFIG_ZMBV_ENCODER)            += zmbvenc.o
+OBJS-$(CONFIG_CAMERA_MOTION_METADATA_ENCODER) += cammenc.o
 
 # (AD)PCM decoders/encoders
 OBJS-$(CONFIG_PCM_ALAW_DECODER)           += pcm.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 0243f47358..eff51f4042 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -651,6 +651,9 @@ static void register_all(void)
     REGISTER_DECODER(XBIN,              xbin);
     REGISTER_DECODER(IDF,               idf);
 
+    /* data */
+    REGISTER_ENCODER(CAMERA_MOTION_METADATA, camera_motion_metadata);
+
     /* external libraries, that shouldn't be used by default if one of the
      * above is available */
     REGISTER_ENCDEC (LIBOPENH264,       libopenh264);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index b697afa0ae..622383f453 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -681,6 +681,7 @@ enum AVCodecID {
     AV_CODEC_ID_DVD_NAV,
     AV_CODEC_ID_TIMED_ID3,
     AV_CODEC_ID_BIN_DATA,
+    AV_CODEC_ID_CAMERA_MOTION_METADATA,
 
 
     AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it
diff --git a/libavcodec/cammenc.c b/libavcodec/cammenc.c
new file mode 100644
index 0000000000..d7592ab69d
--- /dev/null
+++ b/libavcodec/cammenc.c
@@ -0,0 +1,299 @@
+/*
+ * Reference implementation for the CAMM Metadata encoder.
+ * Encodes sensor data for 360-degree cameras such as
+ * GPS, gyro, and acceleration. This is stored in a track separate from video
+ * and audio.
+ *
+ * Copyright (c) 2017 Louis O'Bryan
+ *
+ * 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
+ */
+
+/**
+ * CAMM Metadata encoder
+ * @author Louis O'Bryan
+ */
+
+#include <math.h>
+#include <float.h>
+#include "avcodec.h"
+#include "internal.h"
+#include "bytestream.h"
+#include "libavutil/common.h"
+#include "libavutil/opt.h"
+
+#define DUMMY_ENCODER_SIZE 1
+
+typedef struct CammContext {
+    AVCodecContext *avctx;
+} CammContext;
+
+// Sizes of each type of metadata.
+static int metadata_type_sizes[] = {
+  3 * sizeof(float),
+  2 * sizeof(uint64_t),
+  3 * sizeof(float),
+  3 * sizeof(float),
+  3 * sizeof(float),
+  3 * sizeof(float),
+  3 * sizeof(double) + sizeof(uint32_t) + 7 * sizeof(float),
+  3 * sizeof(float)
+};
+
+static int min_packet_size = sizeof(uint16_t) * 2;
+
+/**
+ * Validates that the latitude has a valid value. Returns 1 if ok, else 0.
+ */
+static int validate_latitude(AVCodecContext *avctx, double latitude) {
+    if (latitude < -M_PI / 4 || latitude > M_PI / 4) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Latitude %f is not in bounds (%f, %f)\n",
+               latitude, -M_PI / 4, M_PI / 4);
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * Validates that the longitude has a valid value. Returns 1 if ok, else 0.
+ */
+static int validate_longitude(AVCodecContext *avctx, double longitude) {
+    if (longitude < -M_PI / 2 || longitude > M_PI / 2) {
+        av_log(avctx, AV_LOG_ERROR,
+               "longitude %f is not in bounds (%f, %f)\n",
+               longitude, -M_PI / 2, M_PI / 2);
+        return 0;
+    }
+    return 1;
+}
+
+static void log_float_values(AVCodecContext *avctx, const char *name, float *data) {
+    int i;
+    float value;
+    for (i = 0; i < 3; ++i) {
+        value = data[i];
+        av_log(avctx, AV_LOG_DEBUG, "%s[%d] = %f\n", name, i, value);
+    }
+}
+
+/**
+ * Validates that the incoming data is correctly formatted and returns
+ * 1 if it is, 0 otherwise.
+ */
+static int validate_data(AVCodecContext *avctx, const AVFrame *data) {
+    uint16_t packet_type;
+    int expected_packet_size;
+    uint64_t pixel_exposure_time;
+    uint64_t rolling_shutter_skew_time;
+    uint32_t *camm_data;
+    double time_gps_epoch;
+    uint32_t gps_fix_type;
+    double latitude;
+    double longitude;
+    float altitude;
+
+    if (data->pkt_size < min_packet_size) {
+        av_log(avctx, AV_LOG_ERROR,
+               "CAMM input data with size %d is too small\n", data->pkt_size);
+        return 0;
+    }
+
+    packet_type = ((uint16_t*)(*data->extended_data))[1];
+    if (packet_type > sizeof(metadata_type_sizes) / sizeof(int)) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Packet type %d is not recognized\n", packet_type);
+        return 0;
+    }
+
+    expected_packet_size = metadata_type_sizes[packet_type];
+    if (expected_packet_size != data->pkt_size) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Packet size %d does not match expected size %d for type %d\n",
+               data->pkt_size, expected_packet_size, packet_type);
+        return 0;
+    }
+
+    // The actual sensor data starts after the reserved slot and packet type.
+    camm_data = ((uint32_t*)(*data->extended_data)) + 1;
+    switch (packet_type) {
+        // float angle_axis[3]
+        case 0:
+            log_float_values(avctx, "angle_axis", (float*)camm_data);
+            break;
+        // int32 pixel_exposure_time
+        // int32 rolling_shutter_skew_time
+        case 1:
+            pixel_exposure_time = ((uint32_t*)camm_data)[0];
+            av_log(avctx, AV_LOG_DEBUG,
+                   "pixel_exposure_time = %lu\n", pixel_exposure_time);
+            rolling_shutter_skew_time = ((uint32_t*)camm_data)[1];
+            av_log(avctx, AV_LOG_DEBUG, "rolling_shutter_skew_time = %lu\n",
+                   rolling_shutter_skew_time);
+            break;
+        // float gyro[3]
+        case 2:
+            log_float_values(avctx, "gyro", (float*)camm_data);
+            break;
+        case 3:
+            log_float_values(avctx, "acceleration", (float*)camm_data);
+            break;
+        // float position[3]
+        case 4:
+            log_float_values(avctx, "position", (float*)camm_data);
+            break;
+        // float latitude
+        // float longitude
+        // float altitude
+        case 5:
+            if (!validate_latitude(avctx, ((float*)camm_data)[0])) return 0;
+            if (!validate_longitude(avctx, ((float*)camm_data)[1])) return 0;
+
+            av_log(avctx, AV_LOG_DEBUG, "latitude = %.6f\n",
+                   ((float*)camm_data)[0]);
+            av_log(avctx, AV_LOG_DEBUG, "longitude = %.6f\n",
+                   ((float*)camm_data)[1]);
+            av_log(avctx, AV_LOG_DEBUG, "altitude = %.6f\n",
+                   ((float*)camm_data)[2]);
+            break;
+        case 6:
+            time_gps_epoch = ((double*)camm_data)[0];
+            if (time_gps_epoch < 0) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "Time gps epoch %.6f is less than 0.\n",
+                       time_gps_epoch);
+                return 0;
+            }
+            av_log(avctx, AV_LOG_DEBUG, "time_gps_epoch = %.6f\n",
+                   time_gps_epoch);
+            camm_data = (uint32_t*) (((double*)camm_data) + 1);
+
+            gps_fix_type = ((uint32_t*)camm_data)[0];
+            if (gps_fix_type != 0 && gps_fix_type != 1 && gps_fix_type != 2) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "GPS fix type %d is not valid. Should be 0, 1, or 2\n",
+                       gps_fix_type);
+                return 0;
+            }
+            av_log(avctx, AV_LOG_DEBUG, "gps_fix_type = %d\n", gps_fix_type);
+            camm_data = (uint32_t*) (((int32_t*)camm_data) + 1);
+
+            latitude = ((double*)camm_data)[0];
+            if (!validate_latitude(avctx, latitude)) return 0;
+            av_log(avctx, AV_LOG_DEBUG, "latitude = %.6f\n", latitude);
+            camm_data = (uint32_t*) (((double*)camm_data) + 1);
+
+            longitude = ((double*)camm_data)[0];
+            if (!validate_longitude(avctx, longitude)) return 0;
+            av_log(avctx, AV_LOG_DEBUG, "longitude = %.6f\n", latitude);
+            camm_data = (uint32_t*) (((double*)camm_data) + 1);
+
+            altitude = ((float*)camm_data)[0];
+            av_log(avctx, AV_LOG_DEBUG, "altitude = %.6f\n", altitude);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "horizontal accuracy = %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "vertical accuracy %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "vertical east %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "vertical north %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "vertical up %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+
+            av_log(avctx, AV_LOG_DEBUG, "speed accuracy %.6f\n",
+                   ((float*)camm_data)[0]);
+            camm_data = (uint32_t*) (((float*)camm_data) + 1);
+            break;
+        // float magnetic_field[3]
+        case 7:
+            log_float_values(avctx, "magnetic_field", (float*)camm_data);
+            break;
+    }
+
+    return 1;
+}
+
+/**
+ * Encode camera motion metadata. Essentially, this encoder validates the
+ * input data and then just copies the data to an output AVPacket.
+ *  @param avctx          The AVCodecContext context.
+ *  @param packet         The output packet to write the encoded data.
+ *  @param data           The input camera sensor data.
+ *  @param got_packet     1 if a non-empty packet was returned, 0 otherwise.
+ */
+static int encode_data(AVCodecContext *avctx, AVPacket *packet,
+                        const AVFrame *data, int *got_packet)
+{
+    int ret;
+
+    if (!validate_data(avctx, data)) {
+        return AVERROR(EINVAL);
+    }
+
+    // Make sure there is enough size allocated in packet->data.
+    if ((ret = ff_alloc_packet2(avctx, packet, data->pkt_size, 0)) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Can't allocate %d bytes in packet\n",
+               data->pkt_size);
+        return ret;
+    }
+
+    // Copy necessary fields to the AVPacket.
+    packet->pts = data->pts;
+    packet->duration = data->pkt_duration;
+    memcpy(packet->data, *(data->extended_data), data->pkt_size);
+
+    // Indicate that a packet was created.
+    *got_packet = 1;
+    return 0;
+}
+
+/**
+ * Initializes the codec.
+ *
+ * @param avctx     The AVCodecContext context.
+ */
+static av_cold int encode_init(AVCodecContext *avctx) {
+    // Use dummy values for the height and width.
+    avctx->width = DUMMY_ENCODER_SIZE;
+    avctx->height = DUMMY_ENCODER_SIZE;
+    avctx->max_pixels = DUMMY_ENCODER_SIZE;
+
+    return 0;
+}
+
+// Define the encoder that ffmpeg will use for CAMM data.
+AVCodec ff_camera_motion_metadata_encoder = {
+    .name           = "camm",
+    .long_name      = NULL_IF_CONFIG_SMALL("camera motion metadata"),
+    .type           = AVMEDIA_TYPE_DATA,
+    .id             = AV_CODEC_ID_CAMERA_MOTION_METADATA,
+    .priv_data_size = sizeof(CammContext),
+    .encode2        = encode_data,
+    .init           = encode_init,
+};
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index cf1246e431..014c1cd6b3 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3100,6 +3100,12 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .name      = "scte_35",
         .long_name = NULL_IF_CONFIG_SMALL("SCTE 35 Message Queue"),
     },
+    {
+        .id        = AV_CODEC_ID_CAMERA_MOTION_METADATA,
+        .type      = AVMEDIA_TYPE_DATA,
+        .name      = "camm",
+        .long_name = NULL_IF_CONFIG_SMALL("camera motion metadata"),
+    },
 
     /* deprecated codec ids */
 };
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 3c5fea9327..5b99785a72 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,8 +28,8 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  57
-#define LIBAVCODEC_VERSION_MINOR 100
-#define LIBAVCODEC_VERSION_MICRO 103
+#define LIBAVCODEC_VERSION_MINOR 101
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavformat/isom.c b/libavformat/isom.c
index 3a9b3baf96..ca5107bf7f 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -67,6 +67,7 @@ const AVCodecTag ff_mp4_obj_type[] = {
     { AV_CODEC_ID_VORBIS      , 0xDD }, /* nonstandard, gpac uses it */
     { AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* nonstandard, see unsupported-embedded-subs-2.mp4 */
     { AV_CODEC_ID_QCELP       , 0xE1 },
+    { AV_CODEC_ID_CAMERA_MOTION_METADATA, 0xE2 },
     { AV_CODEC_ID_MPEG4SYSTEMS, 0x01 },
     { AV_CODEC_ID_MPEG4SYSTEMS, 0x02 },
     { AV_CODEC_ID_NONE        ,    0 },
@@ -368,6 +369,11 @@ const AVCodecTag ff_codec_movsubtitle_tags[] = {
     { AV_CODEC_ID_NONE, 0 },
 };
 
+const AVCodecTag ff_codec_movdata_tags[] = {
+    { AV_CODEC_ID_CAMERA_MOTION_METADATA, MKTAG('c','a','m','m') },
+};
+
+
 /* map numeric codes from mdhd atom to ISO 639 */
 /* cf. QTFileFormat.pdf p253, qtff.pdf p205 */
 /* http://developer.apple.com/documentation/mac/Text/Text-368.html */
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 88f2f2c819..49528f8635 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -35,8 +35,6 @@
 #include "libavcodec/dnxhddata.h"
 #include "libavcodec/flac.h"
 #include "libavcodec/get_bits.h"
-
-#include "libavcodec/internal.h"
 #include "libavcodec/put_bits.h"
 #include "libavcodec/vc1_common.h"
 #include "libavcodec/raw.h"
@@ -1126,10 +1124,7 @@ static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track)
 
     avio_wb32(pb, 0);
     ffio_wfourcc(pb, "hvcC");
-    if (track->tag == MKTAG('h','v','c','1'))
-        ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 1);
-    else
-        ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 0);
+    ff_isom_write_hvcc(pb, track->vos_data, track->vos_len, 0);
     return update_size(pb, pos);
 }
 
@@ -1230,6 +1225,60 @@ static int mov_write_dpxe_tag(AVIOContext *pb, MOVTrack *track)
     return 0;
 }
 
+static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+    int tag = track->par->codec_tag;
+
+    if (!ff_codec_get_tag(ff_mp4_obj_type, track->par->codec_id))
+        return 0;
+
+    if      (track->par->codec_id == AV_CODEC_ID_H264)      tag = MKTAG('a','v','c','1');
+    else if (track->par->codec_id == AV_CODEC_ID_HEVC)      tag = MKTAG('h','e','v','1');
+    else if (track->par->codec_id == AV_CODEC_ID_VP9)       tag = MKTAG('v','p','0','9');
+    else if (track->par->codec_id == AV_CODEC_ID_AC3)       tag = MKTAG('a','c','-','3');
+    else if (track->par->codec_id == AV_CODEC_ID_EAC3)      tag = MKTAG('e','c','-','3');
+    else if (track->par->codec_id == AV_CODEC_ID_DIRAC)     tag = MKTAG('d','r','a','c');
+    else if (track->par->codec_id == AV_CODEC_ID_MOV_TEXT)  tag = MKTAG('t','x','3','g');
+    else if (track->par->codec_id == AV_CODEC_ID_VC1)       tag = MKTAG('v','c','-','1');
+    else if (track->par->codec_id == AV_CODEC_ID_FLAC)      tag = MKTAG('f','L','a','C');
+    else if (track->par->codec_id == AV_CODEC_ID_OPUS)      tag = MKTAG('O','p','u','s');
+    else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO)  tag = MKTAG('m','p','4','v');
+    else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO)  tag = MKTAG('m','p','4','a');
+    else if (track->par->codec_id == AV_CODEC_ID_DVD_SUBTITLE)  tag = MKTAG('m','p','4','s');
+
+    return tag;
+}
+
+static const AVCodecTag codec_ipod_tags[] = {
+    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },
+    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },
+    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },
+    { AV_CODEC_ID_ALAC,     MKTAG('a','l','a','c') },
+    { AV_CODEC_ID_AC3,      MKTAG('a','c','-','3') },
+    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
+    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
+    { AV_CODEC_ID_NONE, 0 },
+};
+
+static int ipod_get_codec_tag(AVFormatContext *s, MOVTrack *track)
+{
+    int tag = track->par->codec_tag;
+
+    // keep original tag for subs, ipod supports both formats
+    if (!(track->par->codec_type == AVMEDIA_TYPE_SUBTITLE &&
+          (tag == MKTAG('t', 'x', '3', 'g') ||
+           tag == MKTAG('t', 'e', 'x', 't'))))
+        tag = ff_codec_get_tag(codec_ipod_tags, track->par->codec_id);
+
+    if (!av_match_ext(s->filename, "m4a") &&
+        !av_match_ext(s->filename, "m4b") &&
+        !av_match_ext(s->filename, "m4v"))
+        av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a, .m4v nor  .m4b "
+               "Quicktime/Ipod might not play the file\n");
+
+    return tag;
+}
+
 static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track)
 {
     int tag;
@@ -1506,25 +1555,42 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track)
     return tag;
 }
 
+static const AVCodecTag codec_3gp_tags[] = {
+    { AV_CODEC_ID_H263,     MKTAG('s','2','6','3') },
+    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },
+    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },
+    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },
+    { AV_CODEC_ID_AMR_NB,   MKTAG('s','a','m','r') },
+    { AV_CODEC_ID_AMR_WB,   MKTAG('s','a','w','b') },
+    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
+    { AV_CODEC_ID_NONE, 0 },
+};
+
+static const AVCodecTag codec_f4v_tags[] = { // XXX: add GIF/PNG/JPEG?
+    { AV_CODEC_ID_MP3,    MKTAG('.','m','p','3') },
+    { AV_CODEC_ID_AAC,    MKTAG('m','p','4','a') },
+    { AV_CODEC_ID_H264,   MKTAG('a','v','c','1') },
+    { AV_CODEC_ID_VP6A,   MKTAG('V','P','6','A') },
+    { AV_CODEC_ID_VP6F,   MKTAG('V','P','6','F') },
+    { AV_CODEC_ID_NONE, 0 },
+};
+
 static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
 {
     int tag;
 
     if (track->mode == MODE_MP4 || track->mode == MODE_PSP)
-        tag = track->par->codec_tag;
-    else if (track->mode == MODE_ISM)
-        tag = track->par->codec_tag;
-    else if (track->mode == MODE_IPOD) {
-        if (!av_match_ext(s->filename, "m4a") &&
-            !av_match_ext(s->filename, "m4v") &&
-            !av_match_ext(s->filename, "m4b"))
-            av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v "
-                   "Quicktime/Ipod might not play the file\n");
-        tag = track->par->codec_tag;
-    } else if (track->mode & MODE_3GP)
-        tag = track->par->codec_tag;
+        tag = mp4_get_codec_tag(s, track);
+    else if (track->mode == MODE_ISM) {
+        tag = mp4_get_codec_tag(s, track);
+        if (!tag && track->par->codec_id == AV_CODEC_ID_WMAPRO)
+            tag = MKTAG('w', 'm', 'a', ' ');
+    } else if (track->mode == MODE_IPOD)
+        tag = ipod_get_codec_tag(s, track);
+    else if (track->mode & MODE_3GP)
+        tag = ff_codec_get_tag(codec_3gp_tags, track->par->codec_id);
     else if (track->mode == MODE_F4V)
-        tag = track->par->codec_tag;
+        tag = ff_codec_get_tag(codec_f4v_tags, track->par->codec_id);
     else
         tag = mov_get_codec_tag(s, track);
 
@@ -2060,6 +2126,19 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track)
     return update_size(pb, pos);
 }
 
+static int mov_write_camm_tag(AVIOContext *pb) {
+    int64_t size_update;
+    int64_t pos = avio_tell(pb);
+    avio_wb32(pb, 0); /* size */
+    ffio_wfourcc(pb, "camm");
+    avio_wb32(pb, 0); /* Reserved */
+    avio_wb16(pb, 0); /* Reserved */
+    avio_wb16(pb, 1); /* Data-reference index */
+
+    size_update = update_size(pb, pos);
+    return size_update;
+}
+
 static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
 {
     int64_t pos = avio_tell(pb);
@@ -2077,6 +2156,10 @@ static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext
         mov_write_rtp_tag(pb, track);
     else if (track->par->codec_tag == MKTAG('t','m','c','d'))
         mov_write_tmcd_tag(pb, track);
+    else if (track->par->codec_tag
+             == ff_codec_get_tag(ff_mp4_obj_type,
+                                 AV_CODEC_ID_CAMERA_MOTION_METADATA))
+        mov_write_camm_tag(pb);
     return update_size(pb, pos);
 }
 
@@ -2443,6 +2526,11 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
         } else if (track->par->codec_tag == MKTAG('t','m','c','d')) {
             hdlr_type = "tmcd";
             descr = "TimeCodeHandler";
+        } else if (track->par->codec_tag
+                       == ff_codec_get_tag(ff_mp4_obj_type,
+                            AV_CODEC_ID_CAMERA_MOTION_METADATA)) {
+            hdlr_type = "camm";
+            descr = "CameraMetadataMotionHandler";
         } else {
             av_log(s, AV_LOG_WARNING,
                    "Unknown hldr_type for %s, writing dummy values\n",
@@ -6422,73 +6510,6 @@ static int mov_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt)
     return ret;
 }
 
-static const AVCodecTag codec_3gp_tags[] = {
-    { AV_CODEC_ID_H263,     MKTAG('s','2','6','3') },
-    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },
-    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },
-    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },
-    { AV_CODEC_ID_AMR_NB,   MKTAG('s','a','m','r') },
-    { AV_CODEC_ID_AMR_WB,   MKTAG('s','a','w','b') },
-    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
-    { AV_CODEC_ID_NONE, 0 },
-};
-
-const AVCodecTag codec_mp4_tags[] = {
-    { AV_CODEC_ID_MPEG4       , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_H264        , MKTAG('a', 'v', 'c', '1') },
-    { AV_CODEC_ID_HEVC        , MKTAG('h', 'e', 'v', '1') },
-    { AV_CODEC_ID_HEVC        , MKTAG('h', 'v', 'c', '1') },
-    { AV_CODEC_ID_MPEG2VIDEO  , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_MPEG1VIDEO  , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_MJPEG       , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_PNG         , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_JPEG2000    , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_VC1         , MKTAG('v', 'c', '-', '1') },
-    { AV_CODEC_ID_DIRAC       , MKTAG('d', 'r', 'a', 'c') },
-    { AV_CODEC_ID_TSCC2       , MKTAG('m', 'p', '4', 'v') },
-    { AV_CODEC_ID_VP9         , MKTAG('v', 'p', '0', '9') },
-    { AV_CODEC_ID_AAC         , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_MP4ALS      , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_MP3         , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_MP2         , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_AC3         , MKTAG('a', 'c', '-', '3') },
-    { AV_CODEC_ID_EAC3        , MKTAG('e', 'c', '-', '3') },
-    { AV_CODEC_ID_DTS         , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_FLAC        , MKTAG('f', 'L', 'a', 'C') },
-    { AV_CODEC_ID_OPUS        , MKTAG('O', 'p', 'u', 's') },
-    { AV_CODEC_ID_VORBIS      , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_QCELP       , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_EVRC        , MKTAG('m', 'p', '4', 'a') },
-    { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') },
-    { AV_CODEC_ID_MOV_TEXT    , MKTAG('t', 'x', '3', 'g') },
-    { AV_CODEC_ID_NONE        ,    0 },
-};
-
-const AVCodecTag codec_ism_tags[] = {
-    { AV_CODEC_ID_WMAPRO      , MKTAG('w', 'm', 'a', ' ') },
-    { AV_CODEC_ID_NONE        ,    0 },
-};
-
-static const AVCodecTag codec_ipod_tags[] = {
-    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },
-    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },
-    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },
-    { AV_CODEC_ID_ALAC,     MKTAG('a','l','a','c') },
-    { AV_CODEC_ID_AC3,      MKTAG('a','c','-','3') },
-    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },
-    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },
-    { AV_CODEC_ID_NONE, 0 },
-};
-
-static const AVCodecTag codec_f4v_tags[] = {
-    { AV_CODEC_ID_MP3,    MKTAG('.','m','p','3') },
-    { AV_CODEC_ID_AAC,    MKTAG('m','p','4','a') },
-    { AV_CODEC_ID_H264,   MKTAG('a','v','c','1') },
-    { AV_CODEC_ID_VP6A,   MKTAG('V','P','6','A') },
-    { AV_CODEC_ID_VP6F,   MKTAG('V','P','6','F') },
-    { AV_CODEC_ID_NONE, 0 },
-};
-
 #if CONFIG_MOV_MUXER
 MOV_CLASS(mov)
 AVOutputFormat ff_mov_muxer = {
@@ -6549,7 +6570,7 @@ AVOutputFormat ff_mp4_muxer = {
     .write_trailer     = mov_write_trailer,
     .deinit            = mov_free,
     .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
-    .codec_tag         = (const AVCodecTag* const []){ codec_mp4_tags, 0 },
+    .codec_tag         = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
     .check_bitstream   = mov_check_bitstream,
     .priv_class        = &mp4_muxer_class,
 };
@@ -6570,7 +6591,7 @@ AVOutputFormat ff_psp_muxer = {
     .write_trailer     = mov_write_trailer,
     .deinit            = mov_free,
     .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
-    .codec_tag         = (const AVCodecTag* const []){ codec_mp4_tags, 0 },
+    .codec_tag         = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
     .check_bitstream   = mov_check_bitstream,
     .priv_class        = &psp_muxer_class,
 };
@@ -6632,8 +6653,7 @@ AVOutputFormat ff_ismv_muxer = {
     .write_trailer     = mov_write_trailer,
     .deinit            = mov_free,
     .flags             = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
-    .codec_tag         = (const AVCodecTag* const []){
-        codec_mp4_tags, codec_ism_tags, 0 },
+    .codec_tag         = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
     .check_bitstream   = mov_check_bitstream,
     .priv_class        = &ismv_muxer_class,
 };
-- 
2.13.2.725.g09c95d1e9-goog



More information about the ffmpeg-devel mailing list