BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / add-ons / media / media-add-ons / writer / MediaWriter.cpp
blob87d379779659c492a3f26aeafe0ff5f72c14e0e4
1 // MediaWriter.cpp
2 //
3 // Andrew Bachmann, 2002
4 //
5 // A MediaWriter is a node that
6 // implements FileInterface and BBufferConsumer.
7 // It consumes on input, which is a multistream,
8 // and writes the stream to a file.
9 //
10 // see also MediaWriterAddOn.cpp
12 #include <MediaDefs.h>
13 #include <MediaNode.h>
14 #include <MediaAddOn.h>
15 #include <BufferConsumer.h>
16 #include <FileInterface.h>
17 #include <Controllable.h>
18 #include <MediaEventLooper.h>
19 #include <File.h>
20 #include <Errors.h>
21 #include <Entry.h>
22 #include <BufferGroup.h>
23 #include <TimeSource.h>
24 #include <Buffer.h>
25 #include <ParameterWeb.h>
26 #include <MediaRoster.h>
27 #include <limits.h>
29 #include "../AbstractFileInterfaceNode.h"
30 #include "MediaWriter.h"
31 #include "../misc.h"
33 #include <stdio.h>
34 #include <string.h>
36 // -------------------------------------------------------- //
37 // ctor/dtor
38 // -------------------------------------------------------- //
40 MediaWriter::~MediaWriter(void)
42 fprintf(stderr,"MediaWriter::~MediaWriter\n");
43 if (fBufferGroup != 0) {
44 BBufferGroup * group = fBufferGroup;
45 fBufferGroup = 0;
46 delete group;
50 MediaWriter::MediaWriter(
51 size_t defaultChunkSize,
52 float defaultBitRate,
53 const flavor_info * info,
54 BMessage * config,
55 BMediaAddOn * addOn)
56 : BMediaNode("MediaWriter"),
57 BBufferConsumer(B_MEDIA_MULTISTREAM),
58 AbstractFileInterfaceNode(defaultChunkSize,defaultBitRate,info,config,addOn)
60 fprintf(stderr,"MediaWriter::MediaWriter\n");
61 // null some fields
62 fBufferGroup = 0;
63 // don't overwrite available space, and be sure to terminate
64 strncpy(input.name,"MediaWriter Input",B_MEDIA_NAME_LENGTH-1);
65 input.name[B_MEDIA_NAME_LENGTH-1] = '\0';
66 // initialize the input
67 input.node = media_node::null; // until registration
68 input.source = media_source::null;
69 input.destination = media_destination::null; // until registration
70 GetFormat(&input.format);
73 // -------------------------------------------------------- //
74 // implementation of BMediaNode
75 // -------------------------------------------------------- //
77 void MediaWriter::Preroll(void)
79 fprintf(stderr,"MediaWriter::Preroll\n");
80 // XXX:Performance opportunity
81 BMediaNode::Preroll();
84 status_t MediaWriter::HandleMessage(
85 int32 message,
86 const void * data,
87 size_t size)
89 fprintf(stderr,"MediaWriter::HandleMessage\n");
90 status_t status = B_OK;
91 switch (message) {
92 // no special messages for now
93 default:
94 status = BBufferConsumer::HandleMessage(message,data,size);
95 if (status == B_OK) {
96 break;
98 status = AbstractFileInterfaceNode::HandleMessage(message,data,size);
99 break;
101 return status;
104 void MediaWriter::NodeRegistered(void)
106 fprintf(stderr,"MediaWriter::NodeRegistered\n");
108 // now we can do this
109 input.node = Node();
110 input.destination.id = 0;
111 input.destination.port = input.node.port; // same as ControlPort()
113 // creates the parameter web and starts the looper thread
114 AbstractFileInterfaceNode::NodeRegistered();
117 // -------------------------------------------------------- //
118 // implementation of BFileInterface
119 // -------------------------------------------------------- //
121 status_t MediaWriter::SetRef(
122 const entry_ref & file,
123 bool create,
124 bigtime_t * out_time)
126 fprintf(stderr,"MediaWriter::SetRef\n");
127 status_t status;
128 status = AbstractFileInterfaceNode::SetRef(file,B_WRITE_ONLY,create,out_time);
129 if (status != B_OK) {
130 fprintf(stderr,"AbstractFileInterfaceNode::SetRef returned an error\n");
131 return status;
133 if (input.source == media_source::null) {
134 // reset the format, and set the requirements imposed by this file
135 GetFormat(&input.format);
136 AddRequirements(&input.format);
137 return B_OK;
139 // if we are connected we may have to re-negotiate the connection
140 media_format format;
141 GetFormat(&format);
142 AddRequirements(&format);
143 if (format_is_acceptible(input.format,format)) {
144 fprintf(stderr," compatible format = no re-negotiation necessary\n");
145 return B_OK;
147 // first try the easy way : SORRY DEPRECATED into private :-(
148 // int32 change_tag = NewChangeTag();
149 // status = this->BBufferConsumer::RequestFormatChange(input.source,input.destination,&format,&change_tag);
150 // if (status == B_OK) {
151 // fprintf(stderr," format change successful\n");
152 // return B_OK;
153 // }
154 // okay, the hard way requires we get the MediaRoster
155 BMediaRoster * roster = BMediaRoster::Roster(&status);
156 if (roster == 0) {
157 return B_MEDIA_SYSTEM_FAILURE;
159 if (status != B_OK) {
160 return status;
162 // before disconnect one should always stop the nodes (bebook says)
163 // requires run_state cast since the return type on RunState() is
164 // wrong [int32]
165 run_state destinationRunState = run_state(RunState());
166 if (destinationRunState == BMediaEventLooper::B_STARTED) {
167 Stop(0,true); // stop us right now
169 // should also stop the source if it is running, but how?
170 /* BMediaNode sourceNode = ??
171 run_state sourceRunState = sourceNode->??;
172 status = sourceNode->StopNode(??,0,true);
173 if (status != B_OK) {
174 return status;
175 } */
176 // we should disconnect right now
177 media_source inputSource = input.source;
178 status = roster->Disconnect(input.source.id,input.source,
179 input.destination.id,input.destination);
180 if (status != B_OK) {
181 return status;
183 // if that went okay, we'll try reconnecting
184 media_output connectOutput;
185 media_input connectInput;
186 status = roster->Connect(inputSource,input.destination,
187 &format,&connectOutput,&connectInput);
188 if (status != B_OK) {
189 return status;
191 // now restart if necessary
192 if (destinationRunState == BMediaEventLooper::B_STARTED) {
193 Start(0);
195 return status;
198 // -------------------------------------------------------- //
199 // implemention of BBufferConsumer
200 // -------------------------------------------------------- //
202 // Check to make sure the format is okay, then remove
203 // any wildcards corresponding to our requirements.
204 status_t MediaWriter::AcceptFormat(
205 const media_destination & dest,
206 media_format * format)
208 fprintf(stderr,"MediaWriter::AcceptFormat\n");
209 if (format == 0) {
210 fprintf(stderr,"<- B_BAD_VALUE\n");
211 return B_BAD_VALUE; // no crashing
213 if (input.destination != dest) {
214 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION");
215 return B_MEDIA_BAD_DESTINATION; // we only have one input so that better be it
217 /* media_format * myFormat = GetFormat();
218 fprintf(stderr,"proposed format: ");
219 print_media_format(format);
220 fprintf(stderr,"\n");
221 fprintf(stderr,"my format: ");
222 print_media_format(myFormat);
223 fprintf(stderr,"\n");*/
224 // Be's format_is_compatible doesn't work.
225 // if (!format_is_compatible(*format,*myFormat)) {
226 media_format myFormat;
227 GetFormat(&myFormat);
228 if (!format_is_acceptible(*format,myFormat)) {
229 fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
230 return B_MEDIA_BAD_FORMAT;
232 AddRequirements(format);
233 return B_OK;
236 status_t MediaWriter::GetNextInput(
237 int32 * cookie,
238 media_input * out_input)
240 fprintf(stderr,"MediaWriter::GetNextInput\n");
241 // let's not crash even if they are stupid
242 if (out_input == 0) {
243 // no place to write!
244 fprintf(stderr,"<- B_BAD_VALUE\n");
245 return B_BAD_VALUE;
247 if (cookie != 0) {
248 // it's valid but they already got our 1 input
249 if (*cookie != 0) {
250 fprintf(stderr,"<- B_ERROR (no more inputs)\n");
251 return B_ERROR;
253 // so next time they won't get the same input again
254 *cookie = 1;
256 *out_input = input;
257 return B_OK;
260 void MediaWriter::DisposeInputCookie(
261 int32 cookie)
263 fprintf(stderr,"MediaWriter::DisposeInputCookie\n");
264 // nothing to do since our cookies are just integers
265 return; // B_OK;
268 void MediaWriter::BufferReceived(
269 BBuffer * buffer)
271 fprintf(stderr,"MediaWriter::BufferReceived\n");
272 switch (buffer->Header()->type) {
273 case B_MEDIA_PARAMETERS:
275 status_t status = ApplyParameterData(buffer->Data(),buffer->SizeUsed());
276 if (status != B_OK) {
277 fprintf(stderr,"ApplyParameterData in MediaWriter::BufferReceived failed\n");
279 buffer->Recycle();
281 break;
282 case B_MEDIA_MULTISTREAM:
283 if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
284 fprintf(stderr,"NOT IMPLEMENTED: B_SMALL_BUFFER in MediaWriter::BufferReceived\n");
285 // XXX: implement this part
286 buffer->Recycle();
287 } else {
288 media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
289 buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
290 status_t status = EventQueue()->AddEvent(event);
291 if (status != B_OK) {
292 fprintf(stderr,"EventQueue()->AddEvent(event) in MediaWriter::BufferReceived failed\n");
293 buffer->Recycle();
296 break;
297 default:
298 fprintf(stderr,"unexpected buffer type in MediaWriter::BufferReceived\n");
299 buffer->Recycle();
300 break;
304 void MediaWriter::ProducerDataStatus(
305 const media_destination & for_whom,
306 int32 status,
307 bigtime_t at_performance_time)
309 fprintf(stderr,"MediaWriter::ProducerDataStatus\n");
310 if (input.destination != for_whom) {
311 fprintf(stderr,"invalid destination received in MediaWriter::ProducerDataStatus\n");
312 return;
314 media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
315 &input, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
316 EventQueue()->AddEvent(event);
319 status_t MediaWriter::GetLatencyFor(
320 const media_destination & for_whom,
321 bigtime_t * out_latency,
322 media_node_id * out_timesource)
324 fprintf(stderr,"MediaWriter::GetLatencyFor\n");
325 if ((out_latency == 0) || (out_timesource == 0)) {
326 fprintf(stderr,"<- B_BAD_VALUE\n");
327 return B_BAD_VALUE;
329 if (input.destination != for_whom) {
330 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
331 return B_MEDIA_BAD_DESTINATION;
333 *out_latency = EventLatency();
334 *out_timesource = TimeSource()->ID();
335 return B_OK;
338 status_t MediaWriter::Connected(
339 const media_source & producer, /* here's a good place to request buffer group usage */
340 const media_destination & where,
341 const media_format & with_format,
342 media_input * out_input)
344 fprintf(stderr,"MediaWriter::Connected\n");
345 if (out_input == 0) {
346 fprintf(stderr,"<- B_BAD_VALUE\n");
347 return B_BAD_VALUE; // no crashing
349 if (input.destination != where) {
350 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
351 return B_MEDIA_BAD_DESTINATION;
354 // clear any stale buffer groups
355 if (fBufferGroup != 0) {
356 BBufferGroup * group = fBufferGroup;
357 fBufferGroup = 0;
358 delete group;
361 // compute the latency or just guess
362 if (GetCurrentFile() != 0) {
363 bigtime_t start, end;
364 uint8 * data = new uint8[input.format.u.multistream.max_chunk_size]; // <- buffer group buffer size
365 ssize_t bytesWritten = 0;
366 { // timed section
367 start = TimeSource()->RealTime();
368 bytesWritten = GetCurrentFile()->Write(data,input.format.u.multistream.max_chunk_size);
369 end = TimeSource()->RealTime();
371 delete[] data;
372 GetCurrentFile()->Seek(-bytesWritten,SEEK_CUR); // put it back where we found it
374 fInternalLatency = end - start;
376 fprintf(stderr," internal latency from disk write = %lld\n",fInternalLatency);
377 } else {
378 fInternalLatency = 500; // just guess
379 fprintf(stderr," internal latency guessed = %lld\n",fInternalLatency);
382 SetEventLatency(fInternalLatency);
384 // record the agreed upon values
385 input.source = producer;
386 input.format = with_format;
387 *out_input = input;
388 return B_OK;
391 void MediaWriter::Disconnected(
392 const media_source & producer,
393 const media_destination & where)
395 fprintf(stderr,"MediaWriter::Disconnected\n");
396 if (input.destination != where) {
397 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
398 return;
400 if (input.source != producer) {
401 fprintf(stderr,"<- B_MEDIA_BAD_SOURCE\n");
402 return;
404 input.source = media_source::null;
405 GetFormat(&input.format);
406 if (fBufferGroup != 0) {
407 BBufferGroup * group = fBufferGroup;
408 fBufferGroup = 0;
409 delete group;
413 /* The notification comes from the upstream producer, so he's already cool with */
414 /* the format; you should not ask him about it in here. */
415 status_t MediaWriter::FormatChanged(
416 const media_source & producer,
417 const media_destination & consumer,
418 int32 change_tag,
419 const media_format & format)
421 fprintf(stderr,"MediaWriter::FormatChanged\n");
422 if (input.source != producer) {
423 return B_MEDIA_BAD_SOURCE;
425 if (input.destination != consumer) {
426 return B_MEDIA_BAD_DESTINATION;
428 // Since we don't really care about the format of the data
429 // we can just continue to treat things the same way.
430 input.format = format;
431 return B_OK;
434 /* Given a performance time of some previous buffer, retrieve the remembered tag */
435 /* of the closest (previous or exact) performance time. Set *out_flags to 0; the */
436 /* idea being that flags can be added later, and the understood flags returned in */
437 /* *out_flags. */
438 status_t MediaWriter::SeekTagRequested(
439 const media_destination & destination,
440 bigtime_t in_target_time,
441 uint32 in_flags,
442 media_seek_tag * out_seek_tag,
443 bigtime_t * out_tagged_time,
444 uint32 * out_flags)
446 fprintf(stderr,"MediaWriter::SeekTagRequested\n");
447 return BBufferConsumer::SeekTagRequested(destination,in_target_time,in_flags,
448 out_seek_tag,out_tagged_time,out_flags);
451 // -------------------------------------------------------- //
452 // implementation for BMediaEventLooper
453 // -------------------------------------------------------- //
455 // protected:
457 // how should we handle late buffers? drop them?
458 // notify the producer?
459 status_t MediaWriter::HandleBuffer(
460 const media_timed_event *event,
461 bigtime_t lateness,
462 bool realTimeEvent)
464 fprintf(stderr,"MediaWriter::HandleBuffer\n");
465 BBuffer * buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
466 if (buffer == 0) {
467 fprintf(stderr,"<- B_BAD_VALUE\n");
468 return B_BAD_VALUE;
470 if (buffer->Header()->destination != input.destination.id) {
471 fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n");
472 return B_MEDIA_BAD_DESTINATION;
474 WriteFileBuffer(buffer);
475 buffer->Recycle();
476 return B_OK;
479 status_t MediaWriter::HandleDataStatus(
480 const media_timed_event *event,
481 bigtime_t lateness,
482 bool realTimeEvents)
484 fprintf(stderr,"MediaWriter::HandleDataStatus");
485 // we have no where to send a data status to.
486 return B_OK;
490 // -------------------------------------------------------- //
491 // MediaWriter specific functions
492 // -------------------------------------------------------- //
494 // static:
496 void MediaWriter::GetFlavor(flavor_info * outInfo, int32 id)
498 fprintf(stderr,"MediaWriter::GetFlavor\n");
499 if (outInfo == 0) {
500 return;
502 AbstractFileInterfaceNode::GetFlavor(outInfo,id);
503 strcpy(outInfo->name, "Media Writer");
504 strcpy(outInfo->info,
505 "The Haiku Media Writer consumes a multistream and writes a file.");
506 outInfo->kinds |= B_BUFFER_CONSUMER;
507 outInfo->in_format_count = 1; // 1 input
508 media_format * formats = new media_format[outInfo->in_format_count];
509 GetFormat(&formats[0]);
510 outInfo->in_formats = formats;
511 return;
514 void MediaWriter::GetFormat(media_format * outFormat)
516 fprintf(stderr,"MediaWriter::GetFormat\n");
517 if (outFormat == 0) {
518 return;
520 AbstractFileInterfaceNode::GetFormat(outFormat);
521 return;
524 void MediaWriter::GetFileFormat(media_file_format * outFileFormat)
526 fprintf(stderr,"MediaWriter::GetFileFormat\n");
527 if (outFileFormat == 0) {
528 return;
530 AbstractFileInterfaceNode::GetFileFormat(outFileFormat);
531 outFileFormat->capabilities |= media_file_format::B_WRITABLE;
532 return;
535 // protected:
537 status_t MediaWriter::WriteFileBuffer(
538 BBuffer * buffer)
540 fprintf(stderr,"MediaWriter::WriteFileBuffer\n");
541 if (GetCurrentFile() == 0) {
542 fprintf(stderr,"<- B_NO_INIT\n");
543 return B_NO_INIT;
545 fprintf(stderr," writing %" B_PRId32 " bytes at %lld\n",
546 buffer->SizeUsed(),GetCurrentFile()->Position());
547 ssize_t bytesWriten = GetCurrentFile()->Write(buffer->Data(),buffer->SizeUsed());
548 if (bytesWriten < 0) {
549 fprintf(stderr,"<- B_FILE_ERROR\n");
550 return B_FILE_ERROR; // some sort of file related error
552 // nothing more to say?
553 return B_OK;
556 // -------------------------------------------------------- //
557 // stuffing
558 // -------------------------------------------------------- //
560 status_t MediaWriter::_Reserved_MediaWriter_0(void *) { return B_ERROR; }
561 status_t MediaWriter::_Reserved_MediaWriter_1(void *) { return B_ERROR; }
562 status_t MediaWriter::_Reserved_MediaWriter_2(void *) { return B_ERROR; }
563 status_t MediaWriter::_Reserved_MediaWriter_3(void *) { return B_ERROR; }
564 status_t MediaWriter::_Reserved_MediaWriter_4(void *) { return B_ERROR; }
565 status_t MediaWriter::_Reserved_MediaWriter_5(void *) { return B_ERROR; }
566 status_t MediaWriter::_Reserved_MediaWriter_6(void *) { return B_ERROR; }
567 status_t MediaWriter::_Reserved_MediaWriter_7(void *) { return B_ERROR; }
568 status_t MediaWriter::_Reserved_MediaWriter_8(void *) { return B_ERROR; }
569 status_t MediaWriter::_Reserved_MediaWriter_9(void *) { return B_ERROR; }
570 status_t MediaWriter::_Reserved_MediaWriter_10(void *) { return B_ERROR; }
571 status_t MediaWriter::_Reserved_MediaWriter_11(void *) { return B_ERROR; }
572 status_t MediaWriter::_Reserved_MediaWriter_12(void *) { return B_ERROR; }
573 status_t MediaWriter::_Reserved_MediaWriter_13(void *) { return B_ERROR; }
574 status_t MediaWriter::_Reserved_MediaWriter_14(void *) { return B_ERROR; }
575 status_t MediaWriter::_Reserved_MediaWriter_15(void *) { return B_ERROR; }