1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, 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 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON 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
25 SOFTWARE, 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 /*****************************************************************************/
44 typedef struct MMAL_COMPONENT_MODULE_T
45 {
46    MMAL_STATUS_T status; /**< current status of the component */
47 
48 } MMAL_COMPONENT_MODULE_T;
49 
50 typedef 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 
spdif_send_event_format_changed(MMAL_COMPONENT_T * component,MMAL_PORT_T * port,MMAL_ES_FORMAT_T * format)61 static 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 */
spdif_do_processing(MMAL_COMPONENT_T * component)98 static 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: %lld, 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 /*****************************************************************************/
spdif_do_processing_loop(MMAL_COMPONENT_T * component)284 static void spdif_do_processing_loop(MMAL_COMPONENT_T *component)
285 {
286    while (spdif_do_processing(component));
287 }
288 
289 /** Destroy a previously created component */
spdif_component_destroy(MMAL_COMPONENT_T * component)290 static 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 */
spdif_port_enable(MMAL_PORT_T * port,MMAL_PORT_BH_CB_T cb)311 static 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 */
spdif_port_flush(MMAL_PORT_T * port)324 static 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 */
spdif_port_disable(MMAL_PORT_T * port)341 static 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 */
spdif_port_send(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)348 static 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 */
spdif_input_port_format_commit(MMAL_PORT_T * in)356 static 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 */
spdif_output_port_format_commit(MMAL_PORT_T * out)396 static 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  */
mmal_component_create_spdif(const char * name,MMAL_COMPONENT_T * component)424 static 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 
492 MMAL_CONSTRUCTOR(mmal_register_component_spdif);
mmal_register_component_spdif(void)493 void mmal_register_component_spdif(void)
494 {
495    mmal_component_supplier_register("spdif", mmal_component_create_spdif);
496 }
497