mkv.c 77.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

20 21 22 23
#define bool_t ms_bool_t
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/rfc3984.h"
24
#include "mediastreamer2/msticker.h"
25
#include "../audiofilters/waveheader.h"
26
#include "mediastreamer2/formats.h"
27
#include "mkv_reader.h"
28 29 30 31 32 33 34 35 36 37 38 39
#undef bool_t

#define bool_t matroska_bool_t
#include <matroska/matroska.h>
#include <matroska/matroska_sem.h>
#undef bool_t

#define bool_t ambigous use ms_bool_t or matroska_bool_t

/*********************************************************************************************
 * Module interface                                                                          *
 *********************************************************************************************/
François Grisez's avatar
François Grisez committed
40 41
typedef void *(*ModuleNewFunc)();
typedef void (*ModuleFreeFunc)(void *obj);
42
typedef void (*ModuleSetFunc)(void *obj, const MSFmtDescriptor *fmt);
43
typedef void (*ModulePreProFunc)(void *obj, MSQueue *input, MSQueue *output);
44 45
typedef mblk_t *(*ModuleProFunc)(void *obj, mblk_t *buffer, ms_bool_t *isKeyFrame, uint8_t **codecPrivateData, size_t *codecPrivateSize);
typedef void (*ModuleReverseFunc)(void *obj, mblk_t *input, MSQueue *output, ms_bool_t isFirstFrame, const uint8_t *codecPrivateData, size_t codecPrivateSize);
46
typedef void (*ModulePrivateDataFunc)(const void *obj, uint8_t **data, size_t *data_size);
47
typedef void (*ModulePrivateDataLoadFunc)(void *obj, const uint8_t *data, size_t size);
48
typedef ms_bool_t (*ModuleIsKeyFrameFunc)(const mblk_t *frame);
49

François Grisez's avatar
François Grisez committed
50
typedef struct {
51 52
	const char *rfcName;
	const char *codecId;
François Grisez's avatar
François Grisez committed
53 54
	ModuleNewFunc new_module;
	ModuleFreeFunc free_module;
55 56 57
	ModuleSetFunc set;
	ModulePreProFunc preprocess;
	ModuleProFunc process;
58
	ModuleReverseFunc reverse;
59 60 61
	ModulePrivateDataFunc get_private_data;
	ModulePrivateDataLoadFunc load_private_data;
	ModuleIsKeyFrameFunc is_key_frame;
62 63 64 65 66 67
} ModuleDesc;

/*********************************************************************************************
 * h264 module                                                                               *
 *********************************************************************************************/
/* H264Private */
François Grisez's avatar
François Grisez committed
68
typedef struct {
69 70 71 72 73
	uint8_t profile;
	uint8_t level;
	uint8_t NALULenghtSizeMinusOne;
	MSList *sps_list;
	MSList *pps_list;
74 75
} H264Private;

76 77 78 79 80 81
static void _ms_list_append_copy(const mblk_t *buffer, MSList **list) {
	*list = ms_list_append(*list, copymsg(buffer));
}

static void H264Private_init(H264Private *obj, const MSList *spsList, const MSList *ppsList) {
	memset(obj, 0, sizeof(H264Private));
82
	obj->NALULenghtSizeMinusOne = 0xFF;
83 84 85 86 87 88 89
	ms_list_for_each2(spsList, (MSIterate2Func)_ms_list_append_copy, &obj->sps_list);
	ms_list_for_each2(ppsList, (MSIterate2Func)_ms_list_append_copy, &obj->pps_list);
	if(obj->sps_list != NULL) {
		const mblk_t *firstSPS = (const mblk_t *)ms_list_nth_data(obj->sps_list, 0);
		obj->profile = firstSPS->b_rptr[1];
		obj->level = firstSPS->b_rptr[3];
	}
90 91
}

92 93 94
static void H264Private_uninit(H264Private *obj) {
	if(obj->sps_list != NULL) ms_list_free_with_data(obj->sps_list,(void (*)(void *))freemsg);
	if(obj->pps_list != NULL) ms_list_free_with_data(obj->pps_list,(void (*)(void *))freemsg);
95 96
}

97
static H264Private *H264Private_new(const MSList *spsList, const MSList *ppsList) {
98
	H264Private *obj = (H264Private *)ms_new0(H264Private, 1);
99 100
	H264Private_init(obj, spsList, ppsList);
	return obj;
101 102
}

103 104 105
static void H264Private_free(H264Private *obj) {
	H264Private_uninit(obj);
	ms_free(obj);
106 107
}

108 109
static inline const MSList *H264Private_getSPS(const H264Private *obj) {
	return obj->sps_list;
110 111
}

112 113
static inline const MSList *H264Private_getPPS(const H264Private *obj) {
	return obj->pps_list;
114 115
}

François Grisez's avatar
François Grisez committed
116
static void H264Private_serialize(const H264Private *obj, uint8_t **data, size_t *size) {
117 118 119 120
	uint8_t nbSPS, nbPPS;
	MSList *it = NULL;
	uint8_t *result = NULL;
	int i;
121

122 123
	nbSPS = ms_list_size(obj->sps_list);
	nbPPS = ms_list_size(obj->pps_list);
124

125
	*size = 7;
126 127
	*size += (nbSPS + nbPPS) * 2;

François Grisez's avatar
François Grisez committed
128
	for(it=obj->sps_list;it!=NULL;it=it->next) {
129 130 131
		mblk_t *buff = (mblk_t*)(it->data);
		*size += msgdsize(buff);
	}
François Grisez's avatar
François Grisez committed
132
	for(it=obj->pps_list;it!=NULL;it=it->next) {
133 134 135 136
		mblk_t *buff = (mblk_t*)(it->data);
		*size += msgdsize(buff);
	}

137
	result = (uint8_t *)ms_new0(uint8_t, *size);
138 139 140 141 142 143
	result[0] = 0x01;
	result[1] = obj->profile;
	result[3] = obj->level;
	result[4] = obj->NALULenghtSizeMinusOne;
	result[5] = nbSPS & 0x1F;

144
	i=6;
François Grisez's avatar
François Grisez committed
145
	for(it=obj->sps_list; it!=NULL; it=it->next) {
146 147 148 149 150 151 152 153 154 155 156 157
		mblk_t *buff = (mblk_t*)(it->data);
		size_t buff_size = msgdsize(buff);
		uint16_t buff_size_be = htons(buff_size);
		memcpy(&result[i], &buff_size_be, sizeof(buff_size_be));
		i+=sizeof(buff_size_be);
		memcpy(&result[i], buff->b_rptr, buff_size);
		i += buff_size;
	}

	result[i] = nbPPS;
	i++;

François Grisez's avatar
François Grisez committed
158
	for(it=obj->pps_list; it!=NULL; it=it->next) {
159 160 161 162 163 164 165 166 167
		mblk_t *buff = (mblk_t*)(it->data);
		int buff_size = msgdsize(buff);
		uint16_t buff_size_be = htons(buff_size);
		memcpy(&result[i], &buff_size_be, sizeof(buff_size_be));
		i+=sizeof(buff_size_be);
		memcpy(&result[i], buff->b_rptr, buff_size);
		i += buff_size;
	}
	*data = result;
168 169
}

François Grisez's avatar
François Grisez committed
170
static void H264Private_load(H264Private *obj, const uint8_t *data) {
171 172 173 174
	int i, N;
	const uint8_t *r_ptr = NULL;
	uint16_t nalu_size;
	mblk_t *nalu = NULL;
175

176 177
	H264Private_uninit(obj);
	H264Private_init(obj, NULL, NULL);
178

179 180 181
	N = data[5] & 0x1F;
	r_ptr = data + 6;
	for(i=0;i<N;i++) {
182 183
		memcpy(&nalu_size, r_ptr, sizeof(uint16_t)); r_ptr += sizeof(uint16_t);
		nalu_size = ntohs(nalu_size);
184
		nalu = allocb(nalu_size, 0);
185 186 187 188
		memcpy(nalu->b_wptr, r_ptr, nalu_size); nalu->b_wptr += nalu_size; r_ptr += nalu_size;
		obj->sps_list = ms_list_append(obj->sps_list, nalu);
	}

189 190
	N = *r_ptr; r_ptr += 1;
	for(i=0;i<N;i++) {
191 192
		memcpy(&nalu_size, r_ptr, sizeof(uint16_t)); r_ptr += sizeof(uint16_t);
		nalu_size = ntohs(nalu_size);
193
		nalu = allocb(nalu_size, 0);
194 195 196
		memcpy(nalu->b_wptr, r_ptr, nalu_size); nalu->b_wptr += nalu_size; r_ptr += nalu_size;
		obj->pps_list = ms_list_append(obj->pps_list, nalu);
	}
197

198 199 200 201 202
	if(obj->sps_list != NULL) {
		const mblk_t *firstSPS = (const mblk_t *)ms_list_nth_data(obj->sps_list, 0);
		obj->profile = firstSPS->b_rptr[1];
		obj->level = firstSPS->b_rptr[3];
	}
203 204 205
}

/* h264 module */
206
typedef struct {
207
	Rfc3984Context rfc3984Context;
208
	H264Private *codecPrivate;
209 210
} H264Module;

211 212
static void *h264_module_new() {
	H264Module *mod = ms_new0(H264Module, 1);
213
	rfc3984_init(&mod->rfc3984Context);
214
	rfc3984_set_mode(&mod->rfc3984Context,1);
François Grisez's avatar
François Grisez committed
215
	return mod;
216 217
}

218
static void h264_module_free(void *data) {
219 220
	H264Module *obj = (H264Module *)data;
	rfc3984_uninit(&obj->rfc3984Context);
221 222 223
	if(obj->codecPrivate != NULL) {
		H264Private_free(obj->codecPrivate);
	}
224
	ms_free(obj);
225 226
}

227
static void h264_module_preprocessing(void *data, MSQueue *input, MSQueue *output) {
228 229
	H264Module *obj = (H264Module *)data;
	MSQueue queue;
230
	mblk_t *inputBuffer = NULL;
231 232

	ms_queue_init(&queue);
233
	while((inputBuffer = ms_queue_get(input)) != NULL) {
234
		rfc3984_unpack(&obj->rfc3984Context, inputBuffer, &queue);
235
		if(!ms_queue_empty(&queue)) {
236 237
			mblk_t *frame = ms_queue_get(&queue);
			mblk_t *end = frame;
238
			while(!ms_queue_empty(&queue)) {
239 240 241 242 243
				end = concatb(end, ms_queue_get(&queue));
			}
			ms_queue_put(output, frame);
		}
	}
244 245
}

246
static inline int h264_nalu_type(const mblk_t *nalu) {
247
	return (nalu->b_rptr[0]) & ((1<<5)-1);
248 249
}

250
static ms_bool_t h264_is_key_frame(const mblk_t *frame) {
251
	const mblk_t *curNalu = NULL;
252 253
	for(curNalu = frame; curNalu != NULL && h264_nalu_type(curNalu) != 5; curNalu = curNalu->b_cont);
	return curNalu != NULL;
254 255
}

256 257
static void nalus_to_frame(mblk_t *buffer, mblk_t **frame, MSList **spsList, MSList **ppsList, ms_bool_t *isKeyFrame) {
	mblk_t *curNalu = NULL;
258
	uint32_t timecode = mblk_get_timestamp_info(buffer);
259 260
	*frame = NULL;
	*isKeyFrame = FALSE;
261
	
262

263
	for(curNalu = buffer; curNalu != NULL;) {
264
		mblk_t *buff = curNalu;
265 266
		int type = h264_nalu_type(buff);
		
267 268
		curNalu = curNalu->b_cont;
		buff->b_cont = NULL;
269
		switch(type) {
270
		case 7:
271
			*spsList = ms_list_append(*spsList, buff);
272 273 274
			break;

		case 8:
275
			*ppsList = ms_list_append(*ppsList, buff);
276 277 278
			break;

		default:
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
			{
				uint32_t bufferSize = htonl(msgdsize(buff));
				mblk_t *size = allocb(4, 0);
				
				if(type == 5) {
					*isKeyFrame = TRUE;
				}
				memcpy(size->b_wptr, &bufferSize, sizeof(bufferSize));
				size->b_wptr = size->b_wptr + sizeof(bufferSize);
				concatb(size, buff);
				buff = size;

				if(*frame == NULL) {
					*frame = buff;
				} else {
					concatb(*frame, buff);
				}
296
			}
297 298 299
		}
	}

300
	if(*frame != NULL) {
301 302 303
		msgpullup(*frame, -1);
		mblk_set_timestamp_info(*frame, timecode);
	}
304 305
}

306
static mblk_t *h264_module_processing(void *data, mblk_t *nalus, ms_bool_t *isKeyFrame, uint8_t **codecPrivateData, size_t *codecPrivateSize) {
307
	H264Module *obj = (H264Module *)data;
308 309 310 311 312 313 314 315 316 317 318 319 320
	mblk_t *frame = NULL;
	MSList *spsList = NULL, *ppsList = NULL;
	nalus_to_frame(nalus, &frame, &spsList, &ppsList, isKeyFrame);
	if(spsList != NULL || ppsList != NULL) {
		H264Private *codecPrivateStruct = H264Private_new(spsList, ppsList);
		if(obj->codecPrivate == NULL) {
			obj->codecPrivate = codecPrivateStruct;
		} else {
			H264Private_serialize(codecPrivateStruct, codecPrivateData, codecPrivateSize);
			H264Private_free(codecPrivateStruct);
		}
		if(spsList != NULL) ms_list_free_with_data(spsList, (void (*)(void *))freemsg);
		if(ppsList != NULL) ms_list_free_with_data(ppsList, (void (*)(void *))freemsg);
321 322
	}
	return frame;
323 324
}

325 326
static void h264_module_reverse(void *data, mblk_t *input, MSQueue *output, ms_bool_t isFirstFrame, const uint8_t *codecPrivateData, size_t codecPrivateSize) {
	mblk_t *buffer = NULL, *bufferFrag = NULL;
327 328
	H264Module *obj = (H264Module *)data;
	MSQueue queue;
329 330 331
	const MSList *it = NULL;
	H264Private *codecPrivate = NULL, *selectedCodecPrivate = NULL;

332 333 334 335 336 337 338 339 340 341 342 343 344 345
	ms_queue_init(&queue);
	while(input->b_rptr != input->b_wptr) {
		uint32_t naluSize;
		mblk_t *nalu;
		memcpy(&naluSize, input->b_rptr, sizeof(uint32_t)); input->b_rptr += sizeof(uint32_t);
		naluSize = ntohl(naluSize);
		nalu = allocb(naluSize, 0);
		memcpy(nalu->b_wptr, input->b_rptr, naluSize); nalu->b_wptr += naluSize; input->b_rptr += naluSize;
		if(buffer == NULL) {
			buffer = nalu;
		} else {
			concatb(buffer, nalu);
		}
	}
346 347 348 349 350 351 352 353 354 355 356
	if(isFirstFrame) {
		selectedCodecPrivate = obj->codecPrivate;
	} else if(codecPrivateData != NULL) {
		codecPrivate = H264Private_new(NULL, NULL);
		H264Private_load(codecPrivate, codecPrivateData);
		selectedCodecPrivate = codecPrivate;
	}
	if(selectedCodecPrivate != NULL) {
		for(it = H264Private_getSPS(selectedCodecPrivate); it != NULL; it = it->next) {
			mblk_t *sps = copymsg((mblk_t *)it->data);
			ms_queue_put(&queue, sps);
357
			ms_message("MKVPlayer: send SPS");
358 359 360 361
		}
		for(it = H264Private_getPPS(selectedCodecPrivate); it != NULL; it = it->next) {
			mblk_t *pps = copymsg((mblk_t *)it->data);
			ms_queue_put(&queue, pps);
362
			ms_message("MKVPlayer: send PPS");
363
		}
364
	}
365 366 367

	if(codecPrivate != NULL) H264Private_free(codecPrivate);

368 369 370 371 372 373 374
	for(bufferFrag = buffer; bufferFrag != NULL;) {
		mblk_t *curBuff = bufferFrag;
		bufferFrag = bufferFrag->b_cont;
		curBuff->b_cont = NULL;
		ms_queue_put(&queue, curBuff);
	}
	rfc3984_pack(&obj->rfc3984Context, &queue, output, mblk_get_timestamp_info(input));
375
	freemsg(input);
376 377 378
}

static void h264_module_get_private_data(const void *o, uint8_t **data, size_t *data_size) {
379
	const H264Module *obj = (const H264Module *)o;
380
	H264Private_serialize(obj->codecPrivate, data, data_size);
381 382
}

383
static void h264_module_load_private_data(void *o, const uint8_t *data, size_t size) {
384
	H264Module *obj = (H264Module *)o;
385 386
	obj->codecPrivate = H264Private_new(NULL, NULL);
	H264Private_load(obj->codecPrivate, data);
387 388
}

389
/* h264 module description */
390
#ifdef _MSC_VER
391
static const ModuleDesc h264_module_desc = {
392 393
	"H264",
	"V_MPEG4/ISO/AVC",
François Grisez's avatar
François Grisez committed
394 395
	h264_module_new,
	h264_module_free,
396 397 398
	NULL,
	h264_module_preprocessing,
	h264_module_processing,
399
	h264_module_reverse,
400 401 402
	h264_module_get_private_data,
	h264_module_load_private_data,
	h264_is_key_frame
403
};
404
#else
405
static const ModuleDesc h264_module_desc = {
406 407 408 409 410 411 412 413 414 415 416 417 418
	.rfcName = "H264",
	.codecId = "V_MPEG4/ISO/AVC",
	.new_module = h264_module_new,
	.free_module = h264_module_free,
	.set = NULL,
	.preprocess = h264_module_preprocessing,
	.process = h264_module_processing,
	.reverse = h264_module_reverse,
	.get_private_data = h264_module_get_private_data,
	.load_private_data = h264_module_load_private_data,
	.is_key_frame = h264_is_key_frame
};
#endif
419 420 421 422 423

/*********************************************************************************************
 * µLaw module                                                                               *
 *********************************************************************************************/
/* WavPrivate */
François Grisez's avatar
François Grisez committed
424
typedef struct {
425 426 427 428 429 430 431
	uint16_t wFormatTag;
	uint16_t nbChannels;
	uint32_t nSamplesPerSec;
	uint32_t nAvgBytesPerSec;
	uint16_t nBlockAlign;
	uint16_t wBitsPerSample;
	uint16_t cbSize;
432 433
} WavPrivate;

François Grisez's avatar
François Grisez committed
434
static void wav_private_set(WavPrivate *data, const MSFmtDescriptor *obj) {
435 436 437
	uint16_t bitsPerSample = 8;
	uint16_t nbBlockAlign = (bitsPerSample * obj->nchannels)/8;
	uint32_t bitrate = bitsPerSample * obj->nchannels * obj->rate;
438

439 440 441 442 443 444 445
	data->wFormatTag = le_uint16((uint16_t)7);
	data->nbChannels = le_uint16((uint16_t)obj->nchannels);
	data->nSamplesPerSec = le_uint32((uint32_t)obj->rate);
	data->nAvgBytesPerSec = le_uint32(bitrate);
	data->nBlockAlign = le_uint16((uint16_t)nbBlockAlign);
	data->wBitsPerSample = le_uint16(bitsPerSample);
	data->cbSize = 0;
446 447
}

François Grisez's avatar
François Grisez committed
448
static void wav_private_serialize(const WavPrivate *obj, uint8_t **data, size_t *size) {
François Grisez's avatar
François Grisez committed
449
	*size = sizeof(WavPrivate);
450 451
	*data = (uint8_t *)ms_new0(uint8_t, *size);
	memcpy(*data, obj, *size);
452 453
}

François Grisez's avatar
François Grisez committed
454
static inline void wav_private_load(WavPrivate *obj, const uint8_t *data) {
François Grisez's avatar
François Grisez committed
455
	memcpy(obj, data, sizeof(WavPrivate));
456 457 458
}

/* µLaw module */
François Grisez's avatar
François Grisez committed
459
static void *mu_law_module_new() {
460
	return ms_new0(WavPrivate, 1);
461 462
}

François Grisez's avatar
François Grisez committed
463
static void mu_law_module_free(void *o) {
464
	ms_free(o);
465 466
}

467
static void mu_law_module_set(void *o, const MSFmtDescriptor *fmt) {
François Grisez's avatar
François Grisez committed
468
	WavPrivate *obj = (WavPrivate *)o;
469
	wav_private_set(obj, fmt);
470 471
}

François Grisez's avatar
François Grisez committed
472
static void mu_law_module_get_private_data(const void *o, uint8_t **data, size_t *data_size) {
François Grisez's avatar
François Grisez committed
473 474
	const WavPrivate *obj = (const WavPrivate *)o;
	wav_private_serialize(obj, data, data_size);
475 476
}

477
static void mu_law_module_load_private(void *o, const uint8_t *data, size_t size) {
François Grisez's avatar
François Grisez committed
478 479
	WavPrivate *obj = (WavPrivate *)o;
	wav_private_load(obj, data);
480 481
}

482
/* µLaw module description */
483
#ifdef _MSC_VER
484
static const ModuleDesc mu_law_module_desc = {
485 486
	"pcmu",
	"A_MS/ACM",
François Grisez's avatar
François Grisez committed
487 488
	mu_law_module_new,
	mu_law_module_free,
489 490 491
	mu_law_module_set,
	NULL,
	NULL,
492
	NULL,
493 494 495
	mu_law_module_get_private_data,
	mu_law_module_load_private,
	NULL
496
};
497
#else
498
static const ModuleDesc mu_law_module_desc = {
499 500 501 502 503 504 505 506 507 508 509 510 511
	.rfcName = "pcmu",
	.codecId = "A_MS/ACM",
	.new_module = mu_law_module_new,
	.free_module = mu_law_module_free,
	.set = mu_law_module_set,
	.preprocess = NULL,
	.process = NULL,
	.reverse = NULL,
	.get_private_data = mu_law_module_get_private_data,
	.load_private_data = mu_law_module_load_private,
	.is_key_frame = NULL
};
#endif
512

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
/*********************************************************************************************
 * Opus module                                                                               *
 *********************************************************************************************/
// OpusCodecPrivate
typedef struct {
	uint8_t version;
	uint8_t channelCount;
	uint16_t preSkip;
	uint32_t inputSampleRate;
	uint16_t outputGain;
	uint8_t mappingFamily;
} OpusCodecPrivate;

static void opus_codec_private_init(OpusCodecPrivate *obj) {
	memset(obj, 0, sizeof(OpusCodecPrivate));
	obj->version = 1;
	obj->preSkip = le_uint16(3840); // 80ms at 48kHz
	obj->outputGain = le_uint16(0);
}

static void opus_codec_private_set(OpusCodecPrivate *obj, int nChannels, int inputSampleRate) {
	obj->channelCount = nChannels;
	obj->inputSampleRate = le_uint32((uint32_t)inputSampleRate);
}

static void opus_codec_private_serialize(const OpusCodecPrivate *obj, uint8_t **data, size_t *size) {
	const char signature[9] = "OpusHead";
	*size = 19;
	*data = ms_new0(uint8_t, *size);
	memcpy(*data, signature, 8);
	memcpy((*data)+8, obj, 11);
}

static inline void opus_codec_private_load(OpusCodecPrivate *obj, const uint8_t *data, size_t size) {
	memcpy(obj, data+8, 11);
}

// OpusModule
static void *opus_module_new() {
552
	OpusCodecPrivate *obj = ms_new0(OpusCodecPrivate, 1);
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	opus_codec_private_init(obj);
	return obj;
}

static void opus_module_free(void *o) {
	OpusCodecPrivate *obj = (OpusCodecPrivate *)o;
	ms_free(obj);
}

static void opus_module_set(void *o, const MSFmtDescriptor *fmt) {
	OpusCodecPrivate *obj = (OpusCodecPrivate *)o;
	opus_codec_private_set(obj, fmt->nchannels, 0);
}

static void opus_module_get_private_data(const void *o, uint8_t **data, size_t *dataSize) {
	const OpusCodecPrivate *obj = (const OpusCodecPrivate *)o;
	opus_codec_private_serialize(obj, data, dataSize);
}

static void opus_module_load_private_data(void *o, const uint8_t *data, size_t size) {
	OpusCodecPrivate *obj = (OpusCodecPrivate *)o;
	opus_codec_private_load(obj, data, size);
}

#ifdef _MSC_VER
static const ModuleDesc opus_module_desc = {
	"opus",
	"A_OPUS",
	opus_module_new,
	opus_module_free,
	opus_module_set,
	NULL,
	NULL,
	NULL,
	opus_module_get_private_data,
	opus_module_load_private_data,
	NULL
};
#else
static const ModuleDesc opus_module_desc = {
	.rfcName = "opus",
	.codecId = "A_OPUS",
	.new_module = opus_module_new,
	.free_module = opus_module_free,
	.set = opus_module_set,
	.preprocess = NULL,
	.process = NULL,
	.reverse = NULL,
	.get_private_data = opus_module_get_private_data,
	.load_private_data = opus_module_load_private_data,
	.is_key_frame = NULL
};
#endif

607
/*********************************************************************************************
François Grisez's avatar
François Grisez committed
608
 * Modules list                                                                              *
609
 *********************************************************************************************/
François Grisez's avatar
François Grisez committed
610
typedef enum {
611
	H264_MOD_ID,
François Grisez's avatar
François Grisez committed
612
	MU_LAW_MOD_ID,
613
	OPUS_MOD_ID,
François Grisez's avatar
François Grisez committed
614
	NONE_ID
615 616
} ModuleId;

617
static const ModuleDesc const *moduleDescs[] = {
618 619
	&h264_module_desc,
	&mu_law_module_desc,
620
	&opus_module_desc,
621
	NULL
622 623
};

François Grisez's avatar
François Grisez committed
624
static int find_module_id_from_rfc_name(const char *rfcName) {
625 626 627
	int id;
	for(id=0; moduleDescs[id] != NULL && strcmp(moduleDescs[id]->rfcName, rfcName) != 0; id++);
	return id;
628 629
}

François Grisez's avatar
François Grisez committed
630
static int find_module_id_from_codec_id(const char *codecId) {
631 632 633
	int id;
	for(id=0; moduleDescs[id] != NULL && strcmp(moduleDescs[id]->codecId, codecId) != 0; id++);
	return id;
634 635
}

636 637 638 639 640 641 642 643 644
static const char *codec_id_to_rfc_name(const char *codecId) {
	ModuleId id = find_module_id_from_codec_id(codecId);
	if(id == NONE_ID) {
		return NULL;
	} else {
		return moduleDescs[id]->rfcName;
	}
}

François Grisez's avatar
François Grisez committed
645 646 647
/*********************************************************************************************
 * Module                                                                                    *
 *********************************************************************************************/
648
typedef struct {
François Grisez's avatar
François Grisez committed
649 650 651 652
	ModuleId id;
	void *data;
} Module;

653
static Module *module_new(const char *rfcName) {
654
	ModuleId id = find_module_id_from_rfc_name(rfcName);
655
	if(id == NONE_ID) {
François Grisez's avatar
François Grisez committed
656
		return NULL;
657 658
	} else {
		Module *module = (Module *)ms_new0(Module, 1);
François Grisez's avatar
François Grisez committed
659 660 661 662 663 664
		module->id = id;
		module->data = moduleDescs[module->id]->new_module();
		return module;
	}
}

665
static void module_free(Module *module) {
François Grisez's avatar
François Grisez committed
666 667 668 669
	moduleDescs[module->id]->free_module(module->data);
	ms_free(module);
}

670 671
static void module_set(Module *module, const MSFmtDescriptor *format) {
	if(moduleDescs[module->id]->set != NULL) {
François Grisez's avatar
François Grisez committed
672
		moduleDescs[module->id]->set(module->data, format);
673
	}
François Grisez's avatar
François Grisez committed
674 675
}

676 677
static void module_preprocess(Module *module, MSQueue *input, MSQueue *output) {
	if(moduleDescs[module->id]->preprocess != NULL) {
François Grisez's avatar
François Grisez committed
678
		moduleDescs[module->id]->preprocess(module->data, input, output);
679
	} else {
François Grisez's avatar
François Grisez committed
680
		mblk_t *buffer;
681
		while((buffer = ms_queue_get(input)) != NULL) {
François Grisez's avatar
François Grisez committed
682 683 684 685 686
			ms_queue_put(output, buffer);
		}
	}
}

687
static mblk_t *module_process(Module *module, mblk_t *buffer, ms_bool_t *isKeyFrame, uint8_t **codecPrivateData, size_t *codecPrivateSize) {
François Grisez's avatar
François Grisez committed
688
	mblk_t *frame;
689
	if(moduleDescs[module->id]->process != NULL) {
690
		frame = moduleDescs[module->id]->process(module->data, buffer, isKeyFrame, codecPrivateData, codecPrivateSize);
691
	} else {
François Grisez's avatar
François Grisez committed
692 693
		frame = buffer;
		*isKeyFrame = TRUE;
694
		*codecPrivateData = NULL;
François Grisez's avatar
François Grisez committed
695 696 697 698
	}
	return frame;
}

699
static void module_reverse(Module *module, mblk_t *input, MSQueue *output, ms_bool_t isFirstFrame, const uint8_t *codecPrivateData, size_t codecPrivateSize) {
700 701 702
	if(moduleDescs[module->id]->reverse == NULL) {
		ms_queue_put(output, input);
	} else {
703
		moduleDescs[module->id]->reverse(module->data, input, output, isFirstFrame, codecPrivateData, codecPrivateSize);
704 705 706 707
	}
}

static inline void module_get_private_data(const Module *module, uint8_t **data, size_t *dataSize) {
François Grisez's avatar
François Grisez committed
708 709 710
	moduleDescs[module->id]->get_private_data(module->data, data, dataSize);
}

711 712
static inline void module_load_private_data(Module *module, const uint8_t *data, size_t size) {
	moduleDescs[module->id]->load_private_data(module->data, data, size);
François Grisez's avatar
François Grisez committed
713 714
}

715
static inline ms_bool_t module_is_key_frame(const Module *module, const mblk_t *frame) {
François Grisez's avatar
François Grisez committed
716 717 718
	return moduleDescs[module->id]->is_key_frame(frame);
}

719
static inline const char *module_get_codec_id(const Module *module) {
François Grisez's avatar
François Grisez committed
720 721 722
	return moduleDescs[module->id]->codecId;
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
/*********************************************************************************************
 * Matroska                                                                                  *
 *********************************************************************************************/
#define WRITE_DEFAULT_ELEMENT TRUE

static const timecode_t MKV_TIMECODE_SCALE = 1000000;
static const int MKV_DOCTYPE_VERSION = 2;
static const int MKV_DOCTYPE_READ_VERSION = 2;

extern const nodemeta LangStr_Class[];
extern const nodemeta UrlPart_Class[];
extern const nodemeta BufStream_Class[];
extern const nodemeta MemStream_Class[];
extern const nodemeta Streams_Class[];
extern const nodemeta File_Class[];
extern const nodemeta Stdio_Class[];
extern const nodemeta Matroska_Class[];
extern const nodemeta EBMLElement_Class[];
extern const nodemeta EBMLMaster_Class[];
extern const nodemeta EBMLBinary_Class[];
extern const nodemeta EBMLString_Class[];
extern const nodemeta EBMLInteger_Class[];
extern const nodemeta EBMLCRC_Class[];
extern const nodemeta EBMLDate_Class[];
extern const nodemeta EBMLVoid_Class[];

François Grisez's avatar
François Grisez committed
749
static void loadModules(nodemodule *modules) {
750 751 752 753 754 755 756 757 758 759 760
	NodeRegisterClassEx(modules, Streams_Class);
	NodeRegisterClassEx(modules, File_Class);
	NodeRegisterClassEx(modules, Matroska_Class);
	NodeRegisterClassEx(modules, EBMLElement_Class);
	NodeRegisterClassEx(modules, EBMLMaster_Class);
	NodeRegisterClassEx(modules, EBMLBinary_Class);
	NodeRegisterClassEx(modules, EBMLString_Class);
	NodeRegisterClassEx(modules, EBMLInteger_Class);
	NodeRegisterClassEx(modules, EBMLCRC_Class);
	NodeRegisterClassEx(modules, EBMLDate_Class);
	NodeRegisterClassEx(modules, EBMLVoid_Class);
761 762
}

763 764 765 766 767 768
typedef enum {
	MKV_OPEN_CREATE,
	MKV_OPEN_APPEND,
	MKV_OPEN_RO
} MatroskaOpenMode;

François Grisez's avatar
François Grisez committed
769
typedef struct {
770 771
	parsercontext *p;
	stream *output;
772
	ebml_master *header, *segment, *cluster, *info, *tracks, *metaSeek, *cues, *firstCluster, *currentCluster;
773
	matroska_seekpoint *infoMeta, *tracksMeta, *cuesMeta;
774
	matroska_block *currentBlock;
775 776 777
	timecode_t timecodeScale;
	filepos_t segmentInfoPosition;
	int nbClusters;
778 779
} Matroska;

François Grisez's avatar
François Grisez committed
780
static void matroska_init(Matroska *obj) {
781
	memset(obj, 0, sizeof(Matroska));
Simon Morlat's avatar
Simon Morlat committed
782
	obj->p = (parsercontext *)ms_new0(parsercontext, 1);
783 784 785
	ParserContext_Init(obj->p, NULL, NULL, NULL);
	loadModules((nodemodule*)obj->p);
	MATROSKA_Init((nodecontext*)obj->p);
786 787
	obj->segmentInfoPosition = -1;
	obj->timecodeScale = -1;
788 789
}

François Grisez's avatar
François Grisez committed
790
static void matroska_uninit(Matroska *obj) {
François Grisez's avatar
François Grisez committed
791 792 793
	MATROSKA_Done((nodecontext*)obj->p);
	ParserContext_Done(obj->p);
	ms_free(obj->p);
794 795
}

François Grisez's avatar
François Grisez committed
796
static int ebml_reading_profile(const ebml_master *head) {
797
	size_t length = EBML_ElementDataSize((ebml_element *)head, FALSE);
798
	char *docType = ms_new0(char, length);
799
	int profile;
800 801 802 803 804
	int docTypeReadVersion;
	
	EBML_StringGet((ebml_string *)EBML_MasterFindChild(head, &EBML_ContextDocType), docType, length);
	docTypeReadVersion = EBML_IntegerValue((ebml_integer *)EBML_MasterFindChild(head, &EBML_ContextDocTypeReadVersion));
	
805

806
	if (strcmp(docType, "matroska")==0) {
François Grisez's avatar
François Grisez committed
807
		switch (docTypeReadVersion) {
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
		case 1:
			profile = PROFILE_MATROSKA_V1;
			break;
		case 2:
			profile = PROFILE_MATROSKA_V2;
			break;
		case 3:
			profile = PROFILE_MATROSKA_V3;
			break;
		case 4:
			profile = PROFILE_MATROSKA_V4;
			break;
		default:
			profile = -1;
		}
François Grisez's avatar
François Grisez committed
823
	} else if(strcmp(docType, "webm")) {
824
		profile = PROFILE_WEBM;
François Grisez's avatar
François Grisez committed
825
	} else {
826 827 828 829 830
		profile = -1;
	}

	ms_free(docType);
	return profile;
831 832
}

François Grisez's avatar
François Grisez committed
833
static ms_bool_t matroska_create_file(Matroska *obj, const char path[]) {
834
	obj->header = (ebml_master *)EBML_ElementCreate(obj->p, &EBML_ContextHead, TRUE, NULL);
835 836 837 838 839 840 841 842 843
	obj->segment = (ebml_master *)EBML_ElementCreate(obj->p, &MATROSKA_ContextSegment, TRUE, NULL);
	obj->metaSeek = (ebml_master *)EBML_MasterAddElt(obj->segment, &MATROSKA_ContextSeekHead, FALSE);
	obj->infoMeta = (matroska_seekpoint *)EBML_MasterAddElt(obj->metaSeek, &MATROSKA_ContextSeek, TRUE);
	obj->tracksMeta = (matroska_seekpoint *)EBML_MasterAddElt(obj->metaSeek, &MATROSKA_ContextSeek, TRUE);
	obj->cuesMeta = (matroska_seekpoint *)EBML_MasterAddElt(obj->metaSeek, &MATROSKA_ContextSeek, TRUE);
	obj->info = (ebml_master *)EBML_MasterAddElt(obj->segment, &MATROSKA_ContextInfo, TRUE);
	obj->tracks = (ebml_master *)EBML_MasterAddElt(obj->segment, &MATROSKA_ContextTracks, FALSE);
	obj->cues = (ebml_master *)EBML_MasterAddElt(obj->segment, &MATROSKA_ContextCues, FALSE);
	obj->timecodeScale = MKV_TIMECODE_SCALE;
844

845 846 847
	MATROSKA_LinkMetaSeekElement(obj->infoMeta, (ebml_element *)obj->info);
	MATROSKA_LinkMetaSeekElement(obj->tracksMeta, (ebml_element *)obj->tracks);
	MATROSKA_LinkMetaSeekElement(obj->cuesMeta, (ebml_element *)obj->cues);
848

849
	return TRUE;
850 851
}

François Grisez's avatar
François Grisez committed
852
static ms_bool_t matroska_load_file(Matroska *obj) {
853 854
	int upperLevels = 0;
	ebml_parser_context readContext;
855 856
	ebml_element *elt;
	
857 858 859 860 861
	readContext.Context = &MATROSKA_ContextStream;
	readContext.EndPosition = INVALID_FILEPOS_T;
	readContext.Profile = 0;
	readContext.UpContext = NULL;

862
	obj->header = (ebml_master *)EBML_FindNextElement(obj->output, &readContext, &upperLevels, FALSE);
863 864 865 866 867 868
	EBML_ElementReadData(obj->header, obj->output, &readContext, FALSE, SCOPE_ALL_DATA, 0);
	readContext.Profile = ebml_reading_profile((ebml_master *)obj->header);

	obj->segment = (ebml_master *)EBML_FindNextElement(obj->output, &readContext, &upperLevels, FALSE);
	EBML_ElementReadData(obj->segment, obj->output, &readContext, FALSE, SCOPE_PARTIAL_DATA, 0);

François Grisez's avatar
François Grisez committed
869 870
	for(elt = EBML_MasterChildren(obj->segment); elt != NULL; elt = EBML_MasterNext(elt)) {
		if(EBML_ElementIsType(elt, &MATROSKA_ContextSeekHead)) {
871
			matroska_seekpoint *seekPoint = NULL;
872 873
			obj->metaSeek = (ebml_master*)elt;
			
François Grisez's avatar
François Grisez committed
874 875
			for(seekPoint = (matroska_seekpoint *)EBML_MasterChildren(obj->metaSeek); seekPoint != NULL; seekPoint = (matroska_seekpoint *)EBML_MasterNext(seekPoint)) {
				if(MATROSKA_MetaSeekIsClass(seekPoint, &MATROSKA_ContextInfo)) {
876
					obj->infoMeta = seekPoint;
François Grisez's avatar
François Grisez committed
877
				} else if(MATROSKA_MetaSeekIsClass(seekPoint, &MATROSKA_ContextTracks)) {
878
					obj->tracksMeta = seekPoint;
François Grisez's avatar
François Grisez committed
879
				} else if(MATROSKA_MetaSeekIsClass(seekPoint, &MATROSKA_ContextCues)) {
880 881 882
					obj->cuesMeta = seekPoint;
				}
			}
François Grisez's avatar
François Grisez committed
883
		} else if(EBML_ElementIsType(elt, &MATROSKA_ContextInfo)) {
884 885 886
			obj->info = (ebml_master*)elt;
			obj->timecodeScale = EBML_IntegerValue((ebml_integer *)EBML_MasterFindChild(obj->info, &MATROSKA_ContextTimecodeScale));
			MATROSKA_LinkMetaSeekElement(obj->infoMeta, (ebml_element *)obj->info);
François Grisez's avatar
François Grisez committed
887
		} else if(EBML_ElementIsType(elt, &MATROSKA_ContextTracks)) {
888 889
			obj->tracks = (ebml_master*)elt;
			MATROSKA_LinkMetaSeekElement(obj->tracksMeta, (ebml_element *)obj->tracks);
François Grisez's avatar
François Grisez committed
890
		} else if(EBML_ElementIsType(elt, &MATROSKA_ContextCues)) {
891 892
			obj->cues = (ebml_master*)elt;
			MATROSKA_LinkMetaSeekElement(obj->cuesMeta, (ebml_element *)obj->cues);
François Grisez's avatar
François Grisez committed
893
		} else if(EBML_ElementIsType(elt, &MATROSKA_ContextCluster)) {
894
			obj->cluster = (ebml_master *)elt;
François Grisez's avatar
François Grisez committed
895
			if(obj->nbClusters == 0) {
896
				obj->firstCluster = obj->cluster;
897
			}
898
			MATROSKA_LinkClusterBlocks((matroska_cluster *)obj->cluster, obj->info, obj->tracks, FALSE);
899 900 901 902 903 904
			obj->nbClusters++;
		}
	}
	return TRUE;
}

François Grisez's avatar
François Grisez committed
905
static int matroska_open_file(Matroska *obj, const char path[], MatroskaOpenMode mode) {
906 907
	int err = 0;

François Grisez's avatar
François Grisez committed
908
	switch(mode) {
909
	case MKV_OPEN_CREATE:
François Grisez's avatar
François Grisez committed
910
		if((obj->output = StreamOpen(obj->p, path, SFLAG_WRONLY | SFLAG_CREATE)) == NULL) {
911 912 913
			err = -2;
			break;
		}
François Grisez's avatar
François Grisez committed
914
		if(!matroska_create_file(obj, path)) {
915 916 917 918 919
			err = -3;
		}
		break;

	case MKV_OPEN_APPEND:
François Grisez's avatar
François Grisez committed
920
		if((obj->output = StreamOpen(obj->p, path, SFLAG_REOPEN)) == NULL) {
921 922 923
			err = -2;
			break;
		}
François Grisez's avatar
François Grisez committed
924
		if(!matroska_load_file(obj)) {
925 926 927
			err = -3;
			break;
		}
François Grisez's avatar
François Grisez committed
928
		if(obj->cues == NULL) {
929 930
			obj->cues = (ebml_master *)EBML_ElementCreate(obj->p, &MATROSKA_ContextCues, FALSE, NULL);
		}
François Grisez's avatar
François Grisez committed
931
		if(obj->cluster == NULL) {
932
			Stream_Seek(obj->output, 0, SEEK_END);
François Grisez's avatar
François Grisez committed
933
		} else {
934 935 936 937 938
			Stream_Seek(obj->output, EBML_ElementPositionEnd((ebml_element *)obj->cluster), SEEK_SET);
		}
		break;

	case MKV_OPEN_RO:
François Grisez's avatar
François Grisez committed
939
		if((obj->output = StreamOpen(obj->p, path, SFLAG_RDONLY)) == NULL) {
940 941 942
			err = -2;
			break;
		}
François Grisez's avatar
François Grisez committed
943
		if(!matroska_load_file(obj)) {
944 945 946 947 948 949 950 951 952 953
			err = -3;
			break;
		}
		break;

	default:
		err = -1;
		break;
	}
	return err;
954 955
}

François Grisez's avatar
François Grisez committed
956
static void matroska_close_file(Matroska *obj) {
957 958 959 960
	if(obj->output != NULL) {
		StreamClose(obj->output);
	}
	if(obj->header != NULL) {
961
		Node_Release((node *)obj->header);
962 963
	}
	if(obj->segment != NULL) {
964
		Node_Release((node *)obj->segment);
965
	}
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
	obj->output = NULL;
	obj->header = NULL;
	obj->header = NULL;
	obj->segment = NULL;
	obj->cluster = NULL;
	obj->info = NULL;
	obj->tracks = NULL;
	obj->metaSeek = NULL;
	obj->cues = NULL;
	obj->firstCluster = NULL;
	obj->currentCluster = NULL;
	obj->infoMeta = NULL;
	obj->tracksMeta = NULL;
	obj->cuesMeta = NULL;
	obj->currentBlock = NULL;
981 982
	obj->timecodeScale = -1;
	obj->segmentInfoPosition = -1;
983
	obj->nbClusters = 0;
984 985
}

François Grisez's avatar
François Grisez committed
986
static void matroska_set_doctype_version(Matroska *obj, int doctypeVersion, int doctypeReadVersion) {
987 988
	EBML_IntegerSetValue((ebml_integer*)EBML_MasterFindChild(obj->header, &EBML_ContextDocTypeVersion), doctypeVersion);
	EBML_IntegerSetValue((ebml_integer*)EBML_MasterFindChild(obj->header, &EBML_ContextDocTypeReadVersion), doctypeReadVersion);
989 990
}

François Grisez's avatar
François Grisez committed
991
static inline void matroska_write_ebml_header(Matroska *obj) {
992
	EBML_ElementRender((ebml_element *)obj->header, obj->output, WRITE_DEFAULT_ELEMENT, FALSE, FALSE, NULL);
993 994
}

François Grisez's avatar
François Grisez committed
995 996
static int matroska_set_segment_info(Matroska *obj, const char writingApp[], const char muxingApp[], double duration) {
	if(obj->timecodeScale == -1) {
997
		return -1;
François Grisez's avatar
François Grisez committed
998
	} else {
999 1000 1001 1002 1003 1004
		EBML_IntegerSetValue((ebml_integer *)EBML_MasterGetChild(obj->info, &MATROSKA_ContextTimecodeScale), obj->timecodeScale);
		EBML_StringSetValue((ebml_string*)EBML_MasterGetChild(obj->info, &MATROSKA_ContextMuxingApp), muxingApp);
		EBML_StringSetValue((ebml_string*)EBML_MasterGetChild(obj->info, &MATROSKA_ContextWritingApp), writingApp);
		EBML_FloatSetValue((ebml_float *)EBML_MasterGetChild(obj->info, &MATROSKA_ContextDuration), duration);
		return 0;
	}
1005 1006
}

François Grisez's avatar
François Grisez committed
1007
static inline timecode_t matroska_get_duration(const Matroska *obj) {
1008
	return (timecode_t)EBML_FloatValue((ebml_float *)EBML_MasterFindChild(obj->info, &MATROSKA_ContextDuration));
1009 1010
}

1011 1012 1013 1014
static inline timecode_t matroska_get_timecode_scale(const Matroska *obj) {
	return obj->timecodeScale;
}

François Grisez's avatar
François Grisez committed
1015
static void updateElementHeader(ebml_element *element, stream *file) {
1016 1017 1018 1019 1020
	filepos_t initial_pos = Stream_Seek(file, 0, SEEK_CUR);
	Stream_Seek(file, EBML_ElementPosition(element), SEEK_SET);
	EBML_ElementUpdateSize(element, WRITE_DEFAULT_ELEMENT, FALSE);
	EBML_ElementRenderHead(element, file, FALSE, NULL);
	Stream_Seek(file, initial_pos, SEEK_SET);
1021 1022
}

François Grisez's avatar
François Grisez committed
1023
static inline void matroska_start_segment(Matroska *obj) {
1024 1025
	EBML_ElementSetSizeLength((ebml_element *)obj->segment, 8);
	EBML_ElementRenderHead((ebml_element *)obj->segment, obj->output, FALSE, NULL);
1026
}
1027

1028
static size_t matroska_write_zeros(Matroska *obj, size_t nbZeros) {
1029
	uint8_t *data = (uint8_t *)ms_new0(uint8_t, nbZeros);
Simon Morlat's avatar
Simon Morlat committed
1030 1031
	size_t written=0;
	
François Grisez's avatar
François Grisez committed
1032
	if(data == NULL) {
1033 1034 1035 1036 1037
		return 0;
	}
	Stream_Write(obj->output, data, nbZeros, &written);
	ms_free(data);
	return written;
1038 1039
}

François Grisez's avatar
François Grisez committed
1040
static inline void matroska_mark_segment_info_position(Matroska *obj) {
1041
	obj->segmentInfoPosition = Stream_Seek(obj->output, 0, SEEK_CUR);
1042 1043
}

François Grisez's avatar
François Grisez committed
1044 1045
static int matroska_go_to_segment_info_mark(Matroska *obj) {
	if(obj->segmentInfoPosition == -1) {
1046 1047
		return -1;
	}
1048
	Stream_Seek(obj->output, obj->segmentInfoPosition, SEEK_SET);
1049
	return 0;
1050 1051
}

François Grisez's avatar
François Grisez committed
1052
static inline void matroska_go_to_file_end(Matroska *obj) {
1053
	Stream_Seek(obj->output, 0, SEEK_END);
1054 1055
}

François Grisez's avatar
François Grisez committed
1056
static inline void matroska_go_to_segment_begin(Matroska *obj) {
1057
	Stream_Seek(obj->output, EBML_ElementPositionData((ebml_element *)obj->segment), SEEK_SET);
1058 1059
}

François Grisez's avatar
François Grisez committed
1060
static inline void matroska_go_to_last_cluster_end(Matroska *obj) {
1061
	Stream_Seek(obj->output, EBML_ElementPositionEnd((ebml_element *)obj->cluster), SEEK_SET);
1062 1063
}

François Grisez's avatar
François Grisez committed
1064
static inline void matroska_go_to_segment_info_begin(Matroska *obj) {
1065
	Stream_Seek(obj->output, EBML_ElementPosition((ebml_element *)obj->info), SEEK_SET);
1066 1067
}

1068 1069 1070 1071 1072 1073 1074 1075 1076
static inline timecode_t matroska_block_get_timestamp(const Matroska *obj) {
	return MATROSKA_BlockTimecode((matroska_block *)obj->currentBlock)/obj->timecodeScale;
}

static inline int matroska_block_get_track_num(const Matroska *obj) {
	return MATROSKA_BlockTrackNum((matroska_block *)obj->currentBlock);
}

static int ebml_element_cmp_position(const void *a, const void *b) {
1077
	return EBML_ElementPosition((ebml_element *)a) - EBML_ElementPosition((ebml_element *)b);
1078 1079
}

1080
static void ebml_master_sort(ebml_master *master_elt) {
1081
	MSList *elts = NULL;
1082
	MSList *it = NULL;
1083
	ebml_element *elt = NULL;
1084
	
1085
	for(elt = EBML_MasterChildren(master_elt); elt != NULL; elt = EBML_MasterNext(elt)) {
1086 1087 1088
		elts = ms_list_insert_sorted(elts, elt, (MSCompareFunc)ebml_element_cmp_position);
	}
	EBML_MasterClear(master_elt);
1089
	
1090
	for(it = elts; it != NULL; it = ms_list_next(it)) {
1091 1092 1093
		EBML_MasterAppend(master_elt, (ebml_element *)it->data);
	}
	ms_list_free(elts);
1094 1095
}

1096
static int ebml_master_fill_blanks(stream *output, ebml_master *master) {
1097
	MSList *voids = NULL;
1098
	MSList *it = NULL;
1099
	ebml_element *elt1 = NULL, *elt2 = NULL;
1100
	
1101
	for(elt1 = EBML_MasterChildren(master), elt2 = EBML_MasterNext(elt1); elt2 != NULL; elt1 = EBML_MasterNext(elt1), elt2 = EBML_MasterNext(elt2)) {
1102 1103 1104
		filepos_t elt1_end_pos = EBML_ElementPositionEnd(elt1);
		filepos_t elt2_pos = EBML_ElementPosition(elt2);
		int interval = elt2_pos - elt1_end_pos;
1105
		if(interval < 0) {
1106
			return -1; // Elements are neither contigus or distinct.
1107
		} else if(interval == 0) {
1108
			// Nothing to do. Elements are contigus.
1109
		} else if(interval > 0 && interval < 2) {
1110
			return -2; // Not enough space to write a void element.
1111
		} else {
1112 1113 1114 1115 1116 1117 1118 1119
			ebml_element *voidElt = EBML_ElementCreate(master, &EBML_ContextEbmlVoid, TRUE, NULL);
			EBML_VoidSetFullSize(voidElt, interval);
			Stream_Seek(output, elt1_end_pos, SEEK_SET);
			EBML_ElementRender(voidElt, output, FALSE, FALSE, FALSE, NULL);
			voids = ms_list_append(voids, voidElt);
		}
	}

1120
	for(it = voids; it != NULL; it = ms_list_next(it)) {
1121 1122 1123 1124
		EBML_MasterAppend(master, (ebml_element *)it->data);
	}
	ms_list_free(voids);
	return 0;
1125 1126
}

François Grisez's avatar
François Grisez committed
1127
static void ebml_master_delete_empty_elements(ebml_master *master) {
1128
	ebml_element *child = NULL;
François Grisez's avatar
François Grisez committed
1129 1130
	for(child = EBML_MasterChildren(master); child != NULL; child = EBML_MasterNext(child)) {
		if(EBML_ElementDataSize(child, WRITE_DEFAULT_ELEMENT) <= 0) {
1131 1132 1133 1134
			EBML_MasterRemove(master, child);
			NodeDelete((node *)child);
		}
	}
1135 1136
}

François Grisez's avatar
François Grisez committed
1137
static int matroska_close_segment(Matroska *obj) {
1138 1139 1140 1141
	filepos_t initialPos = Stream_Seek(obj->output, 0, SEEK_CUR);
	EBML_ElementUpdateSize(obj->segment, WRITE_DEFAULT_ELEMENT, FALSE);
	ebml_master_delete_empty_elements(obj->segment);
	ebml_master_sort(obj->segment);
François Grisez's avatar
François Grisez committed
1142
	if(ebml_master_fill_blanks(obj->output, obj->segment) < 0) {
1143 1144 1145 1146 1147
		return -1;
	}
	updateElementHeader((ebml_element *)obj->segment, obj->output);
	Stream_Seek(obj->output, initialPos, SEEK_SET);
	return 0;
1148 1149
}

François Grisez's avatar
François Grisez committed
1150
static ebml_master *matroska_find_track_entry(const Matroska *obj, int trackNum) {
1151
	ebml_element *trackEntry = NULL;
1152 1153 1154 1155
	for(trackEntry = EBML_MasterChildren(obj->tracks);
		trackEntry != NULL && EBML_IntegerValue((ebml_integer *)EBML_MasterFindChild((ebml_master *)trackEntry, &MATROSKA_ContextTrackNumber)) != trackNum;
		trackEntry = EBML_MasterNext(trackEntry));
	return (ebml_master *)trackEntry;
1156 1157
}

François Grisez's avatar
François Grisez committed
1158
static int matroska_get_codec_private(const Matroska *obj, int trackNum, const uint8_t **data, size_t *length) {
1159
	ebml_master *trackEntry = matroska_find_track_entry(obj, trackNum);
1160
	ebml_binary *codecPrivate;
1161
	if(trackEntry == NULL)
François Grisez's avatar
François Grisez committed
1162
		return -1;
1163
	codecPrivate = (ebml_binary *)EBML_MasterFindChild(trackEntry, &MATROSKA_ContextCodecPrivate);
1164
	if(codecPrivate == NULL)
François Grisez's avatar
François Grisez committed
1165
		return -2;
1166

1167 1168 1169
	*length = EBML_ElementDataSize((ebml_element *)codecPrivate, FALSE);
	*data =  EBML_BinaryGetData(codecPrivate);
	if(*data == NULL)
François Grisez's avatar
François Grisez committed
1170
		return -3;
1171
	else
François Grisez's avatar
François Grisez committed
1172
		return 0;
1173 1174
}

François Grisez's avatar
François Grisez committed
1175
static void matroska_start_cluster(Matroska *obj, timecode_t clusterTimecode) {
1176
	obj->cluster = (ebml_master *)EBML_MasterAddElt(obj->segment, &MATROSKA_ContextCluster, TRUE);
François Grisez's avatar
François Grisez committed
1177
	if(obj->nbClusters == 0) {
1178
		obj->firstCluster = obj->cluster;
1179
	}
1180 1181 1182 1183
	EBML_ElementSetSizeLength((ebml_element *)obj->cluster, 8);
	EBML_IntegerSetValue((ebml_integer *)EBML_MasterGetChild(obj->cluster, &MATROSKA_ContextTimecode), clusterTimecode);
	EBML_ElementRender((ebml_element *)obj->cluster, obj->output, WRITE_DEFAULT_ELEMENT, FALSE, FALSE, NULL);
	obj->nbClusters++;
1184 1185
}

François Grisez's avatar
François Grisez committed
1186
static void matroska_close_cluster(Matroska *obj) {
1187
	ebml_element *block = NULL;
François Grisez's avatar
François Grisez committed
1188
	if(obj->cluster == NULL) {
1189
		return;
François Grisez's avatar
François Grisez committed
1190
	} else {
1191 1192
		block = EBML_MasterFindChild(obj->cluster, &MATROSKA_ContextSimpleBlock);
	}
François Grisez's avatar
François Grisez committed
1193
	if(block == NULL) {