]> git.sesse.net Git - vlc/blob - modules/codec/omxil/vout.c
mediacodec: factorize release_output_buffer
[vlc] / modules / codec / omxil / vout.c
1 /*****************************************************************************
2  * vout.c: OpenMAX IL video output
3  *****************************************************************************
4  * Copyright © 2013 VideoLAN
5  *
6  * Authors: Martin Storsjo <martin@martin.st>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <vlc_plugin.h>
29 #include <vlc_vout_display.h>
30 #include <vlc_picture_pool.h>
31
32 #include "omxil.h"
33 #include "omxil_core.h"
34 #include "OMX_Broadcom.h"
35
36 // Defined in the broadcom version of OMX_Index.h
37 #define OMX_IndexConfigDisplayRegion 0x7f000010
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
44
45 vlc_module_begin()
46     set_category(CAT_VIDEO)
47     set_subcategory(SUBCAT_VIDEO_VOUT)
48     set_shortname("omxil_vout")
49     set_description(N_("OpenMAX IL video output"))
50     set_capability("vout display", 0)
51     set_callbacks(Open, Close)
52 vlc_module_end()
53
54 /*****************************************************************************
55  * Local prototypes
56  *****************************************************************************/
57
58 static picture_pool_t   *Pool  (vout_display_t *, unsigned);
59 static void             Display(vout_display_t *, picture_t *, subpicture_t *);
60 static int              Control(vout_display_t *, int, va_list);
61
62 /* */
63 struct vout_display_sys_t {
64     picture_pool_t *pool;
65
66     OMX_HANDLETYPE omx_handle;
67
68     char psz_component[OMX_MAX_STRINGNAME_SIZE];
69
70     OmxPort port;
71
72     OmxEventQueue event_queue;
73 };
74
75 struct picture_sys_t {
76     OMX_BUFFERHEADERTYPE *buf;
77     vout_display_sys_t *sys;
78 };
79
80 static int LockSurface(picture_t *);
81 static void UnlockSurface(picture_t *);
82
83 static OMX_ERRORTYPE OmxEventHandler(OMX_HANDLETYPE, OMX_PTR, OMX_EVENTTYPE,
84                                      OMX_U32, OMX_U32, OMX_PTR);
85 static OMX_ERRORTYPE OmxEmptyBufferDone(OMX_HANDLETYPE, OMX_PTR,
86                                         OMX_BUFFERHEADERTYPE *);
87 static OMX_ERRORTYPE OmxFillBufferDone(OMX_HANDLETYPE, OMX_PTR,
88                                        OMX_BUFFERHEADERTYPE *);
89
90 static OMX_ERRORTYPE OmxEventHandler(OMX_HANDLETYPE omx_handle,
91     OMX_PTR app_data, OMX_EVENTTYPE event, OMX_U32 data_1,
92     OMX_U32 data_2, OMX_PTR event_data)
93 {
94     vout_display_t *vd = (vout_display_t *)app_data;
95     vout_display_sys_t *p_sys = vd->sys;
96     (void)omx_handle;
97
98     PrintOmxEvent((vlc_object_t *) vd, event, data_1, data_2, event_data);
99     PostOmxEvent(&p_sys->event_queue, event, data_1, data_2, event_data);
100     return OMX_ErrorNone;
101 }
102
103 static OMX_ERRORTYPE OmxEmptyBufferDone(OMX_HANDLETYPE omx_handle,
104     OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
105 {
106     vout_display_t *vd = (vout_display_t *)app_data;
107     vout_display_sys_t *p_sys = vd->sys;
108     (void)omx_handle;
109
110 #ifndef NDEBUG
111     msg_Dbg(vd, "OmxEmptyBufferDone %p, %p", omx_header, omx_header->pBuffer);
112 #endif
113
114     OMX_FIFO_PUT(&p_sys->port.fifo, omx_header);
115
116     return OMX_ErrorNone;
117 }
118
119 static OMX_ERRORTYPE OmxFillBufferDone(OMX_HANDLETYPE omx_handle,
120     OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
121 {
122     (void)omx_handle;
123     (void)app_data;
124     (void)omx_header;
125
126     return OMX_ErrorNone;
127 }
128
129 static void UpdateDisplaySize(vout_display_t *vd, vout_display_cfg_t *cfg)
130 {
131     OMX_CONFIG_DISPLAYREGIONTYPE config_display;
132     OMX_INIT_STRUCTURE(config_display);
133
134     /* OMX_DISPLAY_SET_PIXEL is broadcom specific */
135     if (strcmp(vd->sys->psz_component, "OMX.broadcom.video_render"))
136         return;
137
138     config_display.nPortIndex = vd->sys->port.i_port_index;
139     config_display.set = OMX_DISPLAY_SET_PIXEL;
140     config_display.pixel_x = cfg->display.width  * vd->fmt.i_height;
141     config_display.pixel_y = cfg->display.height * vd->fmt.i_width;
142     OMX_SetConfig(vd->sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
143 }
144
145 static int Open(vlc_object_t *p_this)
146 {
147     vout_display_t *vd = (vout_display_t *)p_this;
148     vout_display_t *p_dec = vd;
149     char ppsz_components[MAX_COMPONENTS_LIST_SIZE][OMX_MAX_STRINGNAME_SIZE];
150     picture_t** pictures = NULL;
151     OMX_PARAM_PORTDEFINITIONTYPE *def;
152
153     static OMX_CALLBACKTYPE callbacks =
154         { OmxEventHandler, OmxEmptyBufferDone, OmxFillBufferDone };
155
156     if (InitOmxCore(p_this) != VLC_SUCCESS)
157         return VLC_EGENERIC;
158
159     int components = CreateComponentsList(p_this, "iv_renderer", ppsz_components);
160     if (components <= 0) {
161         DeinitOmxCore();
162         return VLC_EGENERIC;
163     }
164
165     /* Allocate structure */
166     vout_display_sys_t *p_sys = (struct vout_display_sys_t*) calloc(1, sizeof(*p_sys));
167     if (!p_sys) {
168         DeinitOmxCore();
169         return VLC_ENOMEM;
170     }
171
172     vd->sys     = p_sys;
173     strcpy(p_sys->psz_component, ppsz_components[0]);
174
175     /* Load component */
176     OMX_ERRORTYPE omx_error = pf_get_handle(&p_sys->omx_handle,
177                                             p_sys->psz_component, vd, &callbacks);
178     CHECK_ERROR(omx_error, "OMX_GetHandle(%s) failed (%x: %s)",
179                 p_sys->psz_component, omx_error, ErrorToString(omx_error));
180
181     InitOmxEventQueue(&p_sys->event_queue);
182     OMX_FIFO_INIT(&p_sys->port.fifo, pOutputPortPrivate);
183     p_sys->port.b_direct = false;
184     p_sys->port.b_flushed = true;
185
186     OMX_PORT_PARAM_TYPE param;
187     OMX_INIT_STRUCTURE(param);
188     omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamVideoInit, &param);
189     CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamVideoInit) failed (%x: %s)",
190                 omx_error, ErrorToString(omx_error));
191
192     p_sys->port.i_port_index = param.nStartPortNumber;
193     p_sys->port.b_valid = true;
194     p_sys->port.omx_handle = p_sys->omx_handle;
195
196     def = &p_sys->port.definition;
197     OMX_INIT_STRUCTURE(*def);
198     def->nPortIndex = p_sys->port.i_port_index;
199     omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, def);
200     CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
201                 omx_error, ErrorToString(omx_error));
202
203 #define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
204
205     def->format.video.nFrameWidth = vd->fmt.i_width;
206     def->format.video.nFrameHeight = vd->fmt.i_height;
207     def->format.video.nStride = 0;
208     def->format.video.nSliceHeight = 0;
209     p_sys->port.definition.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
210
211     if (!strcmp(p_sys->psz_component, "OMX.broadcom.video_render")) {
212         def->format.video.nSliceHeight = ALIGN(def->format.video.nFrameHeight, 16);
213     }
214
215     omx_error = OMX_SetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
216     CHECK_ERROR(omx_error, "OMX_SetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
217                 omx_error, ErrorToString(omx_error));
218     OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
219
220     if (def->format.video.nStride < (int) def->format.video.nFrameWidth)
221         def->format.video.nStride = def->format.video.nFrameWidth;
222     if (def->format.video.nSliceHeight < def->format.video.nFrameHeight)
223         def->format.video.nSliceHeight = def->format.video.nFrameHeight;
224
225     p_sys->port.pp_buffers =
226             malloc(p_sys->port.definition.nBufferCountActual *
227                    sizeof(OMX_BUFFERHEADERTYPE*));
228     p_sys->port.i_buffers = p_sys->port.definition.nBufferCountActual;
229
230     omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
231     CHECK_ERROR(omx_error, "OMX_CommandStateSet Idle failed (%x: %s)",
232                 omx_error, ErrorToString(omx_error));
233
234     unsigned int i;
235     for (i = 0; i < p_sys->port.i_buffers; i++) {
236         omx_error = OMX_AllocateBuffer(p_sys->omx_handle, &p_sys->port.pp_buffers[i],
237                                        p_sys->port.i_port_index, 0,
238                                        p_sys->port.definition.nBufferSize);
239         if (omx_error != OMX_ErrorNone)
240             break;
241         OMX_FIFO_PUT(&p_sys->port.fifo, p_sys->port.pp_buffers[i]);
242     }
243     if (omx_error != OMX_ErrorNone) {
244         p_sys->port.i_buffers = i;
245         for (i = 0; i < p_sys->port.i_buffers; i++)
246             OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_sys->port.pp_buffers[i]);
247         msg_Err(vd, "OMX_AllocateBuffer failed (%x: %s)",
248                 omx_error, ErrorToString(omx_error));
249         goto error;
250     }
251
252     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
253     CHECK_ERROR(omx_error, "Wait for Idle failed (%x: %s)",
254                 omx_error, ErrorToString(omx_error));
255
256     omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet,
257                                 OMX_StateExecuting, 0);
258     CHECK_ERROR(omx_error, "OMX_CommandStateSet Executing failed (%x: %s)",
259                 omx_error, ErrorToString(omx_error));
260     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
261     CHECK_ERROR(omx_error, "Wait for Executing failed (%x: %s)",
262                 omx_error, ErrorToString(omx_error));
263
264     if (!strcmp(p_sys->psz_component, "OMX.broadcom.video_render")) {
265         OMX_CONFIG_DISPLAYREGIONTYPE config_display;
266         OMX_INIT_STRUCTURE(config_display);
267         config_display.nPortIndex = p_sys->port.i_port_index;
268
269         config_display.set = OMX_DISPLAY_SET_SRC_RECT;
270         config_display.src_rect.width = vd->cfg->display.width;
271         config_display.src_rect.height = vd->cfg->display.height;
272         OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
273         config_display.set = OMX_DISPLAY_SET_FULLSCREEN;
274         config_display.fullscreen = OMX_TRUE;
275         OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
276
277         UpdateDisplaySize(vd, vd->cfg);
278     }
279
280
281     /* Setup chroma */
282     video_format_t fmt = vd->fmt;
283
284     fmt.i_chroma = VLC_CODEC_I420;
285     video_format_FixRgb(&fmt);
286
287     /* Setup vout_display */
288     vd->fmt     = fmt;
289     vd->pool    = Pool;
290     vd->display = Display;
291     vd->control = Control;
292     vd->prepare = NULL;
293     vd->manage  = NULL;
294
295     /* Create the associated picture */
296     pictures = calloc(p_sys->port.i_buffers, sizeof(*pictures));
297     if (!pictures)
298         goto error;
299     for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
300         picture_sys_t *picsys = malloc(sizeof(*picsys));
301         if (unlikely(picsys == NULL))
302             goto error;
303         picsys->sys = p_sys;
304
305         picture_resource_t resource = { .p_sys = picsys };
306
307         picture_t *picture = picture_NewFromResource(&fmt, &resource);
308         if (unlikely(picture == NULL))
309         {
310             free(picsys);
311             goto error;
312         }
313         pictures[i] = picture;
314     }
315
316     /* Wrap it into a picture pool */
317     picture_pool_configuration_t pool_cfg;
318     memset(&pool_cfg, 0, sizeof(pool_cfg));
319     pool_cfg.picture_count = p_sys->port.i_buffers;
320     pool_cfg.picture       = pictures;
321     pool_cfg.lock          = LockSurface;
322     pool_cfg.unlock        = UnlockSurface;
323
324     p_sys->pool = picture_pool_NewExtended(&pool_cfg);
325     if (!p_sys->pool) {
326         for (unsigned int i = 0; i < p_sys->port.i_buffers; i++)
327             picture_Release(pictures[i]);
328         goto error;
329     }
330
331     /* Fix initial state */
332     vout_display_SendEventFullscreen(vd, true);
333
334     free(pictures);
335     return VLC_SUCCESS;
336
337 error:
338     free(pictures);
339     Close(p_this);
340     return VLC_EGENERIC;
341 }
342
343 static void Close(vlc_object_t *p_this)
344 {
345     vout_display_t *vd = (vout_display_t *)p_this;
346     vout_display_sys_t *p_sys = vd->sys;
347
348     if (p_sys->omx_handle) {
349         OMX_STATETYPE state;
350         OMX_GetState(p_sys->omx_handle, &state);
351         if (state == OMX_StateExecuting) {
352             OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
353             while (1) {
354                 OMX_U32 cmd, state;
355                 WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, &cmd, &state, 0);
356                 if (cmd == OMX_CommandStateSet && state == OMX_StateIdle)
357                     break;
358             }
359         }
360         OMX_GetState(p_sys->omx_handle, &state);
361         if (state == OMX_StateIdle) {
362             OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateLoaded, 0);
363             for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
364                 OMX_BUFFERHEADERTYPE *p_buffer;
365                 OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
366                 OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_buffer);
367             }
368             WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
369         }
370         free(p_sys->port.pp_buffers);
371         pf_free_handle(p_sys->omx_handle);
372         DeinitOmxEventQueue(&p_sys->event_queue);
373         OMX_FIFO_DESTROY(&p_sys->port.fifo);
374     }
375
376     if (p_sys->pool)
377         picture_pool_Release(p_sys->pool);
378     free(p_sys);
379     DeinitOmxCore();
380 }
381
382 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
383 {
384     VLC_UNUSED(count);
385
386     return vd->sys->pool;
387 }
388
389 static int LockSurface(picture_t *picture)
390 {
391     picture_sys_t *picsys = picture->p_sys;
392     vout_display_sys_t *p_sys = picsys->sys;
393     OMX_BUFFERHEADERTYPE *p_buffer;
394
395     OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
396     for (int i = 0; i < 3; i++) {
397         picture->p[i].p_pixels = p_buffer->pBuffer;
398         picture->p[i].i_pitch = p_sys->port.definition.format.video.nStride;
399         picture->p[i].i_lines = p_sys->port.definition.format.video.nSliceHeight;
400         if (i > 0) {
401             picture->p[i].p_pixels = picture->p[i-1].p_pixels + picture->p[i-1].i_pitch*picture->p[i-1].i_lines;
402             picture->p[i].i_pitch /= 2;
403             picture->p[i].i_lines /= 2;
404         }
405     }
406     p_buffer->nOffset = 0;
407     p_buffer->nFlags = 0;
408     p_buffer->nTimeStamp = ToOmxTicks(0);
409     p_buffer->nFilledLen = 0;
410     picsys->buf = p_buffer;
411
412     return VLC_SUCCESS;
413 }
414
415 static void UnlockSurface(picture_t *picture)
416 {
417     picture_sys_t *picsys = picture->p_sys;
418     vout_display_sys_t *p_sys = picsys->sys;
419     OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
420
421     if (!p_buffer->nFilledLen)
422         OMX_FIFO_PUT(&p_sys->port.fifo, p_buffer);
423     else
424         OMX_EmptyThisBuffer(p_sys->omx_handle, p_buffer);
425 }
426
427 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
428 {
429     VLC_UNUSED(vd);
430     VLC_UNUSED(subpicture);
431     picture_sys_t *picsys = picture->p_sys;
432     vout_display_sys_t *p_sys = picsys->sys;
433     OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
434
435     p_buffer->nFilledLen = 3*p_sys->port.definition.format.video.nStride*p_sys->port.definition.format.video.nSliceHeight/2;
436     p_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
437
438     /* refcount lowers to 0, and pool_cfg.unlock is called */
439
440     picture_Release(picture);
441 }
442
443 static int Control(vout_display_t *vd, int query, va_list args)
444 {
445     VLC_UNUSED(args);
446
447     switch (query) {
448     case VOUT_DISPLAY_HIDE_MOUSE:
449         return VLC_SUCCESS;
450
451     default:
452         msg_Err(vd, "Unknown request in omxil vout display");
453
454     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
455         return VLC_SUCCESS;
456     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
457     {
458         const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
459         UpdateDisplaySize(vd, cfg);
460         return VLC_SUCCESS;
461     }
462     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
463     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
464     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
465     case VOUT_DISPLAY_CHANGE_ZOOM:
466     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
467         return VLC_EGENERIC;
468     }
469 }