1119 lines
27 KiB
C++
1119 lines
27 KiB
C++
/*
|
|
* 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(¶meters);
|
|
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(¶meters);
|
|
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(¶meters);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|