xref: /haiku/src/add-ons/media/media-add-ons/demultiplexer/MediaOutputInfo.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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 	// 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.
71 status_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 
84 status_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 
105 status_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 
125 status_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 
136 status_t MediaOutputInfo::EnableOutput(bool enabled)
137 {
138 	outputEnabled = enabled;
139 	return B_OK;
140 }
141 
142 status_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 
153 status_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 
181 uint32 MediaOutputInfo::ComputeBufferSize() {
182 	return ComputeBufferSize(output.format);
183 }
184 
185 // returns result in # of bytes
186 uint32 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 
221 bigtime_t MediaOutputInfo::ComputeBufferPeriod() {
222 	return ComputeBufferPeriod(output.format);
223 }
224 
225 // returns result in # of microseconds
226 bigtime_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