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 | /** \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 | |
46 | VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * ); |
47 | |
48 | /*****************************************************************************/ |
49 | enum conversion { |
50 | CONVERSION_NONE = 0, |
51 | CONVERSION_U8_TO_S16L, |
52 | CONVERSION_UNKNOWN |
53 | }; |
54 | |
55 | typedef 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 | /*****************************************************************************/ |
73 | static 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 | /*****************************************************************************/ |
80 | static 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 | /*****************************************************************************/ |
88 | static 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 | /*****************************************************************************/ |
102 | static 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 | /*****************************************************************************/ |
122 | static 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 | /*****************************************************************************/ |
200 | VC_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 | /*****************************************************************************/ |
269 | VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm" ); |
270 | |