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/** \file
29 * Implementation of a PCM packetizer.
30 */
31
32#include <stdlib.h>
33#include <string.h>
34
35#include "containers/packetizers.h"
36#include "containers/core/packetizers_private.h"
37#include "containers/core/containers_common.h"
38#include "containers/core/containers_logging.h"
39#include "containers/core/containers_time.h"
40#include "containers/core/containers_utils.h"
41#include "containers/core/containers_bytestream.h"
42
43#define FRAME_SIZE (16*1024) /**< Arbitrary value which is neither too small nor too big */
44#define FACTOR_SHIFT 4 /**< Shift applied to the conversion factor */
45
46VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * );
47
48/*****************************************************************************/
49enum conversion {
50 CONVERSION_NONE = 0,
51 CONVERSION_U8_TO_S16L,
52 CONVERSION_UNKNOWN
53};
54
55typedef struct VC_PACKETIZER_MODULE_T {
56 enum {
57 STATE_NEW_PACKET = 0,
58 STATE_DATA
59 } state;
60
61 unsigned int samples_per_frame;
62 unsigned int bytes_per_sample;
63 unsigned int max_frame_size;
64
65 uint32_t bytes_read;
66 unsigned int frame_size;
67
68 enum conversion conversion;
69 unsigned int conversion_factor;
70} VC_PACKETIZER_MODULE_T;
71
72/*****************************************************************************/
73static VC_CONTAINER_STATUS_T pcm_packetizer_close( VC_PACKETIZER_T *p_ctx )
74{
75 free(p_ctx->priv->module);
76 return VC_CONTAINER_SUCCESS;
77}
78
79/*****************************************************************************/
80static VC_CONTAINER_STATUS_T pcm_packetizer_reset( VC_PACKETIZER_T *p_ctx )
81{
82 VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
83 module->state = STATE_NEW_PACKET;
84 return VC_CONTAINER_SUCCESS;
85}
86
87/*****************************************************************************/
88static void convert_pcm_u8_to_s16l( uint8_t **p_out, uint8_t *in, size_t size)
89{
90 int16_t *out = (int16_t *)*p_out;
91 uint8_t tmp;
92
93 while(size--)
94 {
95 tmp = *in++;
96 *out++ = ((tmp - 128) << 8) | tmp;
97 }
98 *p_out = (uint8_t *)out;
99}
100
101/*****************************************************************************/
102static void convert_pcm( VC_PACKETIZER_T *p_ctx,
103 VC_CONTAINER_BYTESTREAM_T *stream, size_t size, uint8_t *out )
104{
105 VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
106 uint8_t tmp[256];
107 size_t tmp_size;
108
109 while(size)
110 {
111 tmp_size = MIN(sizeof(tmp), size);
112 bytestream_get(stream, tmp, tmp_size);
113 if (module->conversion == CONVERSION_U8_TO_S16L)
114 convert_pcm_u8_to_s16l(&out, tmp, tmp_size);
115 else
116 bytestream_skip(stream, tmp_size);
117 size -= tmp_size;
118 }
119}
120
121/*****************************************************************************/
122static VC_CONTAINER_STATUS_T pcm_packetizer_packetize( VC_PACKETIZER_T *p_ctx,
123 VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags )
124{
125 VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
126 VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
127 VC_CONTAINER_TIME_T *time = &p_ctx->priv->time;
128 int64_t pts, dts;
129 size_t offset, size;
130
131 while(1) switch (module->state)
132 {
133 case STATE_NEW_PACKET:
134 /* Make sure we've got enough data */
135 if(bytestream_size(stream) < module->max_frame_size &&
136 !(flags & VC_PACKETIZER_FLAG_FLUSH))
137 return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
138 if(!bytestream_size(stream))
139 return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
140
141 module->frame_size = bytestream_size(stream);
142 if(module->frame_size > module->max_frame_size)
143 module->frame_size = module->max_frame_size;
144 bytestream_get_timestamps_and_offset(stream, &pts, &dts, &offset, true);
145 vc_container_time_set(time, pts);
146 if(pts != VC_CONTAINER_TIME_UNKNOWN)
147 vc_container_time_add(time, offset / module->bytes_per_sample);
148
149 module->bytes_read = 0;
150 module->state = STATE_DATA;
151 /* fall through to the next state */
152
153 case STATE_DATA:
154 size = module->frame_size - module->bytes_read;
155 out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN;
156 out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
157 out->size = (size * module->conversion_factor) >> FACTOR_SHIFT;
158
159 if(!module->bytes_read)
160 {
161 out->pts = out->dts = vc_container_time_get(time);
162 out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
163 }
164
165 if(flags & VC_PACKETIZER_FLAG_INFO)
166 return VC_CONTAINER_SUCCESS;
167
168 if(flags & VC_PACKETIZER_FLAG_SKIP)
169 {
170 bytestream_skip( stream, size );
171 }
172 else
173 {
174 out->size = MIN(out->size, out->buffer_size);
175 size = (out->size << FACTOR_SHIFT) / module->conversion_factor;
176 out->size = (size * module->conversion_factor) >> FACTOR_SHIFT;
177
178 if(module->conversion != CONVERSION_NONE)
179 convert_pcm(p_ctx, stream, size, out->data);
180 else
181 bytestream_get(stream, out->data, out->size);
182 }
183 module->bytes_read += size;
184
185 if(module->bytes_read == module->frame_size)
186 {
187 vc_container_time_add(time, module->samples_per_frame);
188 module->state = STATE_NEW_PACKET;
189 }
190 return VC_CONTAINER_SUCCESS;
191
192 default:
193 break;
194 };
195
196 return VC_CONTAINER_SUCCESS;
197}
198
199/*****************************************************************************/
200VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T *p_ctx )
201{
202 VC_PACKETIZER_MODULE_T *module;
203 unsigned int bytes_per_sample = 0;
204 enum conversion conversion = CONVERSION_NONE;
205
206 if(p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_BE &&
207 p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_LE &&
208 p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_BE &&
209 p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_LE &&
210 p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_BE &&
211 p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_LE)
212 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
213
214 if(p_ctx->in->type->audio.block_align)
215 bytes_per_sample = p_ctx->in->type->audio.block_align;
216 else if(p_ctx->in->type->audio.bits_per_sample && p_ctx->in->type->audio.channels)
217 bytes_per_sample = p_ctx->in->type->audio.bits_per_sample *
218 p_ctx->in->type->audio.channels / 8;
219
220 if(!bytes_per_sample)
221 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
222
223 /* Check if we support any potential conversion we've been asked to do */
224 if(p_ctx->out->codec_variant)
225 conversion = CONVERSION_UNKNOWN;
226 if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') &&
227 p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE &&
228 p_ctx->in->type->audio.bits_per_sample == 16)
229 conversion = CONVERSION_NONE;
230 if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') &&
231 (p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_LE ||
232 p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_BE) &&
233 p_ctx->in->type->audio.bits_per_sample == 8)
234 conversion = CONVERSION_U8_TO_S16L;
235 if(conversion == CONVERSION_UNKNOWN)
236 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
237
238 p_ctx->priv->module = module = malloc(sizeof(*module));
239 if(!module)
240 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
241 memset(module, 0, sizeof(*module));
242 module->conversion = conversion;
243 module->conversion_factor = 1 << FACTOR_SHIFT;
244
245 p_ctx->out->codec_variant = 0;
246 if(conversion == CONVERSION_U8_TO_S16L)
247 {
248 module->conversion_factor = 2 << FACTOR_SHIFT;
249 p_ctx->out->type->audio.bits_per_sample *= 2;
250 p_ctx->out->type->audio.block_align *= 2;
251 p_ctx->out->codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE;
252 }
253
254 vc_container_time_set_samplerate(&p_ctx->priv->time, p_ctx->in->type->audio.sample_rate, 1);
255
256 p_ctx->max_frame_size = FRAME_SIZE;
257 module->max_frame_size = (FRAME_SIZE << FACTOR_SHIFT) / module->conversion_factor;
258 module->bytes_per_sample = bytes_per_sample;
259 module->samples_per_frame = module->max_frame_size / bytes_per_sample;
260 p_ctx->priv->pf_close = pcm_packetizer_close;
261 p_ctx->priv->pf_packetize = pcm_packetizer_packetize;
262 p_ctx->priv->pf_reset = pcm_packetizer_reset;
263
264 LOG_DEBUG(0, "using pcm audio packetizer");
265 return VC_CONTAINER_SUCCESS;
266}
267
268/*****************************************************************************/
269VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm");
270