1// MediaOutputInfo.cpp
2//
3// Andrew Bachmann, 2002
4//
5// A class to encapsulate and manipulate
6// all the information for a particular
7// output of a media node.
8
9#include <MediaDefs.h>
10#include <BufferGroup.h>
11#include <BufferProducer.h>
12#include <stdio.h>
13#include <string.h>
14#include "MediaOutputInfo.h"
15#include "misc.h"
16
17MediaOutputInfo::MediaOutputInfo(BBufferProducer * node, char * name) {
18	producer = node;
19	// null some fields
20	bufferGroup = 0;
21	bufferPeriod = 0;
22	// start enabled
23	outputEnabled = true;
24	// don't overwrite available space, and be sure to terminate
25	strncpy(output.name,name,B_MEDIA_NAME_LENGTH-1);
26	output.name[B_MEDIA_NAME_LENGTH-1] = '\0';
27	// initialize the output
28	output.node = media_node::null;
29	output.source = media_source::null;
30	output.destination = media_destination::null;
31}
32
33MediaOutputInfo::~MediaOutputInfo() {
34	if (bufferGroup != 0) {
35		BBufferGroup * group = bufferGroup;
36		bufferGroup = 0;
37		delete group;
38	}
39}
40
41status_t MediaOutputInfo::SetBufferGroup(BBufferGroup * group) {
42	if (bufferGroup != 0) {
43		if (bufferGroup == group) {
44			return B_OK; // time saver
45		}
46		delete bufferGroup;
47	}
48	bufferGroup = group;
49}
50
51// They made an offer to us.  We should make sure that the offer is
52// acceptable, and then we can add any requirements we have on top of
53// that.  We leave wildcards for anything that we don't care about.
54status_t MediaOutputInfo::FormatProposal(media_format * format)
55{
56	// Be's format_is_compatible doesn't work,
57	// so use our format_is_acceptible instead
58	if (!format_is_acceptible(*format,generalFormat)) {
59		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
60		return B_MEDIA_BAD_FORMAT;
61	}
62	// XXX: test because we don't trust them!
63	format->SpecializeTo(&wildcardedFormat);
64	return B_OK;
65}
66
67// Presumably we have already agreed with them that this format is
68// okay.  But just in case, we check the offer. (and complain if it
69// is invalid)  Then as the last thing we do, we get rid of any
70// remaining wilcards.
71status_t MediaOutputInfo::FormatChangeRequested(const media_destination & destination,
72							   media_format * io_format)
73{
74	status_t status = FormatProposal(io_format);
75	if (status != B_OK) {
76		fprintf(stderr,"<- MediaOutputInfo::FormatProposal failed\n");
77		*io_format = generalFormat;
78		return status;
79	}
80	io_format->SpecializeTo(&fullySpecifiedFormat);
81	return B_OK;
82}
83
84status_t MediaOutputInfo::PrepareToConnect(const media_destination & where,
85						  media_format * format,
86						  media_source * out_source,
87						  char * out_name)
88{
89	if (output.destination != media_destination::null) {
90		fprintf(stderr,"<- B_MEDIA_ALREADY_CONNECTED\n");
91		return B_MEDIA_ALREADY_CONNECTED;
92	}
93	status_t status = FormatChangeRequested(where,format);
94	if (status != B_OK) {
95		fprintf(stderr,"<- MediaOutputInfo::FormatChangeRequested failed\n");
96		return status;
97	}
98	*out_source = output.source;
99	output.destination = where;
100	strncpy(out_name,output.name,B_MEDIA_NAME_LENGTH-1);
101	out_name[B_MEDIA_NAME_LENGTH] = '\0';
102	return B_OK;
103}
104
105status_t MediaOutputInfo::Connect(const media_destination & destination,
106				 const media_format & format,
107				 char * io_name,
108				 bigtime_t _downstreamLatency)
109{
110	output.destination = destination;
111	output.format = format;
112	strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1);
113	io_name[B_MEDIA_NAME_LENGTH-1] = '\0';
114	downstreamLatency = _downstreamLatency; // must be set before create buffer group
115
116	status_t status = CreateBufferGroup(); // also initializes buffer period
117	if (status != B_OK) {
118		output.destination = media_destination::null;
119		output.format = generalFormat;
120		return status;
121	}
122	return B_OK;
123}
124
125status_t MediaOutputInfo::Disconnect()
126{
127	output.destination = media_destination::null;
128	output.format = generalFormat;
129	if (bufferGroup != 0) {
130		BBufferGroup * group = bufferGroup;
131		bufferGroup = 0;
132		delete group;
133	}
134}
135
136status_t MediaOutputInfo::EnableOutput(bool enabled)
137{
138	outputEnabled = enabled;
139	return B_OK;
140}
141
142status_t MediaOutputInfo::AdditionalBufferRequested(
143					media_buffer_id prev_buffer,
144					bigtime_t prev_time,
145					const media_seek_tag * prev_tag)
146{
147	// XXX: implement me
148	return B_OK;
149}
150
151// protected:
152
153status_t MediaOutputInfo::CreateBufferGroup() {
154	bufferPeriod = ComputeBufferPeriod();
155
156	if (bufferGroup == 0) {
157		int32 count = int32(downstreamLatency/bufferPeriod)+2;
158		fprintf(stderr,"  downstream latency = %lld, buffer period = %lld, buffer count = %i\n",
159				downstreamLatency,bufferPeriod,count);
160
161		// allocate the buffers
162		bufferGroup = new BBufferGroup(ComputeBufferSize(),count);
163		if (bufferGroup == 0) {
164			fprintf(stderr,"<- B_NO_MEMORY\n");
165			return B_NO_MEMORY;
166		}
167		status_t status = bufferGroup->InitCheck();
168		if (status != B_OK) {
169			fprintf(stderr,"<- BufferGroup initialization failed\n");
170			BBufferGroup * group = bufferGroup;
171			bufferGroup = 0;
172			delete group;
173			return status;
174		}
175	}
176	return B_OK;
177}
178
179// public:
180
181uint32 MediaOutputInfo::ComputeBufferSize() {
182	return ComputeBufferSize(output.format);
183}
184
185// returns result in # of bytes
186uint32 MediaOutputInfo::ComputeBufferSize(const media_format & format) {
187	uint64 bufferSize = 1024; // default 1024 bytes
188	switch (format.type) {
189	case B_MEDIA_MULTISTREAM:
190		bufferSize = format.u.multistream.max_chunk_size;
191		break;
192	case B_MEDIA_ENCODED_VIDEO:
193		bufferSize = format.u.encoded_video.frame_size;
194		break;
195	case B_MEDIA_RAW_VIDEO:
196		if (format.u.raw_video.interlace == 0) {
197			// okay, you have no fields, you need no space, right?
198			bufferSize = 0;
199		} else {
200			// this is the size of a *field*, not a frame
201			bufferSize = format.u.raw_video.display.bytes_per_row *
202						 format.u.raw_video.display.line_count /
203						 format.u.raw_video.interlace;
204		}
205		break;
206	case B_MEDIA_ENCODED_AUDIO:
207		bufferSize = format.u.encoded_audio.frame_size;
208		break;
209	case B_MEDIA_RAW_AUDIO:
210		bufferSize = format.u.raw_audio.buffer_size;
211		break;
212	default:
213		break;
214	}
215	if (bufferSize > INT_MAX) {
216		bufferSize = INT_MAX;
217	}
218	return int32(bufferSize);
219}
220
221bigtime_t MediaOutputInfo::ComputeBufferPeriod() {
222	return ComputeBufferPeriod(output.format);
223}
224
225// returns result in # of microseconds
226bigtime_t MediaOutputInfo::ComputeBufferPeriod(const media_format & format) {
227	bigtime_t bufferPeriod = 25*1000; // default 25 milliseconds
228	switch (format.type) {
229	case B_MEDIA_MULTISTREAM:
230		// given a buffer size of 8192 bytes
231		// and a bitrate of 1024 kilobits/millisecond (= 128 bytes/millisecond)
232		// we need to produce a buffer every 64 milliseconds (= every 64000 microseconds)
233		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
234								 / format.u.multistream.max_bit_rate);
235		break;
236	case B_MEDIA_ENCODED_VIDEO:
237		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
238								 / format.u.encoded_video.max_bit_rate);
239		break;
240	case B_MEDIA_ENCODED_AUDIO:
241		bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format)
242								 / format.u.encoded_audio.bit_rate);
243		break;
244	case B_MEDIA_RAW_VIDEO:
245		// Given a field rate of 50.00 fields per second, (PAL)
246		// we need to generate a field/buffer
247		// every 1/50 of a second = 20000 microseconds.
248		bufferPeriod = bigtime_t(1000000.0
249								 / format.u.raw_video.field_rate);
250		break;
251	case B_MEDIA_RAW_AUDIO:
252		// Given a sample size of 4 bytes [B_AUDIO_INT]
253		// and a channel count of 2 and a buffer_size
254		// of 256 bytes and a frame_rate of 44100 Hertz (1/sec)
255		// 1 frame = 1 sample/channel.
256		// comes to ??
257		// this is a guess:
258		bufferPeriod = bigtime_t(1000000.0 * ComputeBufferSize(format)
259								 / (format.u.raw_audio.format
260								    & media_raw_audio_format::B_AUDIO_SIZE_MASK)
261								 / format.u.raw_audio.channel_count
262							     / format.u.raw_audio.frame_rate);
263		break;
264	default:
265		break;
266	}
267	return bufferPeriod;
268}
269
270
271