root/src/kits/media/MediaTrack.cpp
/*
 * Copyright 2002-2007, Marcus Overhagen <marcus@overhagen.de>
 * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de>
 * Copyright 2013, Haiku, Inc. All Rights Reserved.
 * All rights reserved. Distributed under the terms of the MIT license.
 *
 * Authors:
 *              Stephan Aßmus, superstippi@gmx.de
 *              Marcus Overhagen, marcus@overhagen.de
 */


#include <MediaTrack.h>

#include <new>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Roster.h>

#include "MediaExtractor.h"
#include "MediaWriter.h"
#include "PluginManager.h"


//#define TRACE_MEDIA_TRACK
#ifdef TRACE_MEDIA_TRACK
#       ifndef TRACE
#               define TRACE printf
#       endif
#else
#       ifndef TRACE
#               define TRACE(a...)
#       endif
#endif

#define ERROR(a...) fprintf(stderr, a)


#define CONVERT_TO_INT32 0
        // TODO: Test! This triggers a few bugs!

// flags used for workarounds
enum {
        FORCE_RAW_AUDIO                                 = 0x0001,
        FORCE_RAW_VIDEO                                 = 0x0002,
        FORCE_RAW_AUDIO_INT16_FORMAT    = 0x0010,
        FORCE_RAW_AUDIO_INT32_FORMAT    = 0x0020,
        FORCE_RAW_AUDIO_FLOAT_FORMAT    = 0x0040,
        FORCE_RAW_AUDIO_HOST_ENDIAN     = 0x0100,
        IGNORE_ENCODED_AUDIO                    = 0x1000,
        IGNORE_ENCODED_VIDEO                    = 0x2000,
};

#define B_MEDIA_DISABLE_FORMAT_TRANSLATION 0x4000
        // TODO: move this (after name change?) to MediaDefs.h


class RawDecoderChunkProvider : public ChunkProvider {
public:
                                                        RawDecoderChunkProvider(Decoder* decoder,
                                                                int buffer_size, int frame_size);
        virtual                                 ~RawDecoderChunkProvider();

                        status_t                GetNextChunk(const void** chunkBuffer,
                                                                size_t* chunkSize,
                                                                media_header* mediaHeader);

private:
                        Decoder*                fDecoder;
                        void*                   fBuffer;
                        int                             fBufferSize;
                        int                             fFrameSize;
};


/*************************************************************
 * protected BMediaTrack
 *************************************************************/

BMediaTrack::~BMediaTrack()
{
        CALLED();

        gPluginManager.DestroyDecoder(fRawDecoder);
        gPluginManager.DestroyDecoder(fDecoder);
        gPluginManager.DestroyEncoder(fEncoder);
}

/*************************************************************
 * public BMediaTrack
 *************************************************************/

status_t
BMediaTrack::InitCheck() const
{
        CALLED();

        return fInitStatus;
}


status_t
BMediaTrack::GetCodecInfo(media_codec_info* _codecInfo) const
{
        CALLED();

        if (fDecoder == NULL)
                return B_NO_INIT;

        *_codecInfo = fCodecInfo;
        strlcpy(_codecInfo->pretty_name, fCodecInfo.pretty_name,
                sizeof(_codecInfo->pretty_name));

        return B_OK;
}


status_t
BMediaTrack::EncodedFormat(media_format* _format) const
{
        CALLED();

        if (_format == NULL)
                return B_BAD_VALUE;

        if (fExtractor == NULL)
                return B_NO_INIT;

        *_format = *fExtractor->EncodedFormat(fStream);

#ifdef TRACE_MEDIA_TRACK
        char s[200];
        string_for_format(*_format, s, sizeof(s));
        printf("BMediaTrack::EncodedFormat: %s\n", s);
#endif

        return B_OK;
}


// for BeOS R5 compatibility
extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
        BMediaTrack* self, media_format* _format);

status_t DecodedFormat__11BMediaTrackP12media_format(BMediaTrack* self,
        media_format* _format)
{
        return self->DecodedFormat(_format, 0);
}


status_t
BMediaTrack::DecodedFormat(media_format* _format, uint32 flags)
{
        CALLED();

        if (_format == NULL)
                return B_BAD_VALUE;

        if (fExtractor == NULL || fDecoder == NULL)
                return B_NO_INIT;

        gPluginManager.DestroyDecoder(fRawDecoder);
        fRawDecoder = NULL;

#ifdef TRACE_MEDIA_TRACK
        char s[200];
        string_for_format(*_format, s, sizeof(s));
        printf("BMediaTrack::DecodedFormat: req1: %s\n", s);
#endif

        if ((fWorkaroundFlags & FORCE_RAW_AUDIO)
                || ((fWorkaroundFlags & IGNORE_ENCODED_AUDIO)
                        && _format->type == B_MEDIA_ENCODED_AUDIO)) {
                _format->type = B_MEDIA_RAW_AUDIO;
                _format->u.raw_audio = media_multi_audio_format::wildcard;
        }
        if ((fWorkaroundFlags & FORCE_RAW_VIDEO)
                || ((fWorkaroundFlags & IGNORE_ENCODED_VIDEO)
                        && _format->type == B_MEDIA_ENCODED_VIDEO)) {
                _format->type = B_MEDIA_RAW_VIDEO;
                _format->u.raw_video = media_raw_video_format::wildcard;
        }
        if (_format->type == B_MEDIA_RAW_AUDIO) {
                if (fWorkaroundFlags & FORCE_RAW_AUDIO_HOST_ENDIAN)
                        _format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;

                if (fWorkaroundFlags & FORCE_RAW_AUDIO_INT16_FORMAT) {
                        _format->u.raw_audio.format
                                = media_raw_audio_format::B_AUDIO_SHORT;
                }
                if (fWorkaroundFlags & FORCE_RAW_AUDIO_INT32_FORMAT) {
                        _format->u.raw_audio.format
                                = media_raw_audio_format::B_AUDIO_INT;
                }
                if (fWorkaroundFlags & FORCE_RAW_AUDIO_FLOAT_FORMAT) {
                        _format->u.raw_audio.format
                                = media_raw_audio_format::B_AUDIO_FLOAT;
                }
        }

#ifdef TRACE_MEDIA_TRACK
        string_for_format(*_format, s, sizeof(s));
        printf("BMediaTrack::DecodedFormat: req2: %s\n", s);
#endif

        fFormat = *_format;
        status_t result = fDecoder->NegotiateOutputFormat(_format);

#ifdef TRACE_MEDIA_TRACK
        string_for_format(*_format, s, sizeof(s));
        printf("BMediaTrack::DecodedFormat: nego: %s\n", s);
#endif

        if (_format->type == 0) {
#ifdef TRACE_MEDIA_TRACK
                printf("BMediaTrack::DecodedFormat: Decoder didn't set output format "
                        "type.\n");
#endif
        }

        if (_format->type == B_MEDIA_RAW_AUDIO) {
                if (_format->u.raw_audio.byte_order == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
                                "output byte order.\n");
#endif
                }
                if (_format->u.raw_audio.format == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
                                "output sample format.\n");
#endif
                }
                if (_format->u.raw_audio.buffer_size <= 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
                                "output buffer size.\n");
#endif
                }
        }

        if (_format->type == B_MEDIA_RAW_VIDEO) {
                if (_format->u.raw_video.display.format == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
                                "output color space.\n");
#endif
                }
                if (_format->u.raw_video.display.line_width == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
                                "output line_width.\n");
#endif
                }
                if (_format->u.raw_video.display.line_count == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
                                "output line_count.\n");
#endif
                }
                if (_format->u.raw_video.display.bytes_per_row == 0) {
#ifdef TRACE_MEDIA_TRACK
                        printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
                                "output bytes_per_row.\n");
#endif
                }
        }

        if ((flags & B_MEDIA_DISABLE_FORMAT_TRANSLATION) == 0) {
                if (fFormat.type == B_MEDIA_RAW_AUDIO
                        && _format->type == B_MEDIA_RAW_AUDIO
                        && fFormat.u.raw_audio.format != 0
                        && fFormat.u.raw_audio.format != _format->u.raw_audio.format) {
                        if (SetupFormatTranslation(*_format, &fFormat))
                                *_format = fFormat;
                }
        }

        fFormat = *_format;

//      string_for_format(*_format, s, sizeof(s));
//      printf("BMediaTrack::DecodedFormat: status: %s\n", s);
        return result;
}


status_t
BMediaTrack::GetMetaData(BMessage* _data) const
{
        CALLED();

        if (fExtractor == NULL)
                return B_NO_INIT;

        if (_data == NULL)
                return B_BAD_VALUE;

        _data->MakeEmpty();

        return fExtractor->GetStreamMetaData(fStream, _data);
}


int64
BMediaTrack::CountFrames() const
{
        CALLED();

        int64 frames = fExtractor ? fExtractor->CountFrames(fStream) : 0;
//      printf("BMediaTrack::CountFrames: %lld\n", frames);
        return frames;
}


bigtime_t
BMediaTrack::Duration() const
{
        CALLED();

        bigtime_t duration = fExtractor ? fExtractor->Duration(fStream) : 0;
//      printf("BMediaTrack::Duration: %lld\n", duration);
        return duration;
}


int64
BMediaTrack::CurrentFrame() const
{
        return fCurrentFrame;
}


bigtime_t
BMediaTrack::CurrentTime() const
{
        return fCurrentTime;
}

// BMediaTrack::ReadFrames(char*, long long*, media_header*)
// Compatibility for R5 and below. Required by Corum III and Civ:CTP.
#if __GNUC__ < 3

extern "C" status_t
ReadFrames__11BMediaTrackPcPxP12media_header(BMediaTrack* self,
        char* _buffer, int64* _frameCount, media_header* header)
{
        return self->ReadFrames(_buffer, _frameCount, header, 0);
}

#endif  // __GNUC__ < 3

status_t
BMediaTrack::ReadFrames(void* buffer, int64* _frameCount, media_header* header)
{
        return ReadFrames(buffer, _frameCount, header, NULL);
}


status_t
BMediaTrack::ReadFrames(void* buffer, int64* _frameCount,
        media_header* _header, media_decode_info* info)
{
//      CALLED();

        if (fDecoder == NULL)
                return B_NO_INIT;

        if (buffer == NULL || _frameCount == NULL)
                return B_BAD_VALUE;

        media_header header;
        if (_header == NULL)
                _header = &header;

        // Always clear the header first, as the decoder may not set all fields.
        memset(_header, 0, sizeof(media_header));

        status_t result = fRawDecoder != NULL
                ? fRawDecoder->Decode(buffer, _frameCount, _header, info)
                : fDecoder->Decode(buffer, _frameCount, _header, info);

        if (result == B_OK) {
                fCurrentFrame += *_frameCount;
                bigtime_t framesDuration = (bigtime_t)(*_frameCount * 1000000
                        / _FrameRate());
                fCurrentTime = _header->start_time + framesDuration;
#if 0
        // This debug output shows drift between calculated fCurrentFrame and
        // time-based current frame, if there is any.
        if (fFormat.type == B_MEDIA_RAW_AUDIO) {
                printf("current frame: %lld / calculated: %lld (%.2f/%.2f)\r",
                        fCurrentFrame,
                        int64(fCurrentTime * _FrameRate() / 1000000.0 + 0.5),
                        fCurrentTime / 1000000.0, (float)fCurrentFrame / _FrameRate());
                fflush(stdout);
        }
#endif
        } else {
                ERROR("BMediaTrack::ReadFrames: decoder returned error %#" B_PRIx32
                        " (%s)\n", result, strerror(result));
                *_frameCount = 0;
        }

#if 0
        PRINT(1, "BMediaTrack::ReadFrames: stream %ld, start-time %5Ld.%06Ld, "
                "%lld frames\n", fStream,  _header->start_time / 1000000,
                _header->start_time % 1000000, *out_frameCount);
#endif

        return result;
}


status_t
BMediaTrack::ReplaceFrames(const void* inBuffer, int64* _frameCount,
        const media_header* header)
{
        UNIMPLEMENTED();

        // TODO: Actually, a file is either open for reading or writing at the
        // moment. Since the chunk size of encoded media data will change,
        // implementing this call will only be possible for raw media tracks.

        return B_NOT_SUPPORTED;
}


status_t
BMediaTrack::SeekToTime(bigtime_t* _time, int32 flags)
{
        CALLED();

        if (fDecoder == NULL || fExtractor == NULL)
                return B_NO_INIT;

        if (_time == NULL)
                return B_BAD_VALUE;

        // Make sure flags are valid
        flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_TIME;

        #if DEBUG
        bigtime_t requestedTime = *_time;
        #endif
        int64 frame = 0;

        status_t result = fExtractor->Seek(fStream, flags, &frame, _time);
        if (result != B_OK) {
                ERROR("BMediaTrack::SeekToTime: extractor seek failed\n");
                return result;
        }

        result = fDecoder->SeekedTo(frame, *_time);
        if (result != B_OK) {
                ERROR("BMediaTrack::SeekToTime: decoder seek failed\n");
                return result;
        }

        if (fRawDecoder != NULL) {
                result = fRawDecoder->SeekedTo(frame, *_time);
                if (result != B_OK) {
                        ERROR("BMediaTrack::SeekToTime: raw decoder seek failed\n");
                        return result;
                }
        }

        fCurrentFrame = frame;
        fCurrentTime = *_time;

        PRINT(1, "BMediaTrack::SeekToTime finished, requested %.6f, result %.6f\n",
                requestedTime / 1000000.0, *_time / 1000000.0);

        return B_OK;
}


status_t
BMediaTrack::SeekToFrame(int64* _frame, int32 flags)
{
        CALLED();

        if (fDecoder == NULL || fExtractor == NULL)
                return B_NO_INIT;

        if (_frame == NULL)
                return B_BAD_VALUE;

        // Make sure flags are valid
        flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_FRAME;

        #if DEBUG
        int64 requestedFrame = *_frame;
        #endif
        bigtime_t time = 0;

        status_t result = fExtractor->Seek(fStream, flags, _frame, &time);
        if (result != B_OK) {
                ERROR("BMediaTrack::SeekToFrame: extractor seek failed\n");
                return result;
        }

        result = fDecoder->SeekedTo(*_frame, time);
        if (result != B_OK) {
                ERROR("BMediaTrack::SeekToFrame: decoder seek failed\n");
                return result;
        }

        if (fRawDecoder != NULL) {
                result = fRawDecoder->SeekedTo(*_frame, time);
                if (result != B_OK) {
                        ERROR("BMediaTrack::SeekToFrame: raw decoder seek failed\n");
                        return result;
                }
        }

        fCurrentFrame = *_frame;
        fCurrentTime = time;

        PRINT(1, "BMediaTrack::SeekToTime SeekToFrame, requested %lld, "
                "result %lld\n", requestedFrame, *_frame);

        return B_OK;
}


status_t
BMediaTrack::FindKeyFrameForTime(bigtime_t* _time, int32 flags) const
{
        CALLED();

        if (fExtractor == NULL)
                return B_NO_INIT;

        if (_time == NULL)
                return B_BAD_VALUE;

        // Make sure flags are valid
        flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_TIME;

        int64 frame = 0;
                // dummy frame, will be ignored because of flags
        status_t result = fExtractor->FindKeyFrame(fStream, flags, &frame, _time);
        if (result != B_OK) {
                ERROR("BMediaTrack::FindKeyFrameForTime: extractor seek failed: %s\n",
                        strerror(result));
        }

        return result;
}


status_t
BMediaTrack::FindKeyFrameForFrame(int64* _frame, int32 flags) const
{
        CALLED();

        if (fExtractor == NULL)
                return B_NO_INIT;

        if (_frame == NULL)
                return B_BAD_VALUE;

        // Make sure flags are valid
        flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_FRAME;

        bigtime_t time = 0;
                // dummy time, will be ignored because of flags
        status_t result = fExtractor->FindKeyFrame(fStream, flags, _frame, &time);
        if (result != B_OK) {
                ERROR("BMediaTrack::FindKeyFrameForFrame: extractor seek failed: %s\n",
                        strerror(result));
        }

        return result;
}


status_t
BMediaTrack::ReadChunk(char** _buffer, int32* _size, media_header* _header)
{
        CALLED();

        if (fExtractor == NULL)
                return B_NO_INIT;

        if (_buffer == NULL || _size == NULL)
                return B_BAD_VALUE;

        media_header header;
        if (_header == NULL)
                _header = &header;

        // Always clear the header first, as the extractor may not set all fields.
        memset(_header, 0, sizeof(media_header));

        const void* buffer;
        size_t size;
        status_t result = fExtractor->GetNextChunk(fStream, &buffer, &size,
                _header);

        if (result == B_OK) {
                *_buffer = const_cast<char*>(static_cast<const char*>(buffer));
                        // TODO: Change the pointer type when we break the API.
                *_size = size;
                // TODO: This changes the meaning of fCurrentTime from pointing
                // to the next chunk start time (i.e. after seeking) to the start time
                // of the last chunk. Asking the extractor for the current time will
                // not work so well because of the chunk cache. But providing a
                // "duration" field in the media_header could be useful.
                fCurrentTime = _header->start_time;
                fCurrentFrame = (int64)(fCurrentTime * _FrameRate() / 1000000LL);

        }

        return result;
}


status_t
BMediaTrack::AddCopyright(const char* copyright)
{
        if (fWriter == NULL)
                return B_NO_INIT;

        return fWriter->SetCopyright(fStream, copyright);
}


status_t
BMediaTrack::AddTrackInfo(uint32 code, const void* data, size_t size,
        uint32 flags)
{
        if (fWriter == NULL)
                return B_NO_INIT;

        return fWriter->AddTrackInfo(fStream, code, data, size, flags);
}


status_t
BMediaTrack::WriteFrames(const void* data, int32 frameCount, int32 flags)
{
        media_encode_info encodeInfo;
        encodeInfo.flags = flags;

        return WriteFrames(data, frameCount, &encodeInfo);
}


status_t
BMediaTrack::WriteFrames(const void* data, int64 frameCount,
        media_encode_info* info)
{
        if (fEncoder == NULL)
                return B_NO_INIT;

        return fEncoder->Encode(data, frameCount, info);
}


status_t
BMediaTrack::WriteChunk(const void* data, size_t size, uint32 flags)
{
        media_encode_info encodeInfo;
        encodeInfo.flags = flags;

        return WriteChunk(data, size, &encodeInfo);
}


status_t
BMediaTrack::WriteChunk(const void* data, size_t size, media_encode_info* info)
{
        if (fWriter == NULL)
                return B_NO_INIT;

        return fWriter->WriteChunk(fStream, data, size, info);
}


status_t
BMediaTrack::Flush()
{
        if (fWriter == NULL)
                return B_NO_INIT;

        return fWriter->Flush();
}


// deprecated BeOS R5 API
BParameterWeb*
BMediaTrack::Web()
{
        BParameterWeb* web;
        if (GetParameterWeb(&web) == B_OK)
                return web;

        return NULL;
}


status_t
BMediaTrack::GetParameterWeb(BParameterWeb** outWeb)
{
        if (outWeb == NULL)
                return B_BAD_VALUE;

        if (fEncoder == NULL)
                return B_NO_INIT;

        // TODO: This method is new in Haiku. The header mentions it returns a
        // copy. But how could it even do that? How can one clone a web and make
        // it point to the same BControllable?
        *outWeb = fEncoder->ParameterWeb();
        if (*outWeb != NULL)
                return B_OK;

        return B_NOT_SUPPORTED;
}


status_t
BMediaTrack::GetParameterValue(int32 id, void* value, size_t* size)
{
        if (value == NULL || size == NULL)
                return B_BAD_VALUE;

        if (fEncoder == NULL)
                return B_NO_INIT;

        return fEncoder->GetParameterValue(id, value, size);
}


status_t
BMediaTrack::SetParameterValue(int32 id, const void* value, size_t size)
{
        if (value == NULL || size == 0)
                return B_BAD_VALUE;

        if (fEncoder == NULL)
                return B_NO_INIT;

        return fEncoder->SetParameterValue(id, value, size);
}


BView*
BMediaTrack::GetParameterView()
{
        if (fEncoder == NULL)
                return NULL;

        return fEncoder->ParameterView();
}


status_t
BMediaTrack::GetQuality(float* quality)
{
        if (quality == NULL)
                return B_BAD_VALUE;

        encode_parameters parameters;
        status_t result = GetEncodeParameters(&parameters);
        if (result != B_OK)
                return result;

        *quality = parameters.quality;

        return B_OK;
}


status_t
BMediaTrack::SetQuality(float quality)
{
        encode_parameters parameters;
        status_t result = GetEncodeParameters(&parameters);
        if (result != B_OK)
                return result;

        if (quality < 0.0f)
                quality = 0.0f;

        if (quality > 1.0f)
                quality = 1.0f;

        parameters.quality = quality;

        return SetEncodeParameters(&parameters);
}


status_t
BMediaTrack::GetEncodeParameters(encode_parameters* parameters) const
{
        if (parameters == NULL)
                return B_BAD_VALUE;

        if (fEncoder == NULL)
                return B_NO_INIT;

        return fEncoder->GetEncodeParameters(parameters);
}


status_t
BMediaTrack::SetEncodeParameters(encode_parameters* parameters)
{
        if (parameters == NULL)
                return B_BAD_VALUE;

        if (fEncoder == NULL)
                return B_NO_INIT;

        return fEncoder->SetEncodeParameters(parameters);
}


status_t
BMediaTrack::Perform(int32 selector, void* data)
{
        return B_OK;
}

// #pragma mark - private


BMediaTrack::BMediaTrack(BPrivate::media::MediaExtractor* extractor,
        int32 stream)
{
        CALLED();

        fWorkaroundFlags = 0;
        fDecoder = NULL;
        fRawDecoder = NULL;
        fExtractor = extractor;
        fStream = stream;
        fInitStatus = B_OK;

        SetupWorkaround();

        status_t ret = fExtractor->CreateDecoder(fStream, &fDecoder, &fCodecInfo);
        if (ret != B_OK) {
                TRACE("BMediaTrack::BMediaTrack: Error: creating decoder failed: "
                        "%s\n", strerror(ret));
                // We do not set fInitStatus here, because ReadChunk should still work.
                fDecoder = NULL;
        }

        fCurrentFrame = 0;
        fCurrentTime = 0;

        // not used:
        fEncoder = NULL;
        fEncoderID = 0;
        fWriter = NULL;
}


BMediaTrack::BMediaTrack(BPrivate::media::MediaWriter* writer,
        int32 streamIndex, media_format* format,
        const media_codec_info* codecInfo)
{
        CALLED();

        fWorkaroundFlags = 0;
        fEncoder = NULL;
        fEncoderID = -1;
                // TODO: Not yet sure what this was needed for...
        fWriter = writer;
        fStream = streamIndex;
        fInitStatus = B_OK;

        SetupWorkaround();

        if (codecInfo != NULL) {
                status_t ret = fWriter->CreateEncoder(&fEncoder, codecInfo, format);
                if (ret != B_OK) {
                        TRACE("BMediaTrack::BMediaTrack: Error: creating decoder failed: "
                                "%s\n", strerror(ret));
                        // We do not set fInitStatus here, because WriteChunk should still
                        // work.
                        fEncoder = NULL;
                } else {
                        fCodecInfo = *codecInfo;
                        fInitStatus = fEncoder->SetUp(format);
                }
        }

        fFormat = *format;

        // not used:
        fCurrentFrame = 0;
        fCurrentTime = 0;
        fDecoder = NULL;
        fRawDecoder = NULL;
        fExtractor = NULL;
}


// Does nothing, returns B_ERROR, for Zeta compatiblity only
status_t
BMediaTrack::ControlCodec(int32 selector, void* io_data, size_t size)
{
        return B_ERROR;
}


void
BMediaTrack::SetupWorkaround()
{
        app_info        ainfo;
        thread_info     tinfo;

        get_thread_info(find_thread(0), &tinfo);
        be_roster->GetRunningAppInfo(tinfo.team, &ainfo);

        if (strcmp(ainfo.signature, "application/x-vnd.marcone-soundplay") == 0) {
                fWorkaroundFlags = FORCE_RAW_AUDIO | FORCE_RAW_AUDIO_INT16_FORMAT
                        | FORCE_RAW_AUDIO_HOST_ENDIAN;
                printf("BMediaTrack::SetupWorkaround: SoundPlay workaround active\n");
        }
        if (strcmp(ainfo.signature, "application/x-vnd.Be.MediaPlayer") == 0) {
                fWorkaroundFlags = IGNORE_ENCODED_AUDIO | IGNORE_ENCODED_VIDEO;
                printf("BMediaTrack::SetupWorkaround: MediaPlayer workaround active\n");
        }

#if CONVERT_TO_INT32
        // TODO: Test
        if (!(fWorkaroundFlags & FORCE_RAW_AUDIO_INT16_FORMAT))
                fWorkaroundFlags |= FORCE_RAW_AUDIO_INT32_FORMAT;
#endif
}


bool
BMediaTrack::SetupFormatTranslation(const media_format &from, media_format* to)
{
        gPluginManager.DestroyDecoder(fRawDecoder);
        fRawDecoder = NULL;

#ifdef TRACE_MEDIA_TRACK
        char s[200];
        string_for_format(from, s, sizeof(s));
        printf("BMediaTrack::SetupFormatTranslation: from: %s\n", s);
#endif

        status_t result = gPluginManager.CreateDecoder(&fRawDecoder, from);
        if (result != B_OK) {
                ERROR("BMediaTrack::SetupFormatTranslation: CreateDecoder failed\n");
                return false;
        }

        // XXX video?
        int buffer_size = from.u.raw_audio.buffer_size;
        int frame_size = (from.u.raw_audio.format & 15)
                * from.u.raw_audio.channel_count;
        media_format fromNotConst = from;

        ChunkProvider* chunkProvider
                = new (std::nothrow) RawDecoderChunkProvider(fDecoder, buffer_size,
                        frame_size);
        if (chunkProvider == NULL) {
                ERROR("BMediaTrack::SetupFormatTranslation: can't create chunk "
                        "provider\n");
                goto error;
        }
        fRawDecoder->SetChunkProvider(chunkProvider);

        result = fRawDecoder->Setup(&fromNotConst, 0, 0);
        if (result != B_OK) {
                ERROR("BMediaTrack::SetupFormatTranslation: Setup failed\n");
                goto error;
        }

#ifdef TRACE_MEDIA_TRACK
        string_for_format(*to, s, sizeof(s));
        printf("BMediaTrack::SetupFormatTranslation:   to: %s\n", s);
#endif

        result = fRawDecoder->NegotiateOutputFormat(to);
        if (result != B_OK) {
                ERROR("BMediaTrack::SetupFormatTranslation: NegotiateOutputFormat "
                        "failed\n");
                goto error;
        }

#ifdef TRACE_MEDIA_TRACK
        string_for_format(*to, s, sizeof(s));
        printf("BMediaTrack::SetupFormatTranslation:  result: %s\n", s);
#endif

        return true;

error:
        gPluginManager.DestroyDecoder(fRawDecoder);
        fRawDecoder = NULL;
        return false;
}


double
BMediaTrack::_FrameRate() const
{
        switch (fFormat.type) {
                case B_MEDIA_RAW_VIDEO:
                        return fFormat.u.raw_video.field_rate;
                case B_MEDIA_ENCODED_VIDEO:
                        return fFormat.u.encoded_video.output.field_rate;
                case B_MEDIA_RAW_AUDIO:
                        return fFormat.u.raw_audio.frame_rate;
                case B_MEDIA_ENCODED_AUDIO:
                        return fFormat.u.encoded_audio.output.frame_rate;
                default:
                        return 1.0;
        }
}

#if 0
// unimplemented
BMediaTrack::BMediaTrack()
BMediaTrack::BMediaTrack(const BMediaTrack &)
BMediaTrack &BMediaTrack::operator=(const BMediaTrack &)
#endif

status_t BMediaTrack::_Reserved_BMediaTrack_0(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_1(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_2(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_3(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_4(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_5(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_6(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_7(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_8(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_9(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_10(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_11(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_12(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_13(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_14(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_15(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_16(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_17(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_18(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_19(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_20(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_21(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_22(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_23(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_24(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_25(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_26(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_27(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_28(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_29(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_30(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_31(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_32(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_33(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_34(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_35(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_36(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_37(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_38(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_39(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_40(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_41(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_42(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_43(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_44(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_45(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_46(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_47(int32 arg, ...) { return B_ERROR; }


RawDecoderChunkProvider::RawDecoderChunkProvider(Decoder* decoder,
        int buffer_size, int frame_size)
{
//      printf("RawDecoderChunkProvider: buffer_size %d, frame_size %d\n",
//              buffer_size, frame_size);
        fDecoder = decoder;
        fFrameSize = frame_size;
        fBufferSize = buffer_size;
        fBuffer = malloc(buffer_size);
}


RawDecoderChunkProvider::~RawDecoderChunkProvider()
{
        free(fBuffer);
}


status_t
RawDecoderChunkProvider::GetNextChunk(const void** chunkBuffer,
        size_t* chunkSize, media_header* header)
{
        int64 frames;
        media_decode_info info;
        status_t result = fDecoder->Decode(fBuffer, &frames, header, &info);
        if (result == B_OK) {
                *chunkBuffer = fBuffer;
                *chunkSize = frames * fFrameSize;
//              printf("RawDecoderChunkProvider::GetNextChunk, %lld frames, "
//                      "%ld bytes, start-time %lld\n", frames, *chunkSize,
//                      header->start_time);
        } else
                ERROR("RawDecoderChunkProvider::GetNextChunk failed\n");

        return result;
}