qtmux: Make sure to write 64-bit STCO table when needed.
authorJan Schmidt <jan@centricular.com>
Thu, 29 Apr 2021 22:12:47 +0000 (08:12 +1000)
committerJan Schmidt <jan@centricular.com>
Thu, 29 Apr 2021 22:12:47 +0000 (08:12 +1000)
qtmux attempts to choose between writing a 32-bit stco chunk offset table
when it can, but switch to a 64-bit co64 table when file offsets go over
4GB.

This patch fixes a problem where the atom handling code was checking
mdat-relative offsets instead of the final file offset (computed by
adding the mdat position plus the mdat-relative offset) - leading to
problems where files with a size between 4GB and 4GB+offset-of-the-mdat
would write incorrect STCO tables with some samples having truncated
32-bit offsets.

Smaller files write STCO correctly, larger files would switch to
co64 and also output correctly.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/970>

gst/isomp4/atoms.c
gst/isomp4/atoms.h

index e439c0b..2935d5c 100644 (file)
@@ -850,6 +850,9 @@ atom_co64_init (AtomSTCO64 * co64)
   guint8 flags[3] = { 0, 0, 0 };
 
   atom_full_init (&co64->header, FOURCC_stco, 0, 0, 0, flags);
+
+  co64->chunk_offset = 0;
+  co64->max_offset = 0;
   atom_array_init (&co64->entries, 256);
 }
 
@@ -2422,7 +2425,17 @@ atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
 {
   guint64 original_offset = *offset;
   guint i;
-  gboolean trunc_to_32 = stco64->header.header.type == FOURCC_stco;
+
+  /* If any (mdat-relative) offset will by over 32-bits when converted to an
+   * absolute file offset then we need to write a 64-bit co64 atom, otherwise
+   * we can write a smaller stco 32-bit table */
+  gboolean write_stco64 =
+      (stco64->max_offset + stco64->chunk_offset) > G_MAXUINT32;
+
+  if (write_stco64)
+    stco64->header.header.type = FOURCC_co64;
+  else
+    stco64->header.header.type = FOURCC_stco;
 
   if (!atom_full_copy_data (&stco64->header, buffer, size, offset)) {
     return 0;
@@ -2438,10 +2451,10 @@ atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
     guint64 value =
         atom_array_index (&stco64->entries, i) + stco64->chunk_offset;
 
-    if (trunc_to_32) {
-      prop_copy_uint32 ((guint32) value, buffer, size, offset);
-    } else {
+    if (write_stco64) {
       prop_copy_uint64 (value, buffer, size, offset);
+    } else {
+      prop_copy_uint32 ((guint32) value, buffer, size, offset);
     }
   }
 
@@ -3171,8 +3184,8 @@ atom_stco64_add_entry (AtomSTCO64 * stco64, guint64 entry)
     return FALSE;
 
   atom_array_append (&stco64->entries, entry, 256);
-  if (entry > G_MAXUINT32)
-    stco64->header.header.type = FOURCC_co64;
+  if (entry > stco64->max_offset)
+    stco64->max_offset = entry;
 
   return TRUE;
 }
index 8f34048..33d1525 100644 (file)
@@ -548,13 +548,17 @@ typedef struct _AtomTREF
 
 /*
  * used for both STCO and CO64
- * if used as STCO, entries should be truncated to use only 32bits
+ * The table will be written out as STCO automatically when
+ * the offsets being written will fit in a 32-bit table,
+ * otherwise it is written as CO64
  */
 typedef struct _AtomSTCO64
 {
   AtomFull header;
   /* Global offset to add to entries when serialising */
   guint32 chunk_offset;
+  /* Maximum offset stored in the table */
+  guint64 max_offset;
   ATOM_ARRAY (guint64) entries;
 } AtomSTCO64;