1 // A shared library that you can LD_PRELOAD into an FFmpeg-using process
2 // (most likely ffmpeg(1)) to make it output Metacube. This is obviously
3 // pretty hacky, since it needs to override various FFmpeg functions,
4 // so there are few guarantees here. It is written in C to avoid pulling
5 // in the C++ runtime into FFmpeg's C-only world.
7 // You should not link to this library. It does not have ABI stability.
8 // It is licensed the same as the rest of Cubemap.
13 #include <libavformat/avformat.h>
14 #include <libavformat/avio.h>
15 #include <libavutil/avassert.h>
16 #include <libavutil/crc.h>
17 #include <libavutil/error.h>
18 #include <libavutil/intreadwrite.h>
24 #include <sys/signal.h>
25 #include "metacube2.h"
27 static pthread_once_t metacube2_crc_once_control = PTHREAD_ONCE_INIT;
28 static AVCRC metacube2_crc_table[257];
30 // We need to store some extra information for each context,
31 // so this is where we do it. The “opaque” field in the AVIOContext
32 // points to this struct, but we can also look it up by the AVIOContext
33 // pointer by scanning through the singly linked list starting with
35 struct ContextExtraData {
36 struct ContextExtraData *next; // NULL for last entry.
37 AVIOContext *ctx; // The context we are associating data with.
40 int (*old_write_data_type)(void *opaque, uint8_t * buf,
42 enum AVIODataMarkerType type,
45 // Used during avformat_write_header(), to combine adjacent header blocks
46 // into one (in particular, the MP4 mux has an unneeded avio_flush()
47 // halfway throughout).
49 int64_t header_first_time;
50 uint8_t *buffered_header;
51 size_t buffered_header_bytes;
53 static struct ContextExtraData *first_extra_data = NULL;
55 // Look up ContextExtraData for the given context, creating a new one if needed.
56 static struct ContextExtraData *get_extra_data(AVIOContext * ctx)
58 for (struct ContextExtraData * ed = first_extra_data; ed != NULL;
64 struct ContextExtraData *ed = (struct ContextExtraData *)
65 malloc(sizeof(struct ContextExtraData));
67 ed->seen_sync_point = false;
68 ed->old_write_data_type = NULL;
69 ed->in_header = false;
70 ed->buffered_header = NULL;
71 ed->buffered_header_bytes = 0;
73 ed->next = first_extra_data;
74 first_extra_data = ed;
78 // Clear ContextExtraData for a given context (presumably before it's freed).
79 static void free_extra_data(AVIOContext * ctx)
81 if (first_extra_data == NULL) {
84 if (first_extra_data->ctx == ctx) {
85 struct ContextExtraData *to_free = first_extra_data;
86 first_extra_data = to_free->next;
90 for (struct ContextExtraData * ed = first_extra_data; ed != NULL;
92 if (ed->next != NULL && ed->next->ctx == ctx) {
93 struct ContextExtraData *to_free = ed->next;
94 ed->next = to_free->next;
101 static void metacube2_crc_init_table_once(void)
103 av_assert0(av_crc_init
104 (metacube2_crc_table, 0, 16, 0x8fdb,
105 sizeof(metacube2_crc_table)) >= 0);
108 static uint16_t metacube2_compute_crc_ff(const struct
109 metacube2_block_header *hdr)
111 static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags);
112 const uint8_t *data = (uint8_t *) & hdr->size;
115 pthread_once(&metacube2_crc_once_control,
116 metacube2_crc_init_table_once);
118 // Metacube2 specifies a CRC start of 0x1234, but its pycrc-derived CRC
119 // includes a finalization step that is done somewhat differently in av_crc().
120 // 0x1234 alone sent through that finalization becomes 0x394a, and then we
121 // need a byte-swap of the CRC value (both on input and output) to account for
122 // differing conventions.
123 crc = av_crc(metacube2_crc_table, 0x4a39, data, data_len);
124 return av_bswap16(crc);
127 static int write_packet(void *opaque, uint8_t * buf, int buf_size,
128 enum AVIODataMarkerType type, int64_t time)
131 return AVERROR(EINVAL);
134 struct ContextExtraData *ed = (struct ContextExtraData *) opaque;
137 if (ed->buffered_header_bytes == 0) {
138 ed->header_first_time = time;
141 size_t new_buffered_header_bytes =
142 ed->buffered_header_bytes + buf_size;
143 if (new_buffered_header_bytes < ed->buffered_header_bytes) {
144 return AVERROR(ENOMEM);
146 ed->buffered_header =
147 (uint8_t *) realloc(ed->buffered_header,
148 new_buffered_header_bytes);
149 if (ed->buffered_header == NULL) {
150 return AVERROR(ENOMEM);
153 memcpy(ed->buffered_header + ed->buffered_header_bytes,
155 ed->buffered_header_bytes = new_buffered_header_bytes;
158 // Find block size if we add a Metacube2 header in front.
159 unsigned new_buf_size =
160 (unsigned) buf_size + sizeof(struct metacube2_block_header);
161 if (new_buf_size < (unsigned) buf_size
162 || new_buf_size > (unsigned) INT_MAX) {
166 // Fill in the header.
167 struct metacube2_block_header hdr;
169 if (type == AVIO_DATA_MARKER_SYNC_POINT)
170 ed->seen_sync_point = 1;
171 else if (type == AVIO_DATA_MARKER_HEADER)
172 // NOTE: If there are multiple blocks marked METACUBE_FLAGS_HEADER,
173 // only the last one will count. This may become a problem if the
174 // mux flushes halfway through the stream header; if so, we would
175 // need to keep track of and concatenate the different parts.
176 flags |= METACUBE_FLAGS_HEADER;
177 else if (ed->seen_sync_point)
178 flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START;
180 memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync));
181 AV_WB32(&hdr.size, buf_size);
182 AV_WB16(&hdr.flags, flags);
183 AV_WB16(&hdr.csum, metacube2_compute_crc_ff(&hdr));
186 ed->ctx->opaque = ed->old_opaque;
187 if (new_buf_size < ed->ctx->max_packet_size) {
188 // Combine the two packets. (This is what we normally want.)
189 // So we allocate a new block, with a Metacube2 header in front.
190 uint8_t *buf_with_hdr = (uint8_t *) malloc(new_buf_size);
191 if (buf_with_hdr == NULL) {
192 return AVERROR(ENOMEM);
194 memcpy(buf_with_hdr, &hdr, sizeof(hdr));
195 memcpy(buf_with_hdr + sizeof(hdr), buf, buf_size);
196 if (ed->old_write_data_type) {
198 ed->old_write_data_type(ed->old_opaque,
204 ed->ctx->write_packet(ed->old_opaque,
211 && ret >= sizeof(struct metacube2_block_header)) {
212 ret -= sizeof(struct metacube2_block_header);
215 // Send separately. This will split a header block if it's really large,
216 // which we don't want, but that's how things are.
217 if (ed->old_write_data_type) {
219 ed->old_write_data_type(ed->old_opaque,
225 ed->ctx->write_packet(ed->old_opaque,
232 if (ret != sizeof(hdr)) {
236 if (ed->old_write_data_type) {
238 ed->old_write_data_type(ed->old_opaque, buf,
239 buf_size, type, time);
242 ed->ctx->write_packet(ed->old_opaque, buf,
247 ed->ctx->opaque = ed;
251 // Actual hooked functions below.
253 int avformat_write_header(AVFormatContext * ctx, AVDictionary ** options)
255 metacube2_crc_init_table_once();
257 struct ContextExtraData *ed = get_extra_data(ctx->pb);
258 ed->old_opaque = ctx->pb->opaque;
259 ed->old_write_data_type = ctx->pb->write_data_type;
260 ctx->pb->opaque = ed;
261 ctx->pb->write_data_type = write_packet;
262 ctx->pb->seek = NULL;
263 ctx->pb->seekable = 0;
264 if (ed->old_write_data_type == NULL) {
265 ctx->pb->ignore_boundary_point = 1;
268 int (*original_func)(AVFormatContext * ctx,
269 AVDictionary ** options);
270 original_func = dlsym(RTLD_NEXT, "avformat_write_header");
272 ed->in_header = true;
273 int ret = (*original_func) (ctx, options);
274 ed->in_header = false;
276 if (ed->buffered_header_bytes > 0) {
277 int hdr_ret = write_packet(ed, ed->buffered_header,
278 ed->buffered_header_bytes,
279 AVIO_DATA_MARKER_HEADER,
280 ed->header_first_time);
281 free(ed->buffered_header);
282 ed->buffered_header = NULL;
284 if (hdr_ret >= 0 && hdr_ret < ed->buffered_header_bytes) {
285 hdr_ret = AVERROR(EIO);
287 ed->buffered_header_bytes = 0;
296 void avformat_free_context(AVFormatContext * ctx)
301 free_extra_data(ctx->pb);
303 void (*original_func)(AVFormatContext * ctx);
304 original_func = dlsym(RTLD_NEXT, "avformat_free_context");
305 return (*original_func) (ctx);
308 // Hook so that we can restore opaque instead of ours being freed by the caller.
309 int avio_close(AVIOContext * ctx)
314 struct ContextExtraData ed = *get_extra_data(ctx);
315 free_extra_data(ctx);
316 ctx->opaque = ed.old_opaque;
318 int (*original_func)(AVIOContext * ctx);
319 original_func = dlsym(RTLD_NEXT, "avio_close");
320 return (*original_func) (ctx);
323 // Identical to FFmpeg's definition, but we cannot hook avio_close()
324 // when called from FFmpeg's avio_closep(), so we need to hook this one
326 int avio_closep(AVIOContext ** s)
328 int ret = avio_close(*s);
334 int avio_open2(AVIOContext ** s, const char *filename, int flags,
335 const AVIOInterruptCB * int_cb, AVDictionary ** options)
337 // The options, if any, are destroyed on entry, so we can add new ones
339 if (options && *options) {
340 AVDictionaryEntry *listen =
341 av_dict_get(*options, "listen", NULL,
343 if (listen != NULL && atoi(listen->value) != 0) {
344 // If -listen is set, we'll want to add a header, too.
345 av_dict_set(options, "headers",
346 "Content-encoding: metacube\r\n",
351 int (*original_func)(AVIOContext ** s, const char *filename,
352 int flags, const AVIOInterruptCB * int_cb,
353 AVDictionary ** options);
354 original_func = dlsym(RTLD_NEXT, "avio_open2");
355 return (*original_func) (s, filename, flags, int_cb, options);