qtmux: Write additional atoms for prores video
authorVivia Nikolaidou <vivia@ahiru.eu>
Mon, 11 Jul 2016 16:30:12 +0000 (19:30 +0300)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 1 Nov 2016 18:41:22 +0000 (20:41 +0200)
These required atoms are: colorimetry, field information, spatial/temporal
quality, and vendor.

https://bugzilla.gnome.org/show_bug.cgi?id=769048

gst/isomp4/atoms.c
gst/isomp4/atoms.h
gst/isomp4/fourcc.h
gst/isomp4/gstqtmux.c

index f53004e..338bae5 100644 (file)
@@ -3915,6 +3915,106 @@ build_pasp_extension (gint par_width, gint par_height)
       atom_data_free);
 }
 
+AtomInfo *
+build_fiel_extension_prores (const gchar * interlace_mode, gboolean tff)
+{
+  AtomData *atom_data = atom_data_new (FOURCC_fiel);
+  guint8 *data;
+  gint field_order;
+  gint interlace;
+
+  atom_data_alloc_mem (atom_data, 2);
+  data = atom_data->data;
+
+  if (!g_strcmp0 (interlace_mode, "progressive")) {
+    interlace = 1;
+    field_order = 0;
+  } else {
+    interlace = 2;
+    if (!g_strcmp0 (interlace_mode, "interleaved"))
+      field_order = tff ? 9 : 14;
+    else if (!g_strcmp0 (interlace_mode, "mixed"))
+      field_order = tff ? 1 : 6;
+    else
+      g_assert_not_reached ();
+  }
+
+  GST_WRITE_UINT8 (data, interlace);
+  GST_WRITE_UINT8 (data + 1, field_order);
+
+  return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
+      atom_data_free);
+}
+
+AtomInfo *
+build_colr_extension (GstVideoColorimetry colorimetry)
+{
+  AtomData *atom_data = atom_data_new (FOURCC_colr);
+  guint8 *data;
+  guint16 primaries;
+  guint16 transfer_function;
+  guint16 matrix;
+
+  switch (colorimetry.primaries) {
+    case GST_VIDEO_COLOR_PRIMARIES_BT709:
+      primaries = 1;
+      break;
+    case GST_VIDEO_COLOR_PRIMARIES_BT470BG:
+      primaries = 5;
+      break;
+    case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
+    case GST_VIDEO_COLOR_PRIMARIES_SMPTE240M:
+      primaries = 6;
+      break;
+    case GST_VIDEO_COLOR_PRIMARIES_UNKNOWN:
+    default:
+      primaries = 2;
+      break;
+  }
+
+  switch (colorimetry.transfer) {
+    case GST_VIDEO_TRANSFER_BT709:
+      transfer_function = 1;
+      break;
+    case GST_VIDEO_TRANSFER_SMPTE240M:
+      transfer_function = 7;
+      break;
+    case GST_VIDEO_TRANSFER_UNKNOWN:
+    default:
+      transfer_function = 2;
+      break;
+  }
+
+  switch (colorimetry.matrix) {
+    case GST_VIDEO_COLOR_MATRIX_BT709:
+      matrix = 1;
+      break;
+    case GST_VIDEO_COLOR_MATRIX_BT601:
+      matrix = 6;
+      break;
+    case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
+      matrix = 7;
+      break;
+    case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
+    default:
+      matrix = 2;
+      break;
+  }
+
+  atom_data_alloc_mem (atom_data, 10);
+  data = atom_data->data;
+
+  /* colour specification box */
+  GST_WRITE_UINT32_LE (data, FOURCC_nclc);
+
+  GST_WRITE_UINT16_BE (data + 4, primaries);
+  GST_WRITE_UINT16_BE (data + 6, transfer_function);    /* transfer function */
+  GST_WRITE_UINT16_BE (data + 8, matrix);
+
+  return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
+      atom_data_free);
+}
+
 SampleTableEntryMP4V *
 atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
     VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list)
@@ -3923,11 +4023,8 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
   guint dwidth, dheight;
   gint par_n = 0, par_d = 0;
 
-  if ((entry->par_n != 1 || entry->par_d != 1) &&
-      (entry->par_n != entry->par_d)) {
-    par_n = entry->par_n;
-    par_d = entry->par_d;
-  }
+  par_n = entry->par_n;
+  par_d = entry->par_d;
 
   dwidth = entry->width;
   dheight = entry->height;
@@ -3960,7 +4057,7 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
     ste->extension_atoms = g_list_concat (ste->extension_atoms, ext_atoms_list);
 
   /* QT spec has a pasp extension atom in stsd that can hold PAR */
-  if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_MOV)) {
+  if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
     ste->extension_atoms = g_list_append (ste->extension_atoms,
         build_pasp_extension (par_n, par_d));
   }
index 283d9cf..ba829e2 100644 (file)
@@ -1044,6 +1044,8 @@ AtomInfo *   build_jp2h_extension        (gint width, gint height, const gchar *
 
 AtomInfo *   build_jp2x_extension        (const GstBuffer * prefix);
 AtomInfo *   build_fiel_extension        (gint fields);
+AtomInfo *   build_fiel_extension_prores (const gchar * interlace_mode, gboolean tff);
+AtomInfo *   build_colr_extension        (GstVideoColorimetry colorimetry);
 AtomInfo *   build_ac3_extension         (guint8 fscod, guint8 bsid,
                                           guint8 bsmod, guint8 acmod,
                                           guint8 lfe_on, guint8 bitrate_code);
index 9e7408b..d7d9e8d 100644 (file)
@@ -169,6 +169,7 @@ G_BEGIN_DECLS
 #define FOURCC_mp4s     GST_MAKE_FOURCC('m','p','4','s')
 #define FOURCC_mp4v     GST_MAKE_FOURCC('m','p','4','v')
 #define FOURCC_name     GST_MAKE_FOURCC('n','a','m','e')
+#define FOURCC_nclc     GST_MAKE_FOURCC('n','c','l','c')
 #define FOURCC_opus     GST_MAKE_FOURCC('O','p','u','s')
 #define FOURCC_dops     GST_MAKE_FOURCC('d','O','p','s')
 #define FOURCC_pasp     GST_MAKE_FOURCC('p','a','s','p')
index 0847d4e..42e9533 100644 (file)
@@ -4246,8 +4246,14 @@ gst_qt_mux_video_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
     entry.fourcc = fourcc;
   } else if (strcmp (mimetype, "video/x-prores") == 0) {
     const gchar *variant;
+    const gchar *colorimetry_str;
+    const gchar *interlace_mode;
+    gboolean tff = TRUE;
+    GstVideoColorimetry colorimetry;
 
     variant = gst_structure_get_string (structure, "variant");
+    colorimetry_str = gst_structure_get_string (structure, "colorimetry");
+    interlace_mode = gst_structure_get_string (structure, "interlace-mode");
     if (!variant || !g_strcmp0 (variant, "standard"))
       entry.fourcc = FOURCC_apcn;
     else if (!g_strcmp0 (variant, "lt"))
@@ -4256,6 +4262,29 @@ gst_qt_mux_video_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
       entry.fourcc = FOURCC_apch;
     else if (!g_strcmp0 (variant, "proxy"))
       entry.fourcc = FOURCC_apco;
+    if (!g_strcmp0 (interlace_mode, "interleaved") ||
+        !g_strcmp0 (interlace_mode, "mixed")) {
+      /* Assume top-fields-first if unspecified */
+      gst_structure_get_boolean (structure, "top-field-first", &tff);
+    }
+
+    if (gst_video_colorimetry_from_string (&colorimetry, colorimetry_str)) {
+      ext_atom = build_colr_extension (colorimetry);
+      if (ext_atom)
+        ext_atom_list = g_list_append (ext_atom_list, ext_atom);
+    }
+
+    ext_atom = build_fiel_extension_prores (interlace_mode, tff);
+    if (ext_atom)
+      ext_atom_list = g_list_append (ext_atom_list, ext_atom);
+
+    if (colorimetry_str == NULL) {
+      /* TODO: Maybe implement better heuristics */
+      GST_WARNING_OBJECT (qtmux,
+          "Colorimetry information not found in caps. The resulting file's "
+          "color information might be wrong");
+      colorimetry_str = height < 720 ? "bt601" : "bt709";
+    }
   }
 
   if (!entry.fourcc)
@@ -4267,6 +4296,12 @@ gst_qt_mux_video_sink_set_caps (GstQTPad * qtpad, GstCaps * caps)
   qtpad->trak_ste =
       (SampleTableEntry *) atom_trak_set_video_type (qtpad->trak,
       qtmux->context, &entry, rate, ext_atom_list);
+  if (strcmp (mimetype, "video/x-prores") == 0) {
+    SampleTableEntryMP4V *mp4v = (SampleTableEntryMP4V *) qtpad->trak_ste;
+    mp4v->spatial_quality = 0x3FF;
+    mp4v->temporal_quality = 0;
+    mp4v->vendor = FOURCC_appl;
+  }
 
   gst_object_unref (qtmux);
   return TRUE;