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