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 "interface/vcos/vcos.h"
32#include <stdio.h>
33
34#define CHECK_STATUS(status, msg) if (status != MMAL_SUCCESS) { fprintf(stderr, msg"\n"); goto error; }
35
36static uint8_t codec_header_bytes[512];
37static unsigned int codec_header_bytes_size = sizeof(codec_header_bytes);
38
39static FILE *source_file;
40
41/* Macros abstracting the I/O, just to make the example code clearer */
42#define SOURCE_OPEN(uri) \
43 source_file = fopen(uri, "rb"); if (!source_file) goto error;
44#define SOURCE_READ_CODEC_CONFIG_DATA(bytes, size) \
45 size = fread(bytes, 1, size, source_file); rewind(source_file)
46#define SOURCE_READ_DATA_INTO_BUFFER(a) \
47 a->length = fread(a->data, 1, a->alloc_size - 128, source_file); \
48 a->offset = 0; a->pts = a->dts = MMAL_TIME_UNKNOWN
49#define SOURCE_CLOSE() \
50 if (source_file) fclose(source_file)
51
52/** Context for our application */
53static struct CONTEXT_T {
54 VCOS_SEMAPHORE_T semaphore;
55 MMAL_QUEUE_T *queue;
56} context;
57
58/** Callback from the input port.
59 * Buffer has been consumed and is available to be used again. */
60static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
61{
62 struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
63
64 /* The decoder is done with the data, just recycle the buffer header into its pool */
65 mmal_buffer_header_release(buffer);
66
67 /* Kick the processing thread */
68 vcos_semaphore_post(&ctx->semaphore);
69}
70
71/** Callback from the output port.
72 * Buffer has been produced by the port and is available for processing. */
73static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
74{
75 struct CONTEXT_T *ctx = (struct CONTEXT_T *)port->userdata;
76
77 /* Queue the decoded video frame */
78 mmal_queue_put(ctx->queue, buffer);
79
80 /* Kick the processing thread */
81 vcos_semaphore_post(&ctx->semaphore);
82}
83
84int main(int argc, char **argv)
85{
86 MMAL_STATUS_T status = MMAL_EINVAL;
87 MMAL_COMPONENT_T *decoder = 0;
88 MMAL_POOL_T *pool_in = 0, *pool_out = 0;
89 unsigned int count;
90
91 if (argc < 2)
92 {
93 fprintf(stderr, "invalid arguments\n");
94 return -1;
95 }
96
97 bcm_host_init();
98
99 vcos_semaphore_create(&context.semaphore, "example", 1);
100
101 SOURCE_OPEN(argv[1]);
102
103 /* Create the decoder component.
104 * This specific component exposes 2 ports (1 input and 1 output). Like most components
105 * its expects the format of its input port to be set by the client in order for it to
106 * know what kind of data it will be fed. */
107 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder);
108 CHECK_STATUS(status, "failed to create decoder");
109
110 /* Set format of video decoder input port */
111 MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
112 format_in->type = MMAL_ES_TYPE_VIDEO;
113 format_in->encoding = MMAL_ENCODING_H264;
114 format_in->es->video.width = 1280;
115 format_in->es->video.height = 720;
116 format_in->es->video.frame_rate.num = 30;
117 format_in->es->video.frame_rate.den = 1;
118 format_in->es->video.par.num = 1;
119 format_in->es->video.par.den = 1;
120 /* If the data is known to be framed then the following flag should be set:
121 * format_in->flags |= MMAL_ES_FORMAT_FLAG_FRAMED; */
122
123 SOURCE_READ_CODEC_CONFIG_DATA(codec_header_bytes, codec_header_bytes_size);
124 status = mmal_format_extradata_alloc(format_in, codec_header_bytes_size);
125 CHECK_STATUS(status, "failed to allocate extradata");
126 format_in->extradata_size = codec_header_bytes_size;
127 if (format_in->extradata_size)
128 memcpy(format_in->extradata, codec_header_bytes, format_in->extradata_size);
129
130 status = mmal_port_format_commit(decoder->input[0]);
131 CHECK_STATUS(status, "failed to commit format");
132
133 /* Display the output port format */
134 MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
135 fprintf(stderr, "%s\n", decoder->output[0]->name);
136 fprintf(stderr, " type: %i, fourcc: %4.4s\n", format_out->type, (char *)&format_out->encoding);
137 fprintf(stderr, " bitrate: %i, framed: %i\n", format_out->bitrate,
138 !!(format_out->flags & MMAL_ES_FORMAT_FLAG_FRAMED));
139 fprintf(stderr, " extra data: %i, %p\n", format_out->extradata_size, format_out->extradata);
140 fprintf(stderr, " width: %i, height: %i, (%i,%i,%i,%i)\n",
141 format_out->es->video.width, format_out->es->video.height,
142 format_out->es->video.crop.x, format_out->es->video.crop.y,
143 format_out->es->video.crop.width, format_out->es->video.crop.height);
144
145 /* The format of both ports is now set so we can get their buffer requirements and create
146 * our buffer headers. We use the buffer pool API to create these. */
147 decoder->input[0]->buffer_num = decoder->input[0]->buffer_num_min;
148 decoder->input[0]->buffer_size = decoder->input[0]->buffer_size_min;
149 decoder->output[0]->buffer_num = decoder->output[0]->buffer_num_min;
150 decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_min;
151 pool_in = mmal_pool_create(decoder->input[0]->buffer_num,
152 decoder->input[0]->buffer_size);
153 pool_out = mmal_pool_create(decoder->output[0]->buffer_num,
154 decoder->output[0]->buffer_size);
155
156 /* Create a queue to store our decoded video frames. The callback we will get when
157 * a frame has been decoded will put the frame into this queue. */
158 context.queue = mmal_queue_create();
159
160 /* Store a reference to our context in each port (will be used during callbacks) */
161 decoder->input[0]->userdata = (void *)&context;
162 decoder->output[0]->userdata = (void *)&context;
163
164 /* Enable all the input port and the output port.
165 * The callback specified here is the function which will be called when the buffer header
166 * we sent to the component has been processed. */
167 status = mmal_port_enable(decoder->input[0], input_callback);
168 CHECK_STATUS(status, "failed to enable input port");
169 status = mmal_port_enable(decoder->output[0], output_callback);
170 CHECK_STATUS(status, "failed to enable output port");
171
172 /* Component won't start processing data until it is enabled. */
173 status = mmal_component_enable(decoder);
174 CHECK_STATUS(status, "failed to enable component");
175
176 /* Start decoding */
177 fprintf(stderr, "start decoding\n");
178
179 /* This is the main processing loop */
180 for (count = 0; count < 500; count++)
181 {
182 MMAL_BUFFER_HEADER_T *buffer;
183
184 /* Wait for buffer headers to be available on either of the decoder ports */
185 vcos_semaphore_wait(&context.semaphore);
186
187 /* Send data to decode to the input port of the video decoder */
188 if ((buffer = mmal_queue_get(pool_in->queue)) != NULL)
189 {
190 SOURCE_READ_DATA_INTO_BUFFER(buffer);
191 if (!buffer->length)
192 break;
193
194 fprintf(stderr, "sending %i bytes\n", (int)buffer->length);
195 status = mmal_port_send_buffer(decoder->input[0], buffer);
196 CHECK_STATUS(status, "failed to send buffer");
197 }
198
199 /* Get our decoded frames */
200 while ((buffer = mmal_queue_get(context.queue)) != NULL)
201 {
202 /* We have a frame, do something with it (why not display it for instance?).
203 * Once we're done with it, we release it. It will automatically go back
204 * to its original pool so it can be reused for a new video frame.
205 */
206 fprintf(stderr, "decoded frame\n");
207 mmal_buffer_header_release(buffer);
208 }
209
210 /* Send empty buffers to the output port of the decoder */
211 while ((buffer = mmal_queue_get(pool_out->queue)) != NULL)
212 {
213 status = mmal_port_send_buffer(decoder->output[0], buffer);
214 CHECK_STATUS(status, "failed to send buffer");
215 }
216 }
217
218 /* Stop decoding */
219 fprintf(stderr, "stop decoding\n");
220
221 /* Stop everything. Not strictly necessary since mmal_component_destroy()
222 * will do that anyway */
223 mmal_port_disable(decoder->input[0]);
224 mmal_port_disable(decoder->output[0]);
225 mmal_component_disable(decoder);
226
227 error:
228 /* Cleanup everything */
229 if (decoder)
230 mmal_component_destroy(decoder);
231 if (pool_in)
232 mmal_pool_destroy(pool_in);
233 if (pool_out)
234 mmal_pool_destroy(pool_out);
235 if (context.queue)
236 mmal_queue_destroy(context.queue);
237
238 SOURCE_CLOSE();
239 vcos_semaphore_delete(&context.semaphore);
240 return status == MMAL_SUCCESS ? 0 : -1;
241}
242