#include "../AbstractFileInterfaceNode.h"
#include "MediaReader.h"
#include "../misc.h"
#include "debug.h"
#include <Buffer.h>
#include <BufferGroup.h>
#include <BufferProducer.h>
#include <Controllable.h>
#include <Entry.h>
#include <Errors.h>
#include <File.h>
#include <FileInterface.h>
#include <MediaAddOn.h>
#include <MediaDefs.h>
#include <MediaEventLooper.h>
#include <MediaNode.h>
#include <MediaRoster.h>
#include <ParameterWeb.h>
#include <TimeSource.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
MediaReader::~MediaReader(void)
{
fprintf(stderr,"MediaReader::~MediaReader\n");
if (fBufferGroup != 0) {
BBufferGroup * group = fBufferGroup;
fBufferGroup = 0;
delete group;
}
}
MediaReader::MediaReader(
size_t defaultChunkSize,
float defaultBitRate,
const flavor_info * info,
BMessage * config,
BMediaAddOn * addOn)
: BMediaNode("MediaReader"),
BBufferProducer(B_MEDIA_MULTISTREAM),
AbstractFileInterfaceNode(defaultChunkSize, defaultBitRate, info, config, addOn)
{
CALLED();
fBufferGroup = 0;
fOutputEnabled = true;
strncpy(output.name,"MediaReader Output",B_MEDIA_NAME_LENGTH-1);
output.name[B_MEDIA_NAME_LENGTH-1] = '\0';
output.node = media_node::null;
output.source = media_source::null;
output.destination = media_destination::null;
GetFormat(&output.format);
}
void MediaReader::Preroll(void)
{
CALLED();
BMediaNode::Preroll();
}
status_t MediaReader::HandleMessage(
int32 message,
const void * data,
size_t size)
{
CALLED();
status_t status = B_OK;
switch (message) {
default:
status = BBufferProducer::HandleMessage(message,data,size);
if (status == B_OK) {
break;
}
status = AbstractFileInterfaceNode::HandleMessage(message,data,size);
break;
}
return status;
}
void MediaReader::NodeRegistered(void)
{
CALLED();
output.node = Node();
output.source.id = 0;
output.source.port = output.node.port;
AbstractFileInterfaceNode::NodeRegistered();
}
status_t MediaReader::SetRef(
const entry_ref & file,
bool create,
bigtime_t * out_time)
{
CALLED();
status_t status = AbstractFileInterfaceNode::SetRef(file,B_READ_ONLY,create,out_time);
if (status != B_OK) {
PRINT("AbstractFileInterfaceNode::SetRef returned an error\n");
return status;
}
if (output.destination == media_destination::null) {
GetFormat(&output.format);
AddRequirements(&output.format);
return B_OK;
}
media_format format;
GetFormat(&format);
AddRequirements(&format);
if (format_is_acceptible(format,output.format)) {
fprintf(stderr," compatible format = no re-negotiation necessary\n");
return B_OK;
}
BMediaRoster * roster = BMediaRoster::Roster(&status);
if (roster == 0)
return B_MEDIA_SYSTEM_FAILURE;
if (status != B_OK)
return status;
run_state destinationRunState = run_state(RunState());
if (destinationRunState == BMediaEventLooper::B_STARTED)
Stop(0,true);
run_state destinationRunState = destinationNode->??;
status = destinationNode->StopNode(??,0,true);
if (status != B_OK) {
return status;
} */
media_destination outputDestination = output.destination;
status = roster->Disconnect(output.source.id,output.source,
output.destination.id,output.destination);
if (status != B_OK)
return status;
media_output connectOutput;
media_input connectInput;
status = roster->Connect(output.source,outputDestination,
&format,&connectOutput,&connectInput);
if (status != B_OK)
return status;
if (destinationRunState == BMediaEventLooper::B_STARTED) {
Start(0);
}
return status;
}
status_t MediaReader::FormatSuggestionRequested(
media_type type,
int32 quality,
media_format * format)
{
CALLED();
if ((type != B_MEDIA_MULTISTREAM) && (type != B_MEDIA_UNKNOWN_TYPE)) {
PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
GetFormat(format);
AddRequirements(format);
return B_OK;
}
status_t MediaReader::FormatProposal(
const media_source & output_source,
media_format * format)
{
CALLED();
if (output.source != output_source) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
fprintf(stderr,"proposed format: ");
print_media_format(format);
fprintf(stderr,"\n");
fprintf(stderr,"my format: ");
print_media_format(myFormat);
fprintf(stderr,"\n"); */
media_format myFormat;
GetFormat(&myFormat);
if (!format_is_acceptible(*format,myFormat)) {
PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
AddRequirements(format);
return B_OK;
}
status_t MediaReader::FormatChangeRequested(
const media_source & source,
const media_destination & destination,
media_format * io_format,
int32 * _deprecated_)
{
CALLED();
if (output.source != source) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
status_t status = FormatProposal(source,io_format);
if (status != B_OK) {
PRINT("\terror returned by FormatProposal\n");
GetFormat(io_format);
return status;
}
return ResolveWildcards(io_format);
}
status_t MediaReader::GetNextOutput(
int32 * cookie,
media_output * out_output)
{
CALLED();
if (*cookie != 0) {
PRINT("\t<- B_ERROR (no more outputs)\n");
return B_ERROR;
}
*cookie = 1;
*out_output = output;
return B_OK;
}
status_t MediaReader::DisposeOutputCookie(
int32 cookie)
{
CALLED();
return B_OK;
}
status_t MediaReader::SetBufferGroup(
const media_source & for_source,
BBufferGroup * group)
{
CALLED();
if (output.source != for_source) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
if (fBufferGroup != 0) {
if (fBufferGroup == group)
return B_OK;
delete fBufferGroup;
}
if (group != 0) {
fBufferGroup = group;
} else {
media_node_id id;
FindLatencyFor(output.destination, &fDownstreamLatency, &id);
if (fBufferPeriod <= 0) {
fprintf(stderr,"<- B_NO_INIT");
return B_NO_INIT;
}
int32 count = int32(fDownstreamLatency/fBufferPeriod)+2;
PRINT("\tdownstream latency = %lld, buffer period = %lld, buffer count = %ld\n",
fDownstreamLatency, fBufferPeriod, count);
fBufferGroup = new BBufferGroup(output.format.u.multistream.max_chunk_size,count);
if (fBufferGroup == 0) {
PRINT("\t<- B_NO_MEMORY\n");
return B_NO_MEMORY;
}
status_t status = fBufferGroup->InitCheck();
if (status != B_OK) {
PRINT("\t<- fBufferGroup initialization failed\n");
return status;
}
}
return B_OK;
}
status_t MediaReader::VideoClippingChanged(
const media_source & for_source,
int16 num_shorts,
int16 * clip_data,
const media_video_display_info & display,
int32 * _deprecated_)
{
return BBufferProducer::VideoClippingChanged(for_source,num_shorts,clip_data,display,_deprecated_);
}
status_t MediaReader::GetLatency(
bigtime_t * out_latency)
{
CALLED();
*out_latency = EventLatency() + SchedulingLatency();
return B_OK;
}
status_t MediaReader::PrepareToConnect(
const media_source & what,
const media_destination & where,
media_format * format,
media_source * out_source,
char * out_name)
{
CALLED();
if (output.source != what) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return B_MEDIA_BAD_SOURCE;
}
if (output.destination != media_destination::null) {
PRINT("\t<- B_MEDIA_ALREADY_CONNECTED\n");
return B_MEDIA_ALREADY_CONNECTED;
}
status_t status = FormatChangeRequested(output.source,where,format,0);
if (status != B_OK) {
PRINT("\t<- MediaReader::FormatChangeRequested failed\n");
return status;
}
if (format->type != B_MEDIA_MULTISTREAM) {
PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
return B_MEDIA_BAD_FORMAT;
}
*out_source = output.source;
output.destination = where;
strncpy(out_name,output.name,B_MEDIA_NAME_LENGTH-1);
out_name[B_MEDIA_NAME_LENGTH] = '\0';
return ResolveWildcards(format);
}
void MediaReader::Connect(
status_t error,
const media_source & source,
const media_destination & destination,
const media_format & format,
char * io_name)
{
CALLED();
if (error != B_OK) {
PRINT("\t<- error already\n");
output.destination = media_destination::null;
GetFormat(&output.format);
return;
}
if (output.source != source) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
output.destination = media_destination::null;
GetFormat(&output.format);
return;
}
output.destination = destination;
output.format = format;
strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1);
io_name[B_MEDIA_NAME_LENGTH-1] = '\0';
media_node_id id;
FindLatencyFor(output.destination, &fDownstreamLatency, &id);
fBufferPeriod = bigtime_t(1000u * 8000000u / 1024u
* output.format.u.multistream.max_chunk_size
/ output.format.u.multistream.max_bit_rate);
PRINT("\tmax chunk size = %ld, max bit rate = %f, buffer period = %lld\n",
output.format.u.multistream.max_chunk_size,
output.format.u.multistream.max_bit_rate,fBufferPeriod);
if (fBufferGroup == 0) {
status_t status = SetBufferGroup(output.source,0);
if (status != B_OK) {
PRINT("\t<- SetBufferGroup failed\n");
output.destination = media_destination::null;
GetFormat(&output.format);
return;
}
}
SetBufferDuration(fBufferPeriod);
if (GetCurrentFile() != 0) {
bigtime_t start, end;
uint8 * data = new uint8[output.format.u.multistream.max_chunk_size];
BBuffer * buffer = 0;
ssize_t bytesRead = 0;
{
start = TimeSource()->RealTime();
buffer = fBufferGroup->RequestBuffer(
output.format.u.multistream.max_chunk_size,fBufferPeriod);
if (buffer != 0) {
FillFileBuffer(buffer);
} else {
bytesRead = GetCurrentFile()->Read(
data, output.format.u.multistream.max_chunk_size);
}
end = TimeSource()->RealTime();
}
bytesRead = buffer->SizeUsed();
delete[] data;
if (buffer != 0) {
buffer->Recycle();
}
GetCurrentFile()->Seek(-bytesRead,SEEK_CUR);
fInternalLatency = end - start;
PRINT("\tinternal latency from disk read = %lld\n", fInternalLatency);
} else {
fInternalLatency = 100;
PRINT("\tinternal latency guessed = %lld\n", fInternalLatency);
}
SetEventLatency(fDownstreamLatency + fInternalLatency);
}
void MediaReader::Disconnect(
const media_source & what,
const media_destination & where)
{
CALLED();
if (output.destination != where) {
PRINT("\t<- B_MEDIA_BAD_DESTINATION\n");
return;
}
if (output.source != what) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return;
}
output.destination = media_destination::null;
GetFormat(&output.format);
if (fBufferGroup != 0) {
BBufferGroup * group = fBufferGroup;
fBufferGroup = 0;
delete group;
}
}
void MediaReader::LateNoticeReceived(
const media_source & what,
bigtime_t how_much,
bigtime_t performance_time)
{
CALLED();
if (what == output.source) {
switch (RunMode()) {
case B_OFFLINE:
break;
case B_RECORDING:
break;
case B_INCREASE_LATENCY:
fInternalLatency += how_much;
SetEventLatency(fDownstreamLatency + fInternalLatency);
break;
case B_DECREASE_PRECISION:
break;
case B_DROP_DATA:
if (GetCurrentFile() == 0) {
PRINT("MediaReader::LateNoticeReceived called without"
"an GetCurrentFile() (!)\n");
} else {
GetCurrentFile()->Seek(output.format.u.multistream.max_chunk_size,SEEK_CUR);
}
break;
default:
PRINT("MediaReader::LateNoticeReceived with unexpected run mode.\n");
break;
}
}
}
void MediaReader::EnableOutput(
const media_source & what,
bool enabled,
int32 * _deprecated_)
{
CALLED();
if (output.source != what) {
PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
return;
}
fOutputEnabled = enabled;
}
status_t MediaReader::SetPlayRate(
int32 numer,
int32 denom)
{
return BBufferProducer::SetPlayRate(numer,denom);
}
void MediaReader::AdditionalBufferRequested(
const media_source & source,
media_buffer_id prev_buffer,
bigtime_t prev_time,
const media_seek_tag * prev_tag)
{
CALLED();
if (output.source == source) {
BBuffer * buffer;
status_t status = GetFilledBuffer(&buffer);
if (status != B_OK) {
PRINT("MediaReader::AdditionalBufferRequested got an error from GetFilledBuffer.\n");
return;
}
SendBuffer(buffer, output.source, output.destination);
}
}
void MediaReader::LatencyChanged(
const media_source & source,
const media_destination & destination,
bigtime_t new_latency,
uint32 flags)
{
CALLED();
if ((output.source == source) && (output.destination == destination)) {
fDownstreamLatency = new_latency;
SetEventLatency(fDownstreamLatency + fInternalLatency);
}
}
status_t MediaReader::HandleBuffer(
const media_timed_event *event,
bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
if (output.destination == media_destination::null)
return B_MEDIA_NOT_CONNECTED;
status_t status = B_OK;
BBuffer * buffer = fBufferGroup->RequestBuffer(output.format.u.multistream.max_chunk_size,fBufferPeriod);
if (buffer != 0) {
status = FillFileBuffer(buffer);
if (status != B_OK) {
PRINT("MediaReader::HandleEvent got an error from FillFileBuffer.\n");
buffer->Recycle();
} else {
if (fOutputEnabled) {
status = SendBuffer(buffer, output.source, output.destination);
if (status != B_OK) {
PRINT("MediaReader::HandleEvent got an error from SendBuffer.\n");
buffer->Recycle();
}
} else {
buffer->Recycle();
}
}
}
bigtime_t nextEventTime = event->event_time+fBufferPeriod;
media_timed_event nextBufferEvent(nextEventTime, BTimedEventQueue::B_HANDLE_BUFFER);
EventQueue()->AddEvent(nextBufferEvent);
return status;
}
status_t MediaReader::HandleDataStatus(
const media_timed_event *event,
bigtime_t lateness,
bool realTimeEvent)
{
CALLED();
return SendDataStatus(event->data,output.destination,event->event_time);
}
void MediaReader::GetFlavor(flavor_info * outInfo, int32 id)
{
CALLED();
if (outInfo == 0)
return;
AbstractFileInterfaceNode::GetFlavor(outInfo,id);
outInfo->name = strdup("Media Reader");
outInfo->info = strdup(
"The Haiku Media Reader reads a file and produces a multistream.");
outInfo->kinds |= B_BUFFER_PRODUCER;
outInfo->out_format_count = 1;
media_format * formats = new media_format[outInfo->out_format_count];
GetFormat(&formats[0]);
outInfo->out_formats = formats;
return;
}
void MediaReader::GetFormat(media_format * outFormat)
{
CALLED();
AbstractFileInterfaceNode::GetFormat(outFormat);
return;
}
void MediaReader::GetFileFormat(media_file_format * outFileFormat)
{
CALLED();
AbstractFileInterfaceNode::GetFileFormat(outFileFormat);
outFileFormat->capabilities |= media_file_format::B_READABLE;
return;
}
status_t MediaReader::GetFilledBuffer(
BBuffer ** outBuffer)
{
CALLED();
BBuffer * buffer = fBufferGroup->RequestBuffer(output.format.u.multistream.max_chunk_size,-1);
if (buffer == 0) {
PRINT("MediaReader::GetFilledBuffer needs a new buffer.\n");
return B_ERROR;
}
status_t status = FillFileBuffer(buffer);
*outBuffer = buffer;
return status;
}
status_t MediaReader::FillFileBuffer(
BBuffer * buffer)
{
CALLED();
if (GetCurrentFile() == 0) {
PRINT("\t<- B_NO_INIT\n");
return B_NO_INIT;
}
PRINT("\t%ld buffer bytes used, %ld buffer bytes available\n",
buffer->SizeUsed(), buffer->SizeAvailable());
off_t position = GetCurrentFile()->Position();
ssize_t bytesRead = GetCurrentFile()->Read(buffer->Data(),buffer->SizeAvailable());
if (bytesRead < 0) {
PRINT("\t<- B_IO_ERROR\n");
return B_IO_ERROR;
}
PRINT("\t%ld file bytes read at position %ld.\n",
bytesRead, position);
buffer->SetSizeUsed(bytesRead);
media_header * header = buffer->Header();
header->type = B_MEDIA_MULTISTREAM;
header->size_used = bytesRead;
header->file_pos = position;
header->orig_size = bytesRead;
header->time_source = TimeSource()->ID();
header->start_time = TimeSource()->Now();
return B_OK;
}
status_t MediaReader::_Reserved_MediaReader_0(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_1(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_2(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_3(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_4(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_5(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_6(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_7(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_8(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_9(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_10(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_11(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_12(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_13(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_14(void *) { return B_ERROR; }
status_t MediaReader::_Reserved_MediaReader_15(void *) { return B_ERROR; }