/*
 * Copyright 2015, Dario Casalinuovo
 * Copyright 2004, 2006, Jérôme Duval.
 * Copyright 2003-2004, Andrew Bachmann.
 * Copyright 2002-2004, 2006 Marcus Overhagen.
 * Copyright 2002, Eric Jaessler.
 * All rights reserved. Distributed under the terms of the MIT license.
 */
 
 
#include <MediaDefs.h>
 
#include <Application.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <IconUtils.h>
#include <LaunchRoster.h>
#include <Locale.h>
#include <MediaNode.h>
#include <MediaRoster.h>
#include <Node.h>
#include <Notification.h>
#include <Roster.h>
 
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
 
#include "AddOnManager.h"
#include "DataExchange.h"
#include "MediaDebug.h"
#include "MediaMisc.h"
#include "MediaRosterEx.h"
 
 
#define META_DATA_MAX_SIZE			(16 << 20)
#define META_DATA_AREA_MIN_SIZE		32000
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaDefs"
 
 
// #pragma mark - media_destination
 
 
media_destination::media_destination(port_id port, int32 id)
	:
	port(port),
	id(id)
{
}
 
 
media_destination::media_destination(const media_destination& clone)
	:
	port(clone.port),
	id(clone.id)
{
}
 
 
media_destination&
media_destination::operator=(const media_destination& clone)
{
	port = clone.port;
	id = clone.id;
	return *this;
}
 
 
media_destination::media_destination()
	:
	port(-1),
	id(-1)
{
}
 
 
media_destination::~media_destination()
{
}
 
 
media_destination media_destination::null(-1, -1);
 
 
// #pragma mark - media_source
 
 
media_source::media_source(port_id port,
						   int32 id)
	:
	port(port),
	id(id)
{
}
 
 
media_source::media_source(const media_source& clone)
	:
	port(clone.port),
	id(clone.id)
{
}
 
 
media_source&
media_source::operator=(const media_source& clone)
{
	port = clone.port;
	id = clone.id;
	return *this;
}
 
 
media_source::media_source()
	:
	port(-1),
	id(-1)
{
}
 
 
media_source::~media_source()
{
}
 
 
media_source media_source::null(-1, -1);
 
 
// #pragma mark -
 
 
bool
operator==(const media_destination& a, const media_destination& b)
{
	return a.port == b.port && a.id == b.id;
}
 
 
bool
operator!=(const media_destination& a, const media_destination& b)
{
	return a.port != b.port || a.id != b.id;
}
 
 
bool
operator<(const media_destination& a, const media_destination& b)
{
	UNIMPLEMENTED();
	return false;
}
 
 
bool
operator==(const media_source& a, const media_source& b)
{
	return a.port == b.port && a.id == b.id;
}
 
 
bool
operator!=(const media_source& a, const media_source& b)
{
	return a.port != b.port || a.id != b.id;
}
 
 
bool
operator<(const media_source& a, const media_source& b)
{
	UNIMPLEMENTED();
	return false;
}
 
 
bool
operator==(const media_node& a, const media_node& b)
{
	return a.node == b.node && a.port == b.port && a.kind == b.kind;
}
 
 
bool
operator!=(const media_node& a, const media_node& b)
{
	return a.node != b.node || a.port != b.port || a.kind != b.kind;
}
 
 
bool
operator<(const media_node& a, const media_node& b)
{
	UNIMPLEMENTED();
	return false;
}
 
 
// #pragma mark -
 
 
media_multi_audio_format media_raw_audio_format::wildcard;
 
media_multi_audio_format media_multi_audio_format::wildcard;
 
media_encoded_audio_format media_encoded_audio_format::wildcard = {{0}};
 
media_video_display_info media_video_display_info::wildcard = {(color_space)0};
 
media_raw_video_format media_raw_video_format::wildcard = {0};
 
media_encoded_video_format media_encoded_video_format::wildcard = {{0}};
 
media_multistream_format media_multistream_format::wildcard = {0};
 
 
// #pragma mark - media_format::Matches() support
 
 
static bool
raw_audio_format_matches(const media_raw_audio_format& a,
	const media_raw_audio_format& b)
{
	if (a.frame_rate != 0 && b.frame_rate != 0 && a.frame_rate != b.frame_rate)
		return false;
	if (a.channel_count != 0 && b.channel_count != 0
		&& a.channel_count != b.channel_count) {
		return false;
	}
	if (a.format != 0 && b.format != 0 && a.format != b.format)
		return false;
	if (a.byte_order != 0 && b.byte_order != 0 && a.byte_order != b.byte_order)
		return false;
	if (a.buffer_size != 0 && b.buffer_size != 0
		&& a.buffer_size != b.buffer_size) {
		return false;
	}
	if (a.frame_rate != 0 && b.frame_rate != 0 && a.frame_rate != b.frame_rate)
		return false;
	return true;
}
 
 
static bool
multi_audio_info_matches(const media_multi_audio_info& a,
	const media_multi_audio_info& b)
{
	if (a.channel_mask != 0 && b.channel_mask != 0
		&& a.channel_mask != b.channel_mask) {
		return false;
	}
	if (a.valid_bits != 0 && b.valid_bits != 0 && a.valid_bits != b.valid_bits)
		return false;
	if (a.matrix_mask != 0 && b.matrix_mask != 0
		&& a.matrix_mask != b.matrix_mask) {
		return false;
	}
	return true;
}
 
 
static bool
multi_audio_format_matches(const media_multi_audio_format& a,
	const media_multi_audio_format& b)
{
	return raw_audio_format_matches(a, b) && multi_audio_info_matches(a, b);
}
 
 
static bool
raw_video_format_matches(const media_raw_video_format& a,
	const media_raw_video_format& b)
{
	if (a.field_rate != 0 && b.field_rate != 0
		&& a.field_rate != b.field_rate) {
		return false;
	}
	if (a.interlace != 0 && b.interlace != 0
		&& a.interlace != b.interlace) {
		return false;
	}
	if (a.first_active != 0 && b.first_active != 0
		&& a.first_active != b.first_active) {
		return false;
	}
	if (a.last_active != 0 && b.last_active != 0
		&& a.last_active != b.last_active) {
		return false;
	}
	if (a.orientation != 0 && b.orientation != 0
		&& a.orientation != b.orientation) {
		return false;
	}
	if (a.pixel_width_aspect != 0 && b.pixel_width_aspect != 0
		&& a.pixel_width_aspect != b.pixel_width_aspect) {
		return false;
	}
	if (a.pixel_height_aspect != 0 && b.pixel_height_aspect != 0
		&& a.pixel_height_aspect != b.pixel_height_aspect) {
		return false;
	}
	if (a.display.format != 0 && b.display.format != 0
		&& a.display.format != b.display.format) {
		return false;
	}
	if (a.display.line_width != 0 && b.display.line_width != 0
		&& a.display.line_width != b.display.line_width) {
		return false;
	}
	if (a.display.line_count != 0 && b.display.line_count != 0
		&& a.display.line_count != b.display.line_count) {
		return false;
	}
	if (a.display.bytes_per_row != 0 && b.display.bytes_per_row != 0
		&& a.display.bytes_per_row != b.display.bytes_per_row) {
		return false;
	}
	if (a.display.pixel_offset != 0 && b.display.pixel_offset != 0
		&& a.display.pixel_offset != b.display.pixel_offset) {
		return false;
	}
	if (a.display.line_offset != 0 && b.display.line_offset != 0
		&& a.display.line_offset != b.display.line_offset) {
		return false;
	}
	if (a.display.flags != 0 && b.display.flags != 0
		&& a.display.flags != b.display.flags) {
		return false;
	}
 
	return true;
}
 
 
static bool
multistream_format_matches(const media_multistream_format& a,
	const media_multistream_format& b)
{
	if (a.avg_bit_rate != 0 && b.avg_bit_rate != 0
		&& a.avg_bit_rate != b.avg_bit_rate) {
		return false;
	}
	if (a.max_bit_rate != 0 && b.max_bit_rate != 0
		&& a.max_bit_rate != b.max_bit_rate) {
		return false;
	}
	if (a.avg_chunk_size != 0 && b.avg_chunk_size != 0
		&& a.avg_chunk_size != b.avg_chunk_size) {
		return false;
	}
	if (a.max_chunk_size != 0 && b.max_chunk_size != 0
		&& a.max_chunk_size != b.max_chunk_size) {
		return false;
	}
	if (a.flags != 0 && b.flags != 0 && a.flags != b.flags)
		return false;
	if (a.format != 0 && b.format != 0 && a.format != b.format)
		return false;
 
	if (a.format == 0 && b.format == 0) {
		// TODO: How do we compare two formats with no type?
		return true;
	}
 
	switch ((a.format != 0) ? a.format : b.format) {
		default:
			return true; // TODO: really?
 
		case media_multistream_format::B_VID:
			if (a.u.vid.frame_rate != 0 && b.u.vid.frame_rate != 0
				&& a.u.vid.frame_rate != b.u.vid.frame_rate) {
				return false;
			}
			if (a.u.vid.width != 0 && b.u.vid.width != 0
				&& a.u.vid.width != b.u.vid.width) {
				return false;
			}
			if (a.u.vid.height != 0 && b.u.vid.height != 0
				&& a.u.vid.height != b.u.vid.height) {
				return false;
			}
			if (a.u.vid.space != 0 && b.u.vid.space != 0
				&& a.u.vid.space != b.u.vid.space) {
				return false;
			}
			if (a.u.vid.sampling_rate != 0 && b.u.vid.sampling_rate != 0
				&& a.u.vid.sampling_rate != b.u.vid.sampling_rate) {
				return false;
			}
			if (a.u.vid.sample_format != 0 && b.u.vid.sample_format != 0
				&& a.u.vid.sample_format != b.u.vid.sample_format) {
				return false;
			}
			if (a.u.vid.byte_order != 0 && b.u.vid.byte_order != 0
				&& a.u.vid.byte_order != b.u.vid.byte_order) {
				return false;
			}
			if (a.u.vid.channel_count != 0 && b.u.vid.channel_count != 0
				&& a.u.vid.channel_count != b.u.vid.channel_count) {
				return false;
			}
			return true;
 
		case media_multistream_format::B_AVI:
			if (a.u.avi.us_per_frame != 0 && b.u.avi.us_per_frame != 0
				&& a.u.avi.us_per_frame != b.u.avi.us_per_frame) {
				return false;
			}
			if (a.u.avi.width != 0 && b.u.avi.width != 0
				&& a.u.avi.width != b.u.avi.width) {
				return false;
			}
			if (a.u.avi.height != 0 && b.u.avi.height != 0
				&& a.u.avi.height != b.u.avi.height) {
				return false;
			}
			if (a.u.avi.type_count != 0 && b.u.avi.type_count != 0
				&& a.u.avi.type_count != b.u.avi.type_count) {
				return false;
			}
			if (a.u.avi.types[0] != 0 && b.u.avi.types[0] != 0
				&& a.u.avi.types[0] != b.u.avi.types[0]) {
				return false;
			}
			if (a.u.avi.types[1] != 0 && b.u.avi.types[1] != 0
				&& a.u.avi.types[1] != b.u.avi.types[1]) {
				return false;
			}
			if (a.u.avi.types[2] != 0 && b.u.avi.types[2] != 0
				&& a.u.avi.types[2] != b.u.avi.types[2]) {
				return false;
			}
			if (a.u.avi.types[3] != 0 && b.u.avi.types[3] != 0
				&& a.u.avi.types[3] != b.u.avi.types[3]) {
				return false;
			}
			if (a.u.avi.types[4] != 0 && b.u.avi.types[4] != 0
				&& a.u.avi.types[4] != b.u.avi.types[4]) {
				return false;
			}
			return true;
	}
}
 
 
static bool
encoded_audio_format_matches(const media_encoded_audio_format& a,
	const media_encoded_audio_format& b)
{
	if (!raw_audio_format_matches(a.output, b.output))
		return false;
	if (a.encoding != 0 && b.encoding != 0 && a.encoding != b.encoding)
		return false;
	if (a.bit_rate != 0 && b.bit_rate != 0 && a.bit_rate != b.bit_rate)
		return false;
	if (a.frame_size != 0 && b.frame_size != 0 && a.frame_size != b.frame_size)
		return false;
	if (!multi_audio_info_matches(a.multi_info, b.multi_info))
		return false;
 
	if (a.encoding == 0 && b.encoding == 0)
		return true; // can't compare
 
	switch((a.encoding != 0) ? a.encoding : b.encoding) {
		case media_encoded_audio_format::B_ANY:
		default:
			return true;
	}
}
 
 
static bool
encoded_video_format_matches(const media_encoded_video_format& a,
	const media_encoded_video_format& b)
{
	if (!raw_video_format_matches(a.output, b.output))
		return false;
	if (a.encoding != 0 && b.encoding != 0 && a.encoding != b.encoding)
		return false;
 
	if (a.avg_bit_rate != 0 && b.avg_bit_rate != 0
		&& a.avg_bit_rate != b.avg_bit_rate) {
		return false;
	}
	if (a.max_bit_rate != 0 && b.max_bit_rate != 0
		&& a.max_bit_rate != b.max_bit_rate) {
		return false;
	}
	if (a.frame_size != 0 && b.frame_size != 0
		&& a.frame_size != b.frame_size) {
		return false;
	}
	if (a.forward_history != 0 && b.forward_history != 0
		&& a.forward_history != b.forward_history) {
		return false;
	}
	if (a.backward_history != 0 && b.backward_history != 0
		&& a.backward_history != b.backward_history) {
		return false;
	}
 
	if (a.encoding == 0 && b.encoding == 0)
		return true; // can't compare
 
	switch((a.encoding != 0) ? a.encoding : b.encoding) {
		case media_encoded_video_format::B_ANY:
		default:
			return true;
	}
}
 
 
// #pragma mark - media_format::SpecializeTo() support
 
 
static void
raw_audio_format_specialize(media_raw_audio_format* format,
	const media_raw_audio_format* other)
{
	if (format->frame_rate == 0)
		format->frame_rate = other->frame_rate;
	if (format->channel_count == 0)
		format->channel_count = other->channel_count;
	if (format->format == 0)
		format->format = other->format;
	if (format->byte_order == 0)
		format->byte_order = other->byte_order;
	if (format->buffer_size == 0)
		format->buffer_size = other->buffer_size;
	if (format->frame_rate == 0)
		format->frame_rate = other->frame_rate;
}
 
 
static void
multi_audio_info_specialize(media_multi_audio_info* format,
	const media_multi_audio_info* other)
{
	if (format->channel_mask == 0)
		format->channel_mask = other->channel_mask;
	if (format->valid_bits == 0)
		format->valid_bits = other->valid_bits;
	if (format->matrix_mask == 0)
		format->matrix_mask = other->matrix_mask;
}
 
 
static void
multi_audio_format_specialize(media_multi_audio_format* format,
	const media_multi_audio_format* other)
{
	raw_audio_format_specialize(format, other);
	multi_audio_info_specialize(format, other);
}
 
 
static void
raw_video_format_specialize(media_raw_video_format* format,
	const media_raw_video_format* other)
{
	if (format->field_rate == 0)
		format->field_rate = other->field_rate;
	if (format->interlace == 0)
		format->interlace = other->interlace;
	if (format->first_active == 0)
		format->first_active = other->first_active;
	if (format->last_active == 0)
		format->last_active = other->last_active;
	if (format->orientation == 0)
		format->orientation = other->orientation;
	if (format->pixel_width_aspect == 0)
		format->pixel_width_aspect = other->pixel_width_aspect;
	if (format->pixel_height_aspect == 0)
		format->pixel_height_aspect = other->pixel_height_aspect;
	if (format->display.format == 0)
		format->display.format = other->display.format;
	if (format->display.line_width == 0)
		format->display.line_width = other->display.line_width;
	if (format->display.line_count == 0)
		format->display.line_count = other->display.line_count;
	if (format->display.bytes_per_row == 0)
		format->display.bytes_per_row = other->display.bytes_per_row;
	if (format->display.pixel_offset == 0)
		format->display.pixel_offset = other->display.pixel_offset;
	if (format->display.line_offset == 0)
		format->display.line_offset = other->display.line_offset;
	if (format->display.flags == 0)
		format->display.flags = other->display.flags;
}
 
 
static void
multistream_format_specialize(media_multistream_format* format,
	const media_multistream_format* other)
{
	if (format->avg_bit_rate == 0)
		format->avg_bit_rate = other->avg_bit_rate;
	if (format->max_bit_rate == 0)
		format->max_bit_rate = other->max_bit_rate;
	if (format->avg_chunk_size == 0)
		format->avg_chunk_size = other->avg_chunk_size;
	if (format->max_chunk_size == 0)
		format->max_chunk_size = other->max_chunk_size;
	if (format->flags == 0)
		format->flags = other->flags;
	if (format->format == 0)
		format->format = other->format;
 
	switch (format->format) {
		case media_multistream_format::B_VID:
			if (format->u.vid.frame_rate == 0)
				format->u.vid.frame_rate = other->u.vid.frame_rate;
			if (format->u.vid.width == 0)
				format->u.vid.width = other->u.vid.width;
			if (format->u.vid.height == 0)
				format->u.vid.height = other->u.vid.height;
			if (format->u.vid.space == 0)
				format->u.vid.space = other->u.vid.space;
			if (format->u.vid.sampling_rate == 0)
				format->u.vid.sampling_rate = other->u.vid.sampling_rate;
			if (format->u.vid.sample_format == 0)
				format->u.vid.sample_format = other->u.vid.sample_format;
			if (format->u.vid.byte_order == 0)
				format->u.vid.byte_order = other->u.vid.byte_order;
			if (format->u.vid.channel_count == 0)
				format->u.vid.channel_count = other->u.vid.channel_count;
			break;
 
		case media_multistream_format::B_AVI:
			if (format->u.avi.us_per_frame == 0)
				format->u.avi.us_per_frame = other->u.avi.us_per_frame;
			if (format->u.avi.width == 0)
				format->u.avi.width = other->u.avi.width;
			if (format->u.avi.height == 0)
				format->u.avi.height = other->u.avi.height;
			if (format->u.avi.type_count == 0)
				format->u.avi.type_count = other->u.avi.type_count;
			if (format->u.avi.types[0] == 0)
				format->u.avi.types[0] = other->u.avi.types[0];
			if (format->u.avi.types[1] == 0)
				format->u.avi.types[1] = other->u.avi.types[1];
			if (format->u.avi.types[2] == 0)
				format->u.avi.types[2] = other->u.avi.types[2];
			if (format->u.avi.types[3] == 0)
				format->u.avi.types[3] = other->u.avi.types[3];
			if (format->u.avi.types[4] == 0)
				format->u.avi.types[4] = other->u.avi.types[4];
			break;
 
		default:
			ERROR("media_format::SpecializeTo can't specialize "
				"media_multistream_format of format %" B_PRId32 "\n",
				format->format);
	}
}
 
 
static void
encoded_audio_format_specialize(media_encoded_audio_format* format,
	const media_encoded_audio_format* other)
{
	raw_audio_format_specialize(&format->output, &other->output);
	if (format->encoding == 0)
		format->encoding = other->encoding;
	if (format->bit_rate == 0)
		format->bit_rate = other->bit_rate;
	if (format->frame_size == 0)
		format->frame_size = other->frame_size;
	multi_audio_info_specialize(&format->multi_info, &other->multi_info);
}
 
 
static void
encoded_video_format_specialize(media_encoded_video_format* format,
	const media_encoded_video_format* other)
{
	raw_video_format_specialize(&format->output, &other->output);
	if (format->avg_bit_rate == 0)
		format->avg_bit_rate = other->avg_bit_rate;
	if (format->max_bit_rate == 0)
		format->max_bit_rate = other->max_bit_rate;
	if (format->encoding == 0)
		format->encoding = other->encoding;
	if (format->frame_size == 0)
		format->frame_size = other->frame_size;
	if (format->forward_history == 0)
		format->forward_history = other->forward_history;
	if (format->backward_history == 0)
		format->backward_history = other->backward_history;
}
 
 
// #pragma mark - media_format
 
 
bool
media_format::Matches(const media_format* other) const
{
	CALLED();
 
	if (type == 0 && other->type == 0) {
		// TODO: How do we compare two formats with no type?
		return true;
	}
 
	if (type != 0 && other->type != 0 && type != other->type)
		return false;
 
	switch ((type != 0) ? type : other->type) {
		case B_MEDIA_RAW_AUDIO:
			return multi_audio_format_matches(u.raw_audio, other->u.raw_audio);
 
		case B_MEDIA_RAW_VIDEO:
			return raw_video_format_matches(u.raw_video, other->u.raw_video);
 
		case B_MEDIA_MULTISTREAM:
			return multistream_format_matches(u.multistream,
				other->u.multistream);
 
		case B_MEDIA_ENCODED_AUDIO:
			return encoded_audio_format_matches(u.encoded_audio,
				other->u.encoded_audio);
 
		case B_MEDIA_ENCODED_VIDEO:
			return encoded_video_format_matches(u.encoded_video,
				other->u.encoded_video);
 
		default:
			return true; // TODO: really?
	}
}
 
 
void
media_format::SpecializeTo(const media_format* otherFormat)
{
	CALLED();
 
	if (type == 0 && otherFormat->type == 0) {
		ERROR("media_format::SpecializeTo can't specialize wildcard to other "
			"wildcard format\n");
		return;
	}
 
	if (type == 0)
		type = otherFormat->type;
 
	switch (type) {
		case B_MEDIA_RAW_AUDIO:
			multi_audio_format_specialize(&u.raw_audio,
				&otherFormat->u.raw_audio);
			return;
 
		case B_MEDIA_RAW_VIDEO:
			raw_video_format_specialize(&u.raw_video,
				&otherFormat->u.raw_video);
			return;
 
		case B_MEDIA_MULTISTREAM:
			multistream_format_specialize(&u.multistream,
				&otherFormat->u.multistream);
			return;
 
		case B_MEDIA_ENCODED_AUDIO:
			encoded_audio_format_specialize(&u.encoded_audio,
				&otherFormat->u.encoded_audio);
			return;
 
		case B_MEDIA_ENCODED_VIDEO:
			encoded_video_format_specialize(&u.encoded_video,
				&otherFormat->u.encoded_video);
			return;
 
		default:
			ERROR("media_format::SpecializeTo can't specialize format "
				"type %d\n", type);
	}
}
 
 
status_t
media_format::SetMetaData(const void* data, size_t size)
{
	if (!data || size > META_DATA_MAX_SIZE)
		return B_BAD_VALUE;
 
	void* new_addr;
	area_id new_area;
	if (size < META_DATA_AREA_MIN_SIZE) {
		new_area = B_BAD_VALUE;
		new_addr = malloc(size);
		if (!new_addr)
			return B_NO_MEMORY;
	} else {
		new_area = create_area("meta_data_area", &new_addr, B_ANY_ADDRESS,
			ROUND_UP_TO_PAGE(size), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
		if (new_area < 0)
			return (status_t)new_area;
	}
 
	if (meta_data_area > 0)
		delete_area(meta_data_area);
	else
		free(meta_data);
 
	meta_data = new_addr;
	meta_data_size = size;
	meta_data_area = new_area;
 
	memcpy(meta_data, data, size);
 
	if (meta_data_area > 0)
		set_area_protection(meta_data_area, B_READ_AREA);
 
	return B_OK;
}
 
 
const void*
media_format::MetaData() const
{
	return meta_data;
}
 
 
int32
media_format::MetaDataSize() const
{
	return meta_data_size;
}
 
 
void
media_format::Unflatten(const char *flatBuffer)
{
	// TODO: we should not!!! make flat copies of media_format
	memcpy(this, flatBuffer, sizeof(*this));
	meta_data = NULL;
	meta_data_area = B_BAD_VALUE;
}
 
 
void
media_format::Clear()
{
	memset(this, 0x00, sizeof(*this));
	meta_data = NULL;
	meta_data_area = B_BAD_VALUE;
}
 
 
media_format::media_format()
{
	this->Clear();
}
 
 
media_format::media_format(const media_format& other)
{
	this->Clear();
	*this = other;
}
 
 
media_format::~media_format()
{
	if (meta_data_area > 0)
		delete_area(meta_data_area);
	else
		free(meta_data);
}
 
 
// final
media_format&
media_format::operator=(const media_format& clone)
{
	// get rid of this format's meta data
	this->~media_format();
		// danger: using only ~media_format() would call the constructor
 
	// make a binary copy
	memcpy(this, &clone, sizeof(*this));
	// some binary copies are invalid:
	meta_data = NULL;
	meta_data_area = B_BAD_VALUE;
 
	// clone or copy the meta data
	if (clone.meta_data) {
		if (clone.meta_data_area != B_BAD_VALUE) {
			meta_data_area = clone_area("meta_data_clone_area", &meta_data,
				B_ANY_ADDRESS, B_READ_AREA, clone.meta_data_area);
			if (meta_data_area < 0) {
				// whoops, we just lost our meta data
				meta_data = NULL;
				meta_data_size = 0;
			}
		} else {
			meta_data = malloc(meta_data_size);
			if (meta_data) {
				memcpy(meta_data, clone.meta_data, meta_data_size);
			} else {
				// whoops, we just lost our meta data
				meta_data_size = 0;
			}
		}
	}
	return *this;
}
 
 
// #pragma mark -
 
 
bool
operator==(const media_raw_audio_format& a, const media_raw_audio_format& b)
{
	return a.frame_rate == b.frame_rate
		&& a.channel_count == b.channel_count
		&& a.format == b.format
		&& a.byte_order == b.byte_order
		&& a.buffer_size == b.buffer_size;
}
 
 
bool
operator==(const media_multi_audio_info& a, const media_multi_audio_info& b)
{
	return a.channel_mask == b.channel_mask
		&& a.valid_bits == b.valid_bits
		&& a.matrix_mask == b.matrix_mask;
}
 
 
bool
operator==(const media_multi_audio_format& a,
	const media_multi_audio_format& b)
{
	return (media_raw_audio_format)a == (media_raw_audio_format)b
		&& (media_multi_audio_info)a == (media_multi_audio_info)b;
}
 
 
bool
operator==(const media_encoded_audio_format& a,
	const media_encoded_audio_format& b)
{
	return a.output == b.output
		&& a.encoding == b.encoding
		&& a.bit_rate == b.bit_rate
		&& a.frame_size == b.frame_size
		&& a.multi_info == b.multi_info;
}
 
 
bool
operator==(const media_video_display_info& a,
	const media_video_display_info& b)
{
	return a.format == b.format
		&& a.line_width == b.line_width
		&& a.line_count == b.line_count
		&& a.bytes_per_row == b.bytes_per_row
		&& a.pixel_offset == b.pixel_offset
		&& a.line_offset == b.line_offset
		&& a.flags == b.flags;
}
 
 
bool
operator==(const media_raw_video_format& a, const media_raw_video_format& b)
{
	return a.field_rate == b.field_rate
		&& a.interlace == b.interlace
		&& a.first_active == b.first_active
		&& a.last_active == b.last_active
		&& a.orientation == b.orientation
		&& a.pixel_width_aspect == b.pixel_width_aspect
		&& a.pixel_height_aspect == b.pixel_height_aspect
		&& a.display == b.display;
}
 
 
bool
operator==(const media_encoded_video_format& a,
	const media_encoded_video_format& b)
{
	return a.output == b.output
		&& a.avg_bit_rate == b.avg_bit_rate
		&& a.max_bit_rate == b.max_bit_rate
		&& a.encoding == b.encoding
		&& a.frame_size == b.frame_size
		&& a.forward_history == b.forward_history
		&& a.backward_history == b.backward_history;
}
 
 
bool
operator==(const media_multistream_format::vid_info& a,
	const media_multistream_format::vid_info& b)
{
	return a.frame_rate == b.frame_rate
		&& a.width == b.width
		&& a.height == b.height
		&& a.space == b.space
		&& a.sampling_rate == b.sampling_rate
		&& a.sample_format == b.sample_format
		&& a.byte_order == b.byte_order
		&& a.channel_count == b.channel_count;
}
 
 
bool
operator==(const media_multistream_format::avi_info& a,
	const media_multistream_format::avi_info& b)
{
	return a.us_per_frame == b.us_per_frame
		&& a.width == b.width
		&& a.height == b.height
		&& a.type_count == b.type_count
		&& a.types[0] == b.types[0]
		&& a.types[1] == b.types[1]
		&& a.types[2] == b.types[2]
		&& a.types[3] == b.types[3]
		&& a.types[4] == b.types[4];
}
 
 
bool
operator==(const media_multistream_format& a,
	const media_multistream_format& b)
{
	if (a.avg_bit_rate != b.avg_bit_rate
		|| a.max_bit_rate != b.max_bit_rate
		|| a.avg_chunk_size != b.avg_chunk_size
		|| a.max_chunk_size != b.max_chunk_size
		|| a.format != b.format
		|| a.flags != b.flags) {
		return false;
	}
 
	switch (a.format) {
		case media_multistream_format::B_VID:
			return a.u.vid == b.u.vid;
 
		case media_multistream_format::B_AVI:
			return a.u.avi == b.u.avi;
 
		default:
			return true; // TODO: really?
	}
}
 
 
bool
operator==(const media_format& a, const media_format& b)
{
	if (a.type != b.type
		|| a.user_data_type != b.user_data_type
		// TODO: compare user_data[48] ?
		|| a.require_flags != b.require_flags
		|| a.deny_flags != b.deny_flags) {
		return false;
	}
 
	switch (a.type) {
		case B_MEDIA_RAW_AUDIO:
			return a.u.raw_audio == b.u.raw_audio;
 
		case B_MEDIA_RAW_VIDEO:
			return a.u.raw_video == b.u.raw_video;
 
		case B_MEDIA_MULTISTREAM:
			return a.u.multistream == b.u.multistream;
 
		case B_MEDIA_ENCODED_AUDIO:
			return a.u.encoded_audio == b.u.encoded_audio;
 
		case B_MEDIA_ENCODED_VIDEO:
			return a.u.encoded_video == b.u.encoded_video;
 
		default:
			return true; // TODO: really?
	}
}
 
 
// #pragma mark -
 
 
/*! return \c true if a and b are compatible (accounting for wildcards)
	a is the format you want to feed to something accepting b
*/
bool
format_is_compatible(const media_format& a, const media_format& b)
{
	return a.Matches(&b);
}
 
 
bool
string_for_format(const media_format& f, char* buf, size_t size)
{
	char encoding[10]; /* maybe Be wanted to use some 4CCs ? */
	const char* videoOrientation = "0"; /* I'd use "NC", R5 uses 0. */
 
	if (buf == NULL)
		return false;
	switch (f.type) {
	case B_MEDIA_RAW_AUDIO:
		snprintf(buf, size,
			"raw_audio;%g;%" B_PRIu32 ";0x%" B_PRIx32 ";%" B_PRIu32 ";0x%"
				B_PRIxSIZE ";0x%#" B_PRIx32 ";%d;0x%04x",
			f.u.raw_audio.frame_rate,
			f.u.raw_audio.channel_count,
			f.u.raw_audio.format,
			f.u.raw_audio.byte_order,
			f.u.raw_audio.buffer_size,
			f.u.raw_audio.channel_mask,
			f.u.raw_audio.valid_bits,
			f.u.raw_audio.matrix_mask);
		return true;
	case B_MEDIA_RAW_VIDEO:
		if (f.u.raw_video.orientation == B_VIDEO_TOP_LEFT_RIGHT)
			videoOrientation = "TopLR";
		else if (f.u.raw_video.orientation == B_VIDEO_BOTTOM_LEFT_RIGHT)
			videoOrientation = "BotLR";
		snprintf(buf, size, "raw_video;%g;0x%x;%" B_PRIu32 ";%" B_PRIu32 ";%"
				B_PRIu32 ";%" B_PRIu32 ";%s;%d;%d",
			f.u.raw_video.field_rate,
			f.u.raw_video.display.format,
			f.u.raw_video.interlace,
			f.u.raw_video.display.line_width,
			f.u.raw_video.display.line_count,
			f.u.raw_video.first_active,
			videoOrientation,
			f.u.raw_video.pixel_width_aspect,
			f.u.raw_video.pixel_height_aspect);
		return true;
	case B_MEDIA_ENCODED_AUDIO:
		snprintf(encoding, 10, "%d", f.u.encoded_audio.encoding);
		snprintf(buf, size,
			"caudio;%s;%g;%ld;(%g;%" B_PRIu32 ";0x%" B_PRIx32 ";%" B_PRIu32
				";0x%" B_PRIxSIZE ";0x%08" B_PRIx32 ";%d;0x%04x)",
			encoding,
			f.u.encoded_audio.bit_rate,
			f.u.encoded_audio.frame_size,
			f.u.encoded_audio.output.frame_rate,
			f.u.encoded_audio.output.channel_count,
			f.u.encoded_audio.output.format,
			f.u.encoded_audio.output.byte_order,
			f.u.encoded_audio.output.buffer_size,
			f.u.encoded_audio.multi_info.channel_mask,
			f.u.encoded_audio.multi_info.valid_bits,
			f.u.encoded_audio.multi_info.matrix_mask);
		return true;
	case B_MEDIA_ENCODED_VIDEO:
		snprintf(encoding, 10, "%d", f.u.encoded_video.encoding);
		if (f.u.encoded_video.output.orientation == B_VIDEO_TOP_LEFT_RIGHT)
			videoOrientation = "TopLR";
		else if (f.u.encoded_video.output.orientation == B_VIDEO_BOTTOM_LEFT_RIGHT)
			videoOrientation = "BotLR";
		snprintf(buf, size,
			"cvideo;%s;%g;%g;%" B_PRIuSIZE ";(%g;0x%x;%" B_PRIu32 ";%" B_PRIu32
				";%" B_PRIu32 ";%" B_PRIu32 ";%s;%d;%d)",
			encoding,
			f.u.encoded_video.avg_bit_rate,
			f.u.encoded_video.max_bit_rate,
			f.u.encoded_video.frame_size,
			f.u.encoded_video.output.field_rate,
			f.u.encoded_video.output.display.format,
			f.u.encoded_video.output.interlace,
			f.u.encoded_video.output.display.line_width,
			f.u.encoded_video.output.display.line_count,
			f.u.encoded_video.output.first_active,
			videoOrientation,
			f.u.encoded_video.output.pixel_width_aspect,
			f.u.encoded_video.output.pixel_height_aspect);
		return true;
	default:
		snprintf(buf, size, "%d-", f.type);
		unsigned char* p = (unsigned char*)&(f.u);
		size -= strlen(buf);
		buf += strlen(buf);
		for (int i = 0; (size > 2) && (i < 96); i++) {
			snprintf(buf, 3, "%2.2x", *(p + i));
			buf+=2;
			size-=2;
		}
		return true; // ?
	}
	return false;
}
 
 
// #pragma mark -
 
 
bool
operator==(const media_file_format_id& a, const media_file_format_id& b)
{
	return a.node == b.node && a.device == b.device
		&& a.internal_id == b.internal_id;
}
 
 
bool
operator<(const media_file_format_id& a, const media_file_format_id& b)
{
	return a.internal_id < b.internal_id;
}
 
 
// #pragma mark -
 
 
//! Use this function to iterate through available file format writers.
status_t
get_next_file_format(int32* cookie, media_file_format* mff)
{
	if (cookie == NULL || mff == NULL)
		return B_BAD_VALUE;
 
	status_t ret = AddOnManager::GetInstance()->GetFileFormat(mff, *cookie);
	if (ret != B_OK)
		return ret;
 
	*cookie = *cookie + 1;
 
	return B_OK;
}
 
 
// #pragma mark -
 
 
// final & verified
const char* B_MEDIA_SERVER_SIGNATURE = "application/x-vnd.Be.media-server";
const char* B_MEDIA_ADDON_SERVER_SIGNATURE = "application/x-vnd.Be.addon-host";
 
const type_code B_CODEC_TYPE_INFO = 0x040807b2;
 
 
// #pragma mark -
 
 
// shutdown_media_server() and launch_media_server()
// are provided by libbe.so in BeOS R5
 
#define MEDIA_SERVICE_NOTIFICATION_ID "MediaServiceNotificationID"
 
 
void
notify_system(float progress, const char* message)
{
	BNotification notification(B_PROGRESS_NOTIFICATION);
	notification.SetMessageID(MEDIA_SERVICE_NOTIFICATION_ID);
	notification.SetProgress(progress);
	notification.SetGroup(B_TRANSLATE("Media Service"));
	notification.SetContent(message);
 
	app_info info;
	be_app->GetAppInfo(&info);
	BBitmap icon(BRect(0, 0, 32, 32), B_RGBA32);
	BNode node(&info.ref);
	BIconUtils::GetVectorIcon(&node, "BEOS:ICON", &icon);
	notification.SetIcon(&icon);
 
	notification.Send();
}
 
 
void
progress_shutdown(int stage,
	bool (*progress)(int stage, const char* message, void* cookie),
	void* cookie)
{
	// parameter "message" is no longer used. It is kept for compatibility with
	// BeOS as this is used as a shutdown_media_server callback.
 
	TRACE("stage: %i\n", stage);
	const char* string = "Unknown stage";
	switch (stage) {
		case 10:
			string = B_TRANSLATE("Stopping media server" B_UTF8_ELLIPSIS);
			break;
		case 20:
			string = B_TRANSLATE("Waiting for media_server to quit.");
			break;
		case 40:
			string = B_TRANSLATE("Telling media_addon_server to quit.");
			break;
		case 50:
			string = B_TRANSLATE("Waiting for media_addon_server to quit.");
			break;
		case 70:
			string = B_TRANSLATE("Cleaning up.");
			break;
		case 100:
			string = B_TRANSLATE("Done shutting down.");
			break;
	}
 
	if (progress == NULL)
		notify_system(stage / 100.0f, string);
	else
		progress(stage, string, cookie);
}
 
 
status_t
shutdown_media_server(bigtime_t timeout,
	bool (*progress)(int stage, const char* message, void* cookie),
	void* cookie)
{
	BMessage msg(B_QUIT_REQUESTED);
	status_t err = B_MEDIA_SYSTEM_FAILURE;
	bool shutdown = false;
 
	BMediaRoster* roster = BMediaRoster::Roster(&err);
	if (roster == NULL || err != B_OK)
		return err;
 
	if (progress == NULL && roster->Lock()) {
		MediaRosterEx(roster)->EnableLaunchNotification(true, true);
		roster->Unlock();
	}
 
	if ((err = msg.AddBool("be:_user_request", true)) != B_OK)
		return err;
 
	team_id mediaServer = be_roster->TeamFor(B_MEDIA_SERVER_SIGNATURE);
	team_id addOnServer = be_roster->TeamFor(B_MEDIA_ADDON_SERVER_SIGNATURE);
 
	if (mediaServer != B_ERROR) {
		BMessage reply;
		BMessenger messenger(B_MEDIA_SERVER_SIGNATURE, mediaServer);
		progress_shutdown(10, progress, cookie);
 
		err = messenger.SendMessage(&msg, &reply, 2000000, 2000000);
		reply.FindBool("_shutdown", &shutdown);
		if (err == B_TIMED_OUT || shutdown == false) {
			if (messenger.IsValid())
				kill_team(mediaServer);
		} else if (err != B_OK)
			return err;
 
		progress_shutdown(20, progress, cookie);
 
		int32 rv;
		if (reply.FindInt32("error", &rv) == B_OK && rv != B_OK)
			return rv;
	}
 
	if (addOnServer != B_ERROR) {
		shutdown = false;
		BMessage reply;
		BMessenger messenger(B_MEDIA_ADDON_SERVER_SIGNATURE, addOnServer);
		progress_shutdown(40, progress, cookie);
 
		// The media_server usually shutdown the media_addon_server,
		// if not let's do something.
		if (messenger.IsValid()) {
			err = messenger.SendMessage(&msg, &reply, 2000000, 2000000);
			reply.FindBool("_shutdown", &shutdown);
			if (err == B_TIMED_OUT || shutdown == false) {
				if (messenger.IsValid())
					kill_team(addOnServer);
			} else if (err != B_OK)
				return err;
 
			progress_shutdown(50, progress, cookie);
 
			int32 rv;
			if (reply.FindInt32("error", &rv) == B_OK && rv != B_OK)
				return rv;
		}
	}
 
	progress_shutdown(100, progress, cookie);
	return B_OK;
}
 
 
void
progress_startup(int stage,
	bool (*progress)(int stage, const char* message, void* cookie),
	void* cookie)
{
	// parameter "message" is no longer used. It is kept for compatibility with
	// BeOS as this is used as a shutdown_media_server callback.
 
	TRACE("stage: %i\n", stage);
	const char* string = "Unknown stage";
	switch (stage) {
		case 10:
			string = B_TRANSLATE("Stopping media server" B_UTF8_ELLIPSIS);
			break;
		case 20:
			string = B_TRANSLATE("Stopping media_addon_server.");
			break;
		case 50:
			string = B_TRANSLATE("Starting media_services.");
			break;
		case 90:
			string = B_TRANSLATE("Error occurred starting media services.");
			break;
		case 100:
			string = B_TRANSLATE("Ready for use.");
			break;
	}
 
	if (progress == NULL)
		notify_system(stage / 100.0f, string);
	else
		progress(stage, string, cookie);
}
 
 
status_t
launch_media_server(bigtime_t timeout,
	bool (*progress)(int stage, const char* message, void* cookie),
	void* cookie, uint32 flags)
{
	if (BMediaRoster::IsRunning())
		return B_ALREADY_RUNNING;
 
	status_t err = B_MEDIA_SYSTEM_FAILURE;
	BMediaRoster* roster = BMediaRoster::Roster(&err);
	if (roster == NULL || err != B_OK)
		return err;
 
	if (progress == NULL && roster->Lock()) {
		MediaRosterEx(roster)->EnableLaunchNotification(true, true);
		roster->Unlock();
	}
 
	// The media_server crashed
	if (be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE)) {
		progress_startup(10, progress, cookie);
		kill_team(be_roster->TeamFor(B_MEDIA_ADDON_SERVER_SIGNATURE));
	}
 
	// The media_addon_server crashed
	if (be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)) {
		progress_startup(20, progress, cookie);
		kill_team(be_roster->TeamFor(B_MEDIA_SERVER_SIGNATURE));
	}
 
	progress_startup(50, progress, cookie);
 
	err = BLaunchRoster().Start(B_MEDIA_SERVER_SIGNATURE);
 
	if (err != B_OK)
		progress_startup(90, progress, cookie);
	else if (progress != NULL) {
		progress_startup(100, progress, cookie);
		err = B_OK;
	}
 
	return err;
}
 
 
// #pragma mark - media_encode_info
 
 
media_encode_info::media_encode_info()
{
	flags = 0;
	used_data_size = 0;
	start_time = 0;
	time_to_encode = INT64_MAX;
	file_format_data = NULL;
	file_format_data_size = 0;
	codec_data = NULL;
	codec_data_size = 0;
}
 
 
media_decode_info::media_decode_info()
{
	time_to_decode = INT64_MAX;
	file_format_data = NULL;
	file_format_data_size = 0;
	codec_data = NULL;
	codec_data_size = 0;
}
 
 

V547 Expression 'format->encoding == 0' is always true.

V547 Expression 'format->encoding == 0' is always true.