]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/aviobuf.c
avformat/avio: Add Metacube support
[ffmpeg] / libavformat / aviobuf.c
index ddfa4ecbf1cc67aebebca055027a2ef4f0864574..dd7b58ff21fd8df35bbc7ae8437333bacb9e7b6f 100644 (file)
 #include "libavutil/log.h"
 #include "libavutil/opt.h"
 #include "libavutil/avassert.h"
+#include "libavutil/thread.h"
 #include "avformat.h"
 #include "avio.h"
 #include "avio_internal.h"
 #include "internal.h"
+#include "metacube2.h"
 #include "url.h"
 #include <stdarg.h>
 
@@ -105,6 +107,7 @@ int ffio_init_context(AVIOContext *s,
     s->seekable        = seek ? AVIO_SEEKABLE_NORMAL : 0;
     s->min_packet_size = 0;
     s->max_packet_size = 0;
+    s->metacube        = 0;
     s->update_checksum = NULL;
     s->short_seek_threshold = SHORT_SEEK_THRESHOLD;
 
@@ -174,10 +177,66 @@ static void writeout(AVIOContext *s, const uint8_t *data, int len)
     s->pos += len;
 }
 
+static AVOnce metacube2_crc_once_control = AV_ONCE_INIT;
+static AVCRC metacube2_crc_table[257];
+
+static void metacube2_crc_init_table_once(void)
+{
+    av_assert0(av_crc_init(metacube2_crc_table, 0, 16, 0x8fdb, sizeof(metacube2_crc_table)) >= 0);
+}
+
+static uint16_t metacube2_compute_crc(const struct metacube2_block_header *hdr)
+{
+    static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags);
+    const uint8_t *data = (uint8_t *)&hdr->size;
+    uint16_t crc;
+
+    ff_thread_once(&metacube2_crc_once_control, metacube2_crc_init_table_once);
+
+    // Metacube2 specifies a CRC start of 0x1234, but its pycrc-derived CRC
+    // includes a finalization step that is done somewhat differently in av_crc().
+    // 0x1234 alone sent through that finalization becomes 0x394a, and then we
+    // need a byte-swap of the CRC value (both on input and output) to account for
+    // differing conventions.
+    crc = av_crc(metacube2_crc_table, 0x4a39, data, data_len);
+    return av_bswap16(crc);
+}
+
+static void finalize_metacube_block_header(AVIOContext *s)
+{
+    struct metacube2_block_header hdr;
+    int len = s->buf_ptr_max - s->buffer;
+    int flags = 0;
+
+    if (s->current_type == AVIO_DATA_MARKER_SYNC_POINT)
+        s->seen_sync_point = 1;
+    else if (s->current_type == AVIO_DATA_MARKER_HEADER)
+        // NOTE: If there are multiple blocks marked METACUBE_FLAGS_HEADER,
+        // only the last one will count. This may become a problem if the
+        // mux flushes halfway through the stream header; if so, we would
+        // need to keep track of and concatenate the different parts.
+        flags |= METACUBE_FLAGS_HEADER;
+    else if (s->seen_sync_point)
+        flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START;
+
+    memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync));
+    AV_WB32(&hdr.size, len - sizeof(hdr));
+    AV_WB16(&hdr.flags, flags);
+    AV_WB16(&hdr.csum, metacube2_compute_crc(&hdr));
+    memcpy(s->buffer, &hdr, sizeof(hdr));
+}
+
 static void flush_buffer(AVIOContext *s)
 {
+    int buffer_empty;
     s->buf_ptr_max = FFMAX(s->buf_ptr, s->buf_ptr_max);
-    if (s->write_flag && s->buf_ptr_max > s->buffer) {
+    if (s->metacube)
+       buffer_empty = s->buf_ptr_max <= s->buffer + sizeof(struct metacube2_block_header);
+    else
+       buffer_empty = s->buf_ptr_max <= s->buffer;
+    if (s->write_flag && !buffer_empty) {
+        if (s->metacube)
+            finalize_metacube_block_header(s);
         writeout(s, s->buffer, s->buf_ptr_max - s->buffer);
         if (s->update_checksum) {
             s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,
@@ -186,6 +245,10 @@ static void flush_buffer(AVIOContext *s)
         }
     }
     s->buf_ptr = s->buf_ptr_max = s->buffer;
+
+    // Add space for Metacube header.
+    if (s->write_flag && s->metacube)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
     if (!s->write_flag)
         s->buf_end = s->buffer;
 }
@@ -214,7 +277,7 @@ void ffio_fill(AVIOContext *s, int b, int count)
 
 void avio_write(AVIOContext *s, const unsigned char *buf, int size)
 {
-    if (s->direct && !s->update_checksum) {
+    if (s->direct && !s->update_checksum && !s->metacube) {
         avio_flush(s);
         writeout(s, buf, size);
         return;
@@ -264,11 +327,17 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
 
     if (whence == SEEK_CUR) {
         offset1 = pos + (s->buf_ptr - s->buffer);
-        if (offset == 0)
-            return offset1;
+        if (offset == 0) {
+            if (s->metacube && s->write_flag)
+                return offset1 - sizeof(struct metacube2_block_header);
+            else
+                return offset1;
+        }
         if (offset > INT64_MAX - offset1)
             return AVERROR(EINVAL);
         offset += offset1;
+    } else if (s->metacube && s->write_flag) {
+        offset += sizeof(struct metacube2_block_header);
     }
     if (offset < 0)
         return AVERROR(EINVAL);
@@ -321,7 +390,10 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
         s->pos = offset;
     }
     s->eof_reached = 0;
-    return offset;
+    if (s->metacube && s->write_flag)
+        return offset - sizeof(struct metacube2_block_header);
+    else
+        return offset;
 }
 
 int64_t avio_skip(AVIOContext *s, int64_t offset)
@@ -473,7 +545,7 @@ void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType typ
             avio_flush(s);
         return;
     }
-    if (!s->write_data_type)
+    if (!s->write_data_type && !s->metacube)
         return;
     // If ignoring boundary points, just treat it as unknown
     if (type == AVIO_DATA_MARKER_BOUNDARY_POINT && s->ignore_boundary_point)
@@ -953,6 +1025,8 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
     }
     (*s)->short_seek_get = (int (*)(void *))ffurl_get_short_seek;
     (*s)->av_class = &ff_avio_class;
+    (*s)->metacube = h->flags & AVIO_FLAG_METACUBE;
+    (*s)->seen_sync_point = 0;
     return 0;
 fail:
     av_freep(&buffer);
@@ -1016,6 +1090,10 @@ int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size)
 int ffio_set_buf_size(AVIOContext *s, int buf_size)
 {
     uint8_t *buffer;
+
+    if (s->metacube)
+        buf_size += sizeof(struct metacube2_block_header);
+
     buffer = av_malloc(buf_size);
     if (!buffer)
         return AVERROR(ENOMEM);
@@ -1025,6 +1103,11 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size)
     s->orig_buffer_size =
     s->buffer_size = buf_size;
     s->buf_ptr = s->buf_ptr_max = buffer;
+
+    // Add space for Metacube header.
+    if (s->metacube)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
+
     url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
     return 0;
 }
@@ -1034,6 +1117,9 @@ int ffio_realloc_buf(AVIOContext *s, int buf_size)
     uint8_t *buffer;
     int data_size;
 
+    if (s->metacube && s->write_flag)
+        buf_size += sizeof(struct metacube2_block_header);
+
     if (!s->buffer_size)
         return ffio_set_buf_size(s, buf_size);
 
@@ -1052,6 +1138,11 @@ int ffio_realloc_buf(AVIOContext *s, int buf_size)
     s->orig_buffer_size = buf_size;
     s->buffer_size = buf_size;
     s->buf_ptr = s->write_flag ? (s->buffer + data_size) : s->buffer;
+
+    // Add space for Metacube header.
+    if (s->metacube && s->write_flag && data_size == 0)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
+
     if (s->write_flag)
         s->buf_ptr_max = s->buffer + data_size;