]> git.sesse.net Git - vlc/blob - modules/hw/mmal/deinterlace.c
picture_pool: remove ..._Delete(), use ..._Release() directly
[vlc] / modules / hw / mmal / deinterlace.c
1 /*****************************************************************************
2  * mmal.c: MMAL-based deinterlace plugin for Raspberry Pi
3  *****************************************************************************
4  * Copyright © 2014 jusst technologies GmbH
5  * $Id$
6  *
7  * Authors: Julian Scheel <julian@jusst.de>
8  *          Dennis Hamester <dennis.hamester@gmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <vlc_picture_pool.h>
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_filter.h>
33
34 #include "mmal_picture.h"
35
36 #include <bcm_host.h>
37 #include <interface/mmal/mmal.h>
38 #include <interface/mmal/util/mmal_util.h>
39 #include <interface/mmal/util/mmal_default_components.h>
40
41 #define NUM_EXTRA_BUFFERS 2
42 #define MIN_NUM_BUFFERS_IN_TRANSIT 2
43
44 static int Open(filter_t *filter);
45 static void Close(filter_t *filter);
46
47 vlc_module_begin()
48     set_shortname(N_("MMAL deinterlace"))
49     set_description(N_("MMAL-based deinterlace filter plugin"))
50     set_capability("video filter2", 10)
51     set_category(CAT_VIDEO)
52     set_subcategory(SUBCAT_VIDEO_VFILTER)
53     set_callbacks(Open, Close)
54     add_shortcut("deinterlace")
55 vlc_module_end()
56
57 struct filter_sys_t {
58     MMAL_COMPONENT_T *component;
59     MMAL_PORT_T *input;
60     MMAL_POOL_T *input_pool;
61     MMAL_PORT_T *output;
62     MMAL_POOL_T *output_pool;
63
64     picture_pool_t *picture_pool;
65     picture_t **pictures;
66
67     MMAL_QUEUE_T *filtered_pictures;
68     vlc_mutex_t mutex;
69 };
70
71 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
72 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
73 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
74 static picture_t *deinterlace(filter_t *filter, picture_t *picture);
75
76 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
77
78 static int create_picture_pool(filter_t *filter)
79 {
80     picture_pool_configuration_t picture_pool_cfg;
81     filter_sys_t *sys = filter->p_sys;
82     picture_resource_t picture_res;
83     int ret = 0;
84
85     memset(&picture_res, 0, sizeof(picture_resource_t));
86     sys->pictures = calloc(sys->output->buffer_num, sizeof(picture_t *));
87     if (!sys->pictures) {
88         ret = -ENOMEM;
89         goto out;
90     }
91
92     for (unsigned i = 0; i < sys->output->buffer_num; i++) {
93         picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
94         if (!picture_res.p_sys) {
95             ret = -ENOMEM;
96             goto out;
97         }
98
99         picture_res.p_sys->owner = (vlc_object_t *)filter;
100         picture_res.p_sys->queue = sys->output_pool->queue;
101
102         sys->pictures[i] = picture_NewFromResource(&filter->fmt_out.video,
103                 &picture_res);
104         if (!sys->pictures[i]) {
105             free(picture_res.p_sys);
106             ret = -ENOMEM;
107             goto out;
108         }
109     }
110
111     memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
112     picture_pool_cfg.picture_count = sys->output->buffer_num;
113     picture_pool_cfg.picture = sys->pictures;
114     picture_pool_cfg.lock = mmal_picture_lock;
115     picture_pool_cfg.unlock = mmal_picture_unlock;
116
117     sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
118     if (!sys->picture_pool) {
119         msg_Err(filter, "Failed to create picture pool");
120         ret = -ENOMEM;
121         goto out;
122     }
123
124 out:
125     return ret;
126 }
127
128 static int Open(filter_t *filter)
129 {
130     int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
131             (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
132             filter->fmt_in.video.i_frame_rate : 0;
133
134     MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
135             { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
136             MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
137             2,
138             { 3, frame_duration }
139     };
140     MMAL_PARAMETER_UINT32_T extra_buffers;
141
142     int ret = VLC_SUCCESS;
143     MMAL_STATUS_T status;
144     filter_sys_t *sys;
145
146     msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d!", frame_duration);
147
148     if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
149         return VLC_EGENERIC;
150
151     if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
152         return VLC_EGENERIC;
153
154     sys = calloc(1, sizeof(filter_sys_t));
155     if (!sys)
156         return VLC_ENOMEM;
157     filter->p_sys = sys;
158
159     bcm_host_init();
160
161     status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
162     if (status != MMAL_SUCCESS) {
163         msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
164                 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
165         ret = VLC_EGENERIC;
166         goto out;
167     }
168
169     status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
170     if (status != MMAL_SUCCESS) {
171         msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
172                 MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
173         ret = VLC_EGENERIC;
174         goto out;
175     }
176
177     sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
178     status = mmal_port_enable(sys->component->control, control_port_cb);
179     if (status != MMAL_SUCCESS) {
180         msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
181                 sys->component->control->name, status, mmal_status_to_string(status));
182         ret = VLC_EGENERIC;
183         goto out;
184     }
185
186     sys->input = sys->component->input[0];
187     sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
188     if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
189         sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
190     sys->input->format->es->video.width = filter->fmt_in.video.i_width;
191     sys->input->format->es->video.height = filter->fmt_in.video.i_height;
192     sys->input->format->es->video.crop.x = 0;
193     sys->input->format->es->video.crop.y = 0;
194     sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
195     sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
196     sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
197     sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
198
199     es_format_Copy(&filter->fmt_out, &filter->fmt_in);
200     filter->fmt_out.video.i_frame_rate *= 2;
201
202     status = mmal_port_format_commit(sys->input);
203     if (status != MMAL_SUCCESS) {
204         msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
205                         sys->input->name, status, mmal_status_to_string(status));
206         ret = VLC_EGENERIC;
207         goto out;
208     }
209     sys->input->buffer_size = sys->input->buffer_size_recommended;
210
211     sys->input->buffer_num = sys->input->buffer_num_recommended;
212     status = mmal_port_enable(sys->input, input_port_cb);
213     if (status != MMAL_SUCCESS) {
214         msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
215                 sys->input->name, status, mmal_status_to_string(status));
216         ret = VLC_EGENERIC;
217         goto out;
218     }
219
220     sys->output = sys->component->output[0];
221     sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
222     mmal_format_full_copy(sys->output->format, sys->input->format);
223
224     if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
225         extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
226         extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
227         extra_buffers.value = NUM_EXTRA_BUFFERS;
228         status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
229         if (status != MMAL_SUCCESS) {
230             msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
231                     status, mmal_status_to_string(status));
232             ret = VLC_EGENERIC;
233             goto out;
234         }
235     }
236
237     status = mmal_port_format_commit(sys->output);
238     if (status != MMAL_SUCCESS) {
239         msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
240                         sys->input->name, status, mmal_status_to_string(status));
241         ret = VLC_EGENERIC;
242         goto out;
243     }
244
245     sys->output->buffer_num = sys->output->buffer_num_recommended;
246     status = mmal_port_enable(sys->output, output_port_cb);
247     if (status != MMAL_SUCCESS) {
248         msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
249                 sys->output->name, status, mmal_status_to_string(status));
250         ret = VLC_EGENERIC;
251         goto out;
252     }
253
254     status = mmal_component_enable(sys->component);
255     if (status != MMAL_SUCCESS) {
256         msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
257                 sys->component->name, status, mmal_status_to_string(status));
258         ret = VLC_EGENERIC;
259         goto out;
260     }
261
262     sys->output_pool = mmal_pool_create(sys->output->buffer_num,
263             sys->output->buffer_size);
264     if (!sys->output_pool) {
265         msg_Err(filter, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
266                         sys->output->buffer_num, sys->output->buffer_size);
267         goto out;
268     }
269     sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
270     sys->filtered_pictures = mmal_queue_create();
271
272     ret = create_picture_pool(filter);
273     if (ret != VLC_SUCCESS)
274         goto out;
275
276     filter->pf_video_filter = deinterlace;
277     vlc_mutex_init_recursive(&sys->mutex);
278
279 out:
280     if (ret != VLC_SUCCESS)
281         Close(filter);
282
283     return ret;
284 }
285
286 static void Close(filter_t *filter)
287 {
288     filter_sys_t *sys = filter->p_sys;
289     MMAL_BUFFER_HEADER_T *buffer;
290
291     if (!sys)
292         return;
293
294     if (sys->component && sys->component->control->is_enabled)
295         mmal_port_disable(sys->component->control);
296
297     if (sys->input && sys->input->is_enabled)
298         mmal_port_disable(sys->input);
299
300     if (sys->output && sys->output->is_enabled)
301         mmal_port_disable(sys->output);
302
303     if (sys->component && sys->component->is_enabled)
304         mmal_component_disable(sys->component);
305
306     while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
307         picture_t *pic = (picture_t *)buffer->user_data;
308         picture_Release(pic);
309
310         buffer->user_data = NULL;
311         buffer->alloc_size = 0;
312         buffer->data = NULL;
313         mmal_buffer_header_release(buffer);
314     }
315
316     if (sys->filtered_pictures)
317         mmal_queue_destroy(sys->filtered_pictures);
318
319     if (sys->input_pool)
320         mmal_pool_destroy(sys->input_pool);
321
322     if (sys->output_pool)
323         mmal_pool_destroy(sys->output_pool);
324
325     if (sys->component)
326         mmal_component_release(sys->component);
327
328     if (sys->picture_pool)
329         picture_pool_Release(sys->picture_pool);
330
331     vlc_mutex_destroy(&sys->mutex);
332     free(sys->pictures);
333     free(sys);
334
335     bcm_host_deinit();
336 }
337
338 static int send_output_buffer(filter_t *filter)
339 {
340     filter_sys_t *sys = filter->p_sys;
341     MMAL_BUFFER_HEADER_T *buffer;
342     MMAL_STATUS_T status;
343     picture_t *picture;
344     int ret = 0;
345
346     picture = picture_pool_Get(sys->picture_pool);
347     if (!picture) {
348         msg_Warn(filter, "Failed to get new picture");
349         ret = -1;
350         goto out;
351     }
352     picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
353     picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
354
355     buffer = picture->p_sys->buffer;
356     buffer->cmd = 0;
357     buffer->alloc_size = sys->output->buffer_size;
358
359     status = mmal_port_send_buffer(sys->output, buffer);
360     if (status != MMAL_SUCCESS) {
361         msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
362                 status, mmal_status_to_string(status));
363         picture_Release(picture);
364         ret = -1;
365         goto out;
366     }
367
368 out:
369     return ret;
370 }
371
372 static void fill_output_port(filter_t *filter)
373 {
374     filter_sys_t *sys = filter->p_sys;
375     /* allow at least 2 buffers in transit */
376     unsigned max_buffers_in_transit = __MAX(sys->output->buffer_num,
377             MIN_NUM_BUFFERS_IN_TRANSIT);
378     unsigned buffers_available = mmal_queue_length(sys->output_pool->queue);
379     unsigned buffers_in_transit = sys->output_pool->headers_num - buffers_available -
380         mmal_queue_length(sys->filtered_pictures);
381     unsigned buffers_to_send = max_buffers_in_transit - buffers_in_transit;
382     unsigned i;
383
384     if (buffers_to_send > buffers_available)
385         buffers_to_send = buffers_available;
386
387 #ifndef NDEBUG
388     msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
389                     buffers_to_send, buffers_available, buffers_in_transit,
390                     sys->output->buffer_num);
391 #endif
392     for (i = 0; i < buffers_to_send; ++i) {
393         if (send_output_buffer(filter) < 0)
394             break;
395     }
396 }
397
398 static picture_t *deinterlace(filter_t *filter, picture_t *picture)
399 {
400     filter_sys_t *sys = filter->p_sys;
401     MMAL_BUFFER_HEADER_T *buffer;
402     picture_t *out_picture = NULL;
403     picture_t *ret = NULL;
404     MMAL_STATUS_T status;
405
406     /*
407      * Send output buffers
408      */
409     if (sys->output_pool) {
410         int i = 0;
411         vlc_mutex_lock(&sys->mutex);
412         while((buffer = mmal_queue_get(sys->filtered_pictures))) {
413             i++;
414             if (!out_picture) {
415                 out_picture = (picture_t *)buffer->user_data;
416                 ret = out_picture;
417             } else {
418                 out_picture->p_next = (picture_t *)buffer->user_data;
419                 out_picture = out_picture->p_next;
420             }
421             out_picture->date = buffer->pts;
422         }
423         if (out_picture)
424             out_picture->p_next = NULL;
425         fill_output_port(filter);
426         vlc_mutex_unlock(&sys->mutex);
427     }
428
429     /*
430      * Process input
431      */
432     if (!picture)
433         return ret;
434
435     vlc_mutex_lock(&sys->mutex);
436     buffer = mmal_queue_timedwait(sys->input_pool->queue, 2);
437     if (!buffer) {
438         msg_Err(filter, "Failed to retrieve buffer header for input picture");
439         goto out;
440     }
441
442     mmal_buffer_header_reset(buffer);
443     buffer->user_data = picture;
444     buffer->pts = picture->date;
445     buffer->cmd = 0;
446     buffer->alloc_size = sys->input->buffer_size;
447     buffer->length = sys->input->buffer_size;
448     buffer->data = picture->p[0].p_pixels;
449
450     status = mmal_port_send_buffer(sys->input, buffer);
451     if (status != MMAL_SUCCESS) {
452         msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
453                 status, mmal_status_to_string(status));
454     }
455
456 out:
457     vlc_mutex_unlock(&sys->mutex);
458     return ret;
459 }
460
461 static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
462 {
463     filter_t *filter = (filter_t *)port->userdata;
464     MMAL_STATUS_T status;
465
466     if (buffer->cmd == MMAL_EVENT_ERROR) {
467         status = *(uint32_t *)buffer->data;
468         msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
469                 mmal_status_to_string(status));
470     }
471
472     mmal_buffer_header_release(buffer);
473 }
474
475 static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
476 {
477     picture_t *picture = (picture_t *)buffer->user_data;
478     filter_t *filter = (filter_t *)port->userdata;
479
480     buffer->user_data = NULL;
481     mmal_buffer_header_release(buffer);
482     if (picture)
483         picture_Release(picture);
484 }
485
486 static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
487 {
488     filter_t *filter = (filter_t *)port->userdata;
489     filter_sys_t *sys = filter->p_sys;
490     picture_t *picture;
491
492     if (buffer->cmd == 0) {
493         if (buffer->length > 0) {
494             mmal_queue_put(sys->filtered_pictures, buffer);
495             fill_output_port(filter);
496         } else {
497             picture = (picture_t *)buffer->user_data;
498             picture_Release(picture);
499             buffer->user_data = NULL;
500         }
501     } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
502         msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
503         mmal_buffer_header_release(buffer);
504     } else {
505         mmal_buffer_header_release(buffer);
506     }
507 }