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 "bcm_host.h"
29#include "mmal.h"
30#include "util/mmal_default_components.h"
31#include "util/mmal_util_params.h"
32#include "util/mmal_util.h"
33#include "interface/vcos/vcos.h"
34#include <stdio.h>
35
36#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; }
37
38static uint8_t codec_header_bytes[512];
39static unsigned int codec_header_bytes_size = sizeof(codec_header_bytes);
40
41static FILE *source_file;
42
43/* Macros abstracting the I/O, just to make the example code clearer */
44#define SOURCE_OPEN(uri) \
45 source_file = fopen(uri, "rb"); if (!source_file) goto error;
46#define SOURCE_READ_CODEC_CONFIG_DATA(bytes, size) \
47 size = fread(bytes, 1, size, source_file); rewind(source_file)
48#define SOURCE_READ_DATA_INTO_BUFFER(a) \
49 a->length = fread(a->data, 1, a->alloc_size - 128, source_file); \
50 a->offset = 0
51#define SOURCE_CLOSE() \
52 if (source_file) fclose(source_file)
53
54/** Context for our application */
55static struct CONTEXT_T {
56 VCOS_SEMAPHORE_T semaphore;
57 MMAL_QUEUE_T *queue;
58 MMAL_STATUS_T status;
59} context;
60
61static void log_video_format(MMAL_ES_FORMAT_T *format)
62{
63 if (format->type != MMAL_ES_TYPE_VIDEO)
64 return;
65
66 fprintf(stderr, "fourcc: %4.4s, width: %i, height: %i, (%i,%i,%i,%i)\n",
67 (char *)&format->encoding,
68 format->es->video.width, format->es->video.height,
69 format->es->video.crop.x, format->es->video.crop.y,
70 format->es->video.crop.width, format->es->video.crop.height);
71}
72
73/** Callback from the control port.
74 * Component is sending us an event. */
75static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
76{
77 struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
78
79 switch (buffer->cmd)
80 {
81 case MMAL_EVENT_EOS:
82 /* Only sink component generate EOS events */
83 break;
84 case MMAL_EVENT_ERROR:
85 /* Something went wrong. Signal this to the application */
86 ctx->status = *(MMAL_STATUS_T *)buffer->data;
87 break;
88 default:
89 break;
90 }
91
92 /* Done with the event, recycle it */
93 mmal_buffer_header_release(buffer);
94
95 /* Kick the processing thread */
96 vcos_semaphore_post(&ctx->semaphore);
97}
98
99/** Callback from the input port.
100 * Buffer has been consumed and is available to be used again. */
101static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
102{
103 struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
104
105 /* The decoder is done with the data, just recycle the buffer header into its pool */
106 mmal_buffer_header_release(buffer);
107
108 /* Kick the processing thread */
109 vcos_semaphore_post(&ctx->semaphore);
110}
111
112/** Callback from the output port.
113 * Buffer has been produced by the port and is available for processing. */
114static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
115{
116 struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
117
118 /* Queue the decoded video frame */
119 mmal_queue_put(ctx->queue, buffer);
120
121 /* Kick the processing thread */
122 vcos_semaphore_post(&ctx->semaphore);
123}
124
125int main(int argc, char **argv)
126{
127 MMAL_STATUS_T status = MMAL_EINVAL;
128 MMAL_COMPONENT_T *decoder = 0;
129 MMAL_POOL_T *pool_in = 0, *pool_out = 0;
130 MMAL_BOOL_T eos_sent = MMAL_FALSE, eos_received = MMAL_FALSE;
131 unsigned int count;
132
133 if (argc < 2)
134 {
135 fprintf(stderr, "invalid arguments\n");
136 return -1;
137 }
138
139 bcm_host_init();
140
141 vcos_semaphore_create(&context.semaphore, "example", 1);
142
143 SOURCE_OPEN(argv[1]);
144
145 /* Create the decoder component.
146 * This specific component exposes 2 ports (1 input and 1 output). Like most components
147 * its expects the format of its input port to be set by the client in order for it to
148 * know what kind of data it will be fed. */
149 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder);
150 CHECK_STATUS(status, "failed to create decoder");
151
152 /* Enable control port so we can receive events from the component */
153 decoder->control->userdata = (void *)&context;
154 status = mmal_port_enable(decoder->control, control_callback);
155 CHECK_STATUS(status, "failed to enable control port");
156
157 /* Get statistics on the input port */
158 MMAL_PARAMETER_CORE_STATISTICS_T stats = {{0}};
159 stats.hdr.id = MMAL_PARAMETER_CORE_STATISTICS;
160 stats.hdr.size = sizeof(MMAL_PARAMETER_CORE_STATISTICS_T);
161 status = mmal_port_parameter_get(decoder->input[0], &stats.hdr);
162 CHECK_STATUS(status, "failed to get stats");
163 fprintf(stderr, "stats: %i, %i", stats.stats.buffer_count, stats.stats.max_delay);
164
165 /* Set the zero-copy parameter on the input port */
166 MMAL_PARAMETER_BOOLEAN_T zc = {{MMAL_PARAMETER_ZERO_COPY, sizeof(zc)}, MMAL_TRUE};
167 status = mmal_port_parameter_set(decoder->input[0], &zc.hdr);
168 fprintf(stderr, "status: %i\n", status);
169
170 /* Set the zero-copy parameter on the output port */
171 status = mmal_port_parameter_set_boolean(decoder->output[0], MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
172 fprintf(stderr, "status: %i\n", status);
173
174 /* Set format of video decoder input port */
175 MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
176 format_in->type = MMAL_ES_TYPE_VIDEO;
177 format_in->encoding = MMAL_ENCODING_H264;
178 format_in->es->video.width = 1280;
179 format_in->es->video.height = 720;
180 format_in->es->video.frame_rate.num = 30;
181 format_in->es->video.frame_rate.den = 1;
182 format_in->es->video.par.num = 1;
183 format_in->es->video.par.den = 1;
184 /* If the data is known to be framed then the following flag should be set:
185 * format_in->flags |= MMAL_ES_FORMAT_FLAG_FRAMED; */
186
187 SOURCE_READ_CODEC_CONFIG_DATA(codec_header_bytes, codec_header_bytes_size);
188 status = mmal_format_extradata_alloc(format_in, codec_header_bytes_size);
189 CHECK_STATUS(status, "failed to allocate extradata");
190 format_in->extradata_size = codec_header_bytes_size;
191 if (format_in->extradata_size)
192 memcpy(format_in->extradata, codec_header_bytes, format_in->extradata_size);
193
194 status = mmal_port_format_commit(decoder->input[0]);
195 CHECK_STATUS(status, "failed to commit format");
196
197 /* Our decoder can do internal colour conversion, ask for a conversion to RGB565 */
198 MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
199 format_out->encoding = MMAL_ENCODING_RGB16;
200 status = mmal_port_format_commit(decoder->output[0]);
201 CHECK_STATUS(status, "failed to commit format");
202
203 /* Display the output port format */
204 fprintf(stderr, "%s\n", decoder->output[0]->name);
205 fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding);
206 fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate,
207 !!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
208 fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata);
209 fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
210 format_out->es->video.width, format_out->es->video.height,
211 format_out->es->video.crop.x, format_out->es->video.crop.y,
212 format_out->es->video.crop.width, format_out->es->video.crop.height);
213
214 /* The format of both ports is now set so we can get their buffer requirements and create
215 * our buffer headers. We use the buffer pool API to create these. */
216 decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_min;
217 decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_min;
218 decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_min;
219 decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_min;
220 pool_in = mmal_port_pool_create(decoder->output[0],
221 decoder->input[0]->buffer_num,
222 decoder->input[0]->buffer_size);
223 pool_out = mmal_port_pool_create(decoder->output[0],
224 decoder->output[0]->buffer_num,
225 decoder->output[0]->buffer_size);
226
227 /* Create a queue to store our decoded video frames. The callback we will get when
228 * a frame has been decoded will put the frame into this queue. */
229 context.queue = mmal_queue_create();
230
231 /* Store a reference to our context in each port (will be used during callbacks) */
232 decoder->input[0]->userdata = (void *)&context;
233 decoder->output[0]->userdata = (void *)&context;
234
235 /* Enable all the input port and the output port.
236 * The callback specified here is the function which will be called when the buffer header
237 * we sent to the component has been processed. */
238 status = mmal_port_enable(decoder->input[0], input_callback);
239 CHECK_STATUS(status, "failed to enable input port");
240 status = mmal_port_enable(decoder->output[0], output_callback);
241 CHECK_STATUS(status, "failed to enable output port");
242
243 /* Component won't start processing data until it is enabled. */
244 status = mmal_component_enable(decoder);
245 CHECK_STATUS(status, "failed to enable component");
246
247 /* Start decoding */
248 fprintf(stderr, "start decoding\n");
249
250 /* This is the main processing loop */
251 for (count = 0; !eos_received && count < 500; count++)
252 {
253 MMAL_BUFFER_HEADER_T *buffer;
254
255 /* Wait for buffer headers to be available on either of the decoder ports */
256 vcos_semaphore_wait(&context.semaphore);
257
258 /* Check for errors */
259 if (context.status != MMAL_SUCCESS)
260 {
261 fprintf(stderr, "Aborting due to error\n");
262 break;
263 }
264
265 /* Send data to decode to the input port of the video decoder */
266 if (!eos_sent && (buffer = mmal_queue_get(pool_in->queue)) != NULL)
267 {
268 SOURCE_READ_DATA_INTO_BUFFER(buffer);
269 if(!buffer->length) eos_sent = MMAL_TRUE;
270
271 buffer->flags = buffer->length ? 0 : MMAL_BUFFER_HEADER_FLAG_EOS;
272 buffer->pts = buffer->dts = MMAL_TIME_UNKNOWN;
273 fprintf(stderr, "sending %i bytes\n", (int)buffer->length);
274 status = mmal_port_send_buffer(decoder->input[0], buffer);
275 CHECK_STATUS(status, "failed to send buffer");
276 }
277
278 /* Get our decoded frames */
279 while ((buffer = mmal_queue_get(context.queue)) != NULL)
280 {
281 /* We have a frame, do something with it (why not display it for instance?).
282 * Once we're done with it, we release it. It will automatically go back
283 * to its original pool so it can be reused for a new video frame.
284 */
285 eos_received = buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS;
286
287 if (buffer->cmd)
288 {
289 fprintf(stderr, "received event %4.4s\n", (char *)&buffer->cmd);
290 if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
291 {
292 MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(buffer);
293 if (event)
294 {
295 fprintf(stderr, "----------Port format changed----------\n");
296 log_video_format(decoder->output[0]->format);
297 fprintf(stderr, "-----------------to---------------------\n");
298 log_video_format(event->format);
299 fprintf(stderr, " buffers num (opt %i, min %i), size (opt %i, min: %i)\n",
300 event->buffer_num_recommended, event->buffer_num_min,
301 event->buffer_size_recommended, event->buffer_size_min);
302 fprintf(stderr, "----------------------------------------\n");
303 }
304
305 //Assume we can't reuse the buffers, so have to disable, destroy
306 //pool, create new pool, enable port, feed in buffers.
307 status = mmal_port_disable(decoder->output[0]);
308 CHECK_STATUS(status, "failed to disable port");
309
310 //Clear the queue of all buffers
311 while(mmal_queue_length(pool_out->queue) != pool_out->headers_num)
312 {
313 MMAL_BUFFER_HEADER_T *buf;
314 fprintf(stderr, "Wait for buffers to be returned. Have %d of %d buffers\n",
315 mmal_queue_length(pool_out->queue), pool_out->headers_num);
316 vcos_semaphore_wait(&context.semaphore);
317 fprintf(stderr, "Got semaphore\n");
318 buf = mmal_queue_get(context.queue);
319 mmal_buffer_header_release(buf);
320 }
321 fprintf(stderr, "Got all buffers\n");
322
323 mmal_port_pool_destroy(decoder->output[0], pool_out);
324 status = mmal_format_full_copy(decoder->output[0]->format, event->format);
325 CHECK_STATUS(status, "failed to copy port format");
326 status = mmal_port_format_commit(decoder->output[0]);
327 CHECK_STATUS(status, "failed to commit port format");
328
329 pool_out = mmal_port_pool_create(decoder->output[0],
330 decoder->output[0]->buffer_num,
331 decoder->output[0]->buffer_size);
332
333 status = mmal_port_enable(decoder->output[0], output_callback);
334 CHECK_STATUS(status, "failed to enable port");
335 //Allow the following loop to send all the buffers back to the decoder
336 }
337
338 }
339 else
340 fprintf(stderr, "decoded frame (flags %x)\n", buffer->flags);
341 mmal_buffer_header_release(buffer);
342 }
343
344 /* Send empty buffers to the output port of the decoder */
345 while ((buffer = mmal_queue_get(pool_out->queue)) != NULL)
346 {
347 status = mmal_port_send_buffer(decoder->output[0], buffer);
348 CHECK_STATUS(status, "failed to send buffer");
349 }
350 }
351
352 /* Stop decoding */
353 fprintf(stderr, "stop decoding - count %d, eos_received %d\n", count, eos_received);
354
355 /* Stop everything. Not strictly necessary since mmal_component_destroy()
356 * will do that anyway */
357 mmal_port_disable(decoder->input[0]);
358 mmal_port_disable(decoder->output[0]);
359 mmal_component_disable(decoder);
360
361 error:
362 /* Cleanup everything */
363 if (pool_in)
364 mmal_port_pool_destroy(decoder->input[0], pool_in);
365 if (pool_out)
366 mmal_port_pool_destroy(decoder->output[0], pool_out);
367 if (decoder)
368 mmal_component_destroy(decoder);
369 if (context.queue)
370 mmal_queue_destroy(context.queue);
371
372 SOURCE_CLOSE();
373 vcos_semaphore_delete(&context.semaphore);
374 return status == MMAL_SUCCESS ? 0 : -1;
375}
376