#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>
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;
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,
}
}
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;
}
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;
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);
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)
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)
}
(*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);
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);
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;
}
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);
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;