root/src/kits/media/MediaDefs.cpp
/*
 * 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 <ControlLook.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 -


#if __GNUC__ == 2
const media_multi_audio_format media_raw_audio_format::wildcard
        = media_multi_audio_format();

const media_multi_audio_format media_multi_audio_format::wildcard
        = media_multi_audio_format();
#else
const media_multi_audio_format media_raw_audio_format::wildcard = {};

const media_multi_audio_format media_multi_audio_format::wildcard = {};
#endif

const media_encoded_audio_format media_encoded_audio_format::wildcard = {};

const media_video_display_info media_video_display_info::wildcard = {};

const media_raw_video_format media_raw_video_format::wildcard = {};

const media_encoded_video_format media_encoded_video_format::wildcard = {};

const media_multistream_format media_multistream_format::wildcard = {};


// #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(B_ORIGIN, be_control_look->ComposeIconSize(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)
{
        BLaunchRoster launchRoster;
        launchRoster.StopTarget(B_MEDIA_SERVER_SIGNATURE);

        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;
}