1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include "mmal.h"
29#include "core/mmal_component_private.h"
30#include "core/mmal_port_private.h"
31#include "mmal_logging.h"
32
33#define SPDIF_AC3_FRAME_SIZE 6144
34#define SPDIF_EAC3_FRAME_SIZE (6144*4)
35#define SPDIF_FRAME_SIZE SPDIF_EAC3_FRAME_SIZE
36
37/* Buffering requirements */
38#define INPUT_MIN_BUFFER_SIZE SPDIF_FRAME_SIZE
39#define INPUT_MIN_BUFFER_NUM 2
40#define OUTPUT_MIN_BUFFER_SIZE SPDIF_FRAME_SIZE
41#define OUTPUT_MIN_BUFFER_NUM 2
42
43/*****************************************************************************/
44typedef struct MMAL_COMPONENT_MODULE_T
45{
46 MMAL_STATUS_T status; /**< current status of the component */
47
48} MMAL_COMPONENT_MODULE_T;
49
50typedef struct MMAL_PORT_MODULE_T
51{
52 MMAL_QUEUE_T *queue; /**< queue for the buffers sent to the ports */
53 MMAL_BOOL_T needs_configuring; /**< port is waiting for a format commit */
54
55} MMAL_PORT_MODULE_T;
56
57/*****************************************************************************/
58
59/*****************************************************************************/
60
61static MMAL_STATUS_T spdif_send_event_format_changed(MMAL_COMPONENT_T *component, MMAL_PORT_T *port,
62 MMAL_ES_FORMAT_T *format)
63{
64 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
65 MMAL_BUFFER_HEADER_T *buffer = NULL;
66 MMAL_EVENT_FORMAT_CHANGED_T *event;
67
68 /* Get an event buffer */
69 module->status = mmal_port_event_get(port, &buffer, MMAL_EVENT_FORMAT_CHANGED);
70 if (module->status != MMAL_SUCCESS)
71 {
72 LOG_ERROR("unable to get an event buffer");
73 return module->status;
74 }
75 /* coverity[returned_null] Can't return null or call above would have failed */
76 event = mmal_event_format_changed_get(buffer);
77
78 /* Fill in the new format */
79 if (port->format->encoding == MMAL_ENCODING_PCM_SIGNED)
80 mmal_format_copy(event->format, port->format);
81 else
82 mmal_format_copy(event->format, format);
83
84 event->format->es->audio.sample_rate = format->es->audio.sample_rate;
85
86 /* Pass on the buffer requirements */
87 event->buffer_num_min = port->buffer_num_min;
88 event->buffer_size_min = port->buffer_size_min;
89 event->buffer_size_recommended = event->buffer_size_min;
90 event->buffer_num_recommended = port->buffer_num_recommended;
91
92 port->priv->module->needs_configuring = 1;
93 mmal_port_event_send(port, buffer);
94 return MMAL_SUCCESS;
95}
96
97/** Actual processing function */
98static MMAL_BOOL_T spdif_do_processing(MMAL_COMPONENT_T *component)
99{
100 static const uint8_t ac3_spdif_header[6] = {0x72,0xF8,0x1F,0x4E,0x1, 0};
101 MMAL_COMPONENT_MODULE_T *module = component->priv->module;
102 MMAL_PORT_T *port_in = component->input[0];
103 MMAL_PORT_T *port_out = component->output[0];
104 MMAL_BUFFER_HEADER_T *in, *out;
105 unsigned int i, sample_rate, frame_size, spdif_frame_size;
106 uint8_t *in_data;
107
108 if (port_out->priv->module->needs_configuring)
109 return 0;
110
111 in = mmal_queue_get(port_in->priv->module->queue);
112 if (!in)
113 return 0;
114
115 /* Handle event buffers */
116 if (in->cmd)
117 {
118 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(in);
119 if (event)
120 {
121 module->status = mmal_format_full_copy(port_in->format, event->format);
122 if (module->status == MMAL_SUCCESS)
123 module->status = port_in->priv->pf_set_format(port_in);
124 if (module->status != MMAL_SUCCESS)
125 {
126 LOG_ERROR("format not set on port %s %p (%i)", port_in->name, port_in, module->status);
127 if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
128 LOG_ERROR("unable to send an error event buffer");
129 }
130 }
131 else
132 {
133 LOG_ERROR("discarding event %i on port %s %p", (int)in->cmd, port_in->name, port_in);
134 }
135
136 in->length = 0;
137 mmal_port_buffer_header_callback(port_in, in);
138 return 1;
139 }
140
141 /* Don't do anything if we've already seen an error */
142 if (module->status != MMAL_SUCCESS)
143 {
144 mmal_queue_put_back(port_in->priv->module->queue, in);
145 return 0;
146 }
147
148 /* Discard empty buffers */
149 if (!in->length && !in->flags)
150 {
151 mmal_port_buffer_header_callback(port_in, in);
152 return 1;
153 }
154 /* Discard codec config data as it's not needed */
155 if (in->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)
156 {
157 LOG_DEBUG("config buffer %ibytes", in->length);
158 in->length = 0;
159 mmal_port_buffer_header_callback(port_in, in);
160 return 1;
161 }
162
163 out = mmal_queue_get(port_out->priv->module->queue);
164 if (!out)
165 {
166 mmal_queue_put_back(port_in->priv->module->queue, in);
167 return 0;
168 }
169
170 spdif_frame_size = SPDIF_AC3_FRAME_SIZE;
171 if (port_out->format->encoding == MMAL_ENCODING_EAC3)
172 spdif_frame_size = SPDIF_EAC3_FRAME_SIZE;
173
174 /* Sanity check the output buffer is big enough */
175 if (out->alloc_size < spdif_frame_size)
176 {
177 module->status = MMAL_EINVAL;
178 if (mmal_event_error_send(component, module->status) != MMAL_SUCCESS)
179 LOG_ERROR("unable to send an error event buffer");
180 mmal_queue_put_back(port_in->priv->module->queue, in);
181 mmal_queue_put_back(port_out->priv->module->queue, out);
182 return 0;
183 }
184
185 /* Special case for empty buffers carrying a flag */
186 if (!in->length && in->flags)
187 {
188 out->length = 0;
189 goto end;
190 }
191
192 LOG_DEBUG("frame: %" PRId64 ", size %i", in->pts, in->length);
193 mmal_buffer_header_mem_lock(out);
194 mmal_buffer_header_mem_lock(in);
195 in_data = in->data + in->offset;
196
197 /* Sanity check we're dealing with an AC3 frame */
198 if (in->length < 5)
199 {
200 LOG_ERROR("invalid data size (%i bytes)", in->length);
201 goto discard;
202 }
203
204 if (!(in_data[0] == 0x0B || in_data[1] == 0x77) &&
205 !(in_data[0] == 0x77 || in_data[1] == 0x0B))
206 {
207 LOG_ERROR("invalid data (%i bytes): %2.2x,%2.2x,%2.2x,%2.2x",
208 in->length, in_data[0], in_data[1], in_data[2], in_data[3]);
209 goto discard;
210 }
211
212 /* We need to make sure we use the right sample rate
213 * to be able to play this at the right rate */
214 if ((in_data[4] & 0xC0) == 0x40) sample_rate = 44100;
215 else if ((in_data[4] & 0xC0) == 0x80) sample_rate = 32000;
216 else sample_rate = 48000;
217
218 /* If the sample rate changes, stop everything we're doing
219 * and signal the format change. */
220 if (sample_rate != port_out->format->es->audio.sample_rate)
221 {
222 LOG_INFO("format change: %i->%i",
223 port_out->format->es->audio.sample_rate, sample_rate);
224 port_in->format->es->audio.sample_rate = sample_rate;
225 spdif_send_event_format_changed(component, port_out, port_in->format);
226 mmal_buffer_header_mem_unlock(in);
227 mmal_buffer_header_mem_unlock(out);
228 mmal_queue_put_back(port_in->priv->module->queue, in);
229 mmal_queue_put_back(port_out->priv->module->queue, out);
230 return 0;
231 }
232
233 /* Build our S/PDIF frame. We assume that we need to send
234 * little endian S/PDIF data. */
235 if (in->length > spdif_frame_size - 8)
236 LOG_ERROR("frame too big, truncating (%i/%i bytes)",
237 in->length, spdif_frame_size - 8);
238 frame_size = MMAL_MIN(in->length, spdif_frame_size - 8) / 2;
239 memcpy(out->data, ac3_spdif_header, sizeof(ac3_spdif_header));
240 out->data[5] = in_data[5] & 0x7; /* bsmod */
241 out->data[6] = frame_size & 0xFF;
242 out->data[7] = frame_size >> 8;
243
244 /* Copy the AC3 data, reverting the endianness if required */
245 if (in_data[0] == 0x0B)
246 {
247 for (i = 0; i < frame_size; i++)
248 {
249 out->data[8+i*2] = in_data[in->offset+i*2+1];
250 out->data[8+i*2+1] = in_data[in->offset+i*2];
251 }
252 }
253 else
254 memcpy(out->data + 8, in_data, in->length);
255
256 /* The S/PDIF frame needs to be padded */
257 memset(out->data + 8 + frame_size * 2, 0,
258 spdif_frame_size - frame_size * 2 - 8);
259 mmal_buffer_header_mem_unlock(in);
260 mmal_buffer_header_mem_unlock(out);
261 out->length = spdif_frame_size;
262 end:
263 out->offset = 0;
264 out->flags = in->flags;
265 out->pts = in->pts;
266 out->dts = in->dts;
267
268 /* Send buffers back */
269 in->length = 0;
270 mmal_port_buffer_header_callback(port_in, in);
271 mmal_port_buffer_header_callback(port_out, out);
272 return 1;
273
274 discard:
275 mmal_buffer_header_mem_unlock(in);
276 mmal_buffer_header_mem_unlock(out);
277 in->length = 0;
278 mmal_queue_put_back(port_out->priv->module->queue, out);
279 mmal_port_buffer_header_callback(port_in, in);
280 return 1;
281}
282
283/*****************************************************************************/
284static void spdif_do_processing_loop(MMAL_COMPONENT_T *component)
285{
286 while (spdif_do_processing(component));
287}
288
289/** Destroy a previously created component */
290static MMAL_STATUS_T spdif_component_destroy(MMAL_COMPONENT_T *component)
291{
292 unsigned int i;
293
294 for(i = 0; i < component->input_num; i++)
295 if(component->input[i]->priv->module->queue)
296 mmal_queue_destroy(component->input[i]->priv->module->queue);
297 if(component->input_num)
298 mmal_ports_free(component->input, component->input_num);
299
300 for(i = 0; i < component->output_num; i++)
301 if(component->output[i]->priv->module->queue)
302 mmal_queue_destroy(component->output[i]->priv->module->queue);
303 if(component->output_num)
304 mmal_ports_free(component->output, component->output_num);
305
306 vcos_free(component->priv->module);
307 return MMAL_SUCCESS;
308}
309
310/** Enable processing on a port */
311static MMAL_STATUS_T spdif_port_enable(MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb)
312{
313 MMAL_PARAM_UNUSED(cb);
314
315 /* We need to propagate the buffer requirements when the input port is
316 * enabled */
317 if (port->type == MMAL_PORT_TYPE_INPUT)
318 return port->priv->pf_set_format(port);
319
320 return MMAL_SUCCESS;
321}
322
323/** Flush a port */
324static MMAL_STATUS_T spdif_port_flush(MMAL_PORT_T *port)
325{
326 MMAL_PORT_MODULE_T *port_module = port->priv->module;
327 MMAL_BUFFER_HEADER_T *buffer;
328
329 /* Flush buffers that our component is holding on to */
330 buffer = mmal_queue_get(port_module->queue);
331 while(buffer)
332 {
333 mmal_port_buffer_header_callback(port, buffer);
334 buffer = mmal_queue_get(port_module->queue);
335 }
336
337 return MMAL_SUCCESS;
338}
339
340/** Disable processing on a port */
341static MMAL_STATUS_T spdif_port_disable(MMAL_PORT_T *port)
342{
343 /* We just need to flush our internal queue */
344 return spdif_port_flush(port);
345}
346
347/** Send a buffer header to a port */
348static MMAL_STATUS_T spdif_port_send(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
349{
350 mmal_queue_put(port->priv->module->queue, buffer);
351 mmal_component_action_trigger(port->component);
352 return MMAL_SUCCESS;
353}
354
355/** Set format on input port */
356static MMAL_STATUS_T spdif_input_port_format_commit(MMAL_PORT_T *in)
357{
358 MMAL_COMPONENT_T *component = in->component;
359 MMAL_PORT_T *out = component->output[0];
360
361 /* Sanity check we cope with this format */
362 if (in->format->encoding != MMAL_ENCODING_AC3 &&
363 in->format->encoding != MMAL_ENCODING_EAC3)
364 return MMAL_ENXIO;
365
366 LOG_INFO("%4.4s, %iHz, %ichan, %ibps", (char *)&in->format->encoding,
367 in->format->es->audio.sample_rate, in->format->es->audio.channels,
368 in->format->bitrate);
369
370 /* TODO: should we check the bitrate to see if that fits in an S/PDIF
371 * frame? */
372
373 /* Check if there's anything to propagate to the output port */
374 if (!mmal_format_compare(in->format, out->format))
375 return MMAL_SUCCESS;
376 if (out->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
377 in->format->es->audio.sample_rate ==
378 out->format->es->audio.sample_rate)
379 return MMAL_SUCCESS;
380
381 /* If the output port is not enabled we just need to update its format.
382 * Otherwise we'll have to trigger a format changed event for it. */
383 if (!out->is_enabled)
384 {
385 if (out->format->encoding != MMAL_ENCODING_PCM_SIGNED)
386 mmal_format_copy(out->format, in->format);
387 out->format->es->audio.sample_rate = in->format->es->audio.sample_rate;
388 return MMAL_SUCCESS;
389 }
390
391 /* Send an event on the output port */
392 return spdif_send_event_format_changed(component, out, in->format);
393}
394
395/** Set format on output port */
396static MMAL_STATUS_T spdif_output_port_format_commit(MMAL_PORT_T *out)
397{
398 int supported = 0;
399
400 if (out->format->type == MMAL_ES_TYPE_AUDIO &&
401 out->format->encoding == MMAL_ENCODING_PCM_SIGNED &&
402 out->format->es->audio.channels == 2 &&
403 out->format->es->audio.bits_per_sample == 16)
404 supported = 1;
405 if (out->format->type == MMAL_ES_TYPE_AUDIO &&
406 (out->format->encoding == MMAL_ENCODING_AC3 ||
407 out->format->encoding == MMAL_ENCODING_EAC3))
408 supported = 1;
409
410 if (!supported)
411 {
412 LOG_ERROR("invalid format %4.4s, %ichan, %ibps",
413 (char *)&out->format->encoding, out->format->es->audio.channels,
414 out->format->es->audio.bits_per_sample);
415 return MMAL_EINVAL;
416 }
417
418 out->priv->module->needs_configuring = 0;
419 mmal_component_action_trigger(out->component);
420 return MMAL_SUCCESS;
421}
422
423/** Create an instance of a component */
424static MMAL_STATUS_T mmal_component_create_spdif(const char *name, MMAL_COMPONENT_T *component)
425{
426 MMAL_COMPONENT_MODULE_T *module;
427 MMAL_STATUS_T status = MMAL_ENOMEM;
428 MMAL_PARAM_UNUSED(name);
429
430 /* Allocate the context for our module */
431 component->priv->module = module = vcos_malloc(sizeof(*module), "mmal module");
432 if (!module)
433 return MMAL_ENOMEM;
434 memset(module, 0, sizeof(*module));
435
436 component->priv->pf_destroy = spdif_component_destroy;
437
438 /* Allocate and initialise all the ports for this component */
439 component->input = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_INPUT, sizeof(MMAL_PORT_MODULE_T));
440 if(!component->input)
441 goto error;
442 component->input_num = 1;
443 component->input[0]->priv->pf_enable = spdif_port_enable;
444 component->input[0]->priv->pf_disable = spdif_port_disable;
445 component->input[0]->priv->pf_flush = spdif_port_flush;
446 component->input[0]->priv->pf_send = spdif_port_send;
447 component->input[0]->priv->pf_set_format = spdif_input_port_format_commit;
448 component->input[0]->priv->module->queue = mmal_queue_create();
449 if(!component->input[0]->priv->module->queue)
450 goto error;
451
452 component->output = mmal_ports_alloc(component, 1, MMAL_PORT_TYPE_OUTPUT, sizeof(MMAL_PORT_MODULE_T));
453 if(!component->output)
454 goto error;
455 component->output_num = 1;
456 component->output[0]->priv->pf_enable = spdif_port_enable;
457 component->output[0]->priv->pf_disable = spdif_port_disable;
458 component->output[0]->priv->pf_flush = spdif_port_flush;
459 component->output[0]->priv->pf_send = spdif_port_send;
460 component->output[0]->priv->pf_set_format = spdif_output_port_format_commit;
461 component->output[0]->priv->module->queue = mmal_queue_create();
462 if(!component->output[0]->priv->module->queue)
463 goto error;
464
465 status = mmal_component_action_register(component, spdif_do_processing_loop);
466 if (status != MMAL_SUCCESS)
467 goto error;
468
469 component->input[0]->format->type = MMAL_ES_TYPE_AUDIO;
470 component->input[0]->format->encoding = MMAL_ENCODING_AC3;
471 component->input[0]->buffer_size_min =
472 component->input[0]->buffer_size_recommended = INPUT_MIN_BUFFER_SIZE;
473 component->input[0]->buffer_num_min =
474 component->input[0]->buffer_num_recommended = INPUT_MIN_BUFFER_NUM;
475
476 component->output[0]->format->type = MMAL_ES_TYPE_AUDIO;
477 component->output[0]->format->encoding = MMAL_ENCODING_AC3;
478 component->output[0]->format->es->audio.channels = 2;
479 component->output[0]->format->es->audio.bits_per_sample = 16;
480 component->output[0]->buffer_size_min =
481 component->output[0]->buffer_size_recommended = OUTPUT_MIN_BUFFER_SIZE;
482 component->output[0]->buffer_num_min =
483 component->output[0]->buffer_num_recommended = OUTPUT_MIN_BUFFER_NUM;
484
485 return MMAL_SUCCESS;
486
487 error:
488 spdif_component_destroy(component);
489 return status;
490}
491
492MMAL_CONSTRUCTOR(mmal_register_component_spdif);
493void mmal_register_component_spdif(void)
494{
495 mmal_component_supplier_register("spdif", mmal_component_create_spdif);
496}
497