omxil_vout: Set broadcom specific parameters
[vlc.git] / 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 int Open(vlc_object_t *p_this)
130 {
131     vout_display_t *vd = (vout_display_t *)p_this;
132     vout_display_t *p_dec = vd;
133     char ppsz_components[MAX_COMPONENTS_LIST_SIZE][OMX_MAX_STRINGNAME_SIZE];
134     picture_t** pictures = NULL;
135
136     static OMX_CALLBACKTYPE callbacks =
137         { OmxEventHandler, OmxEmptyBufferDone, OmxFillBufferDone };
138
139     if (InitOmxCore(p_this) != VLC_SUCCESS)
140         return VLC_EGENERIC;
141
142     int components = CreateComponentsList(p_this, "iv_renderer", ppsz_components);
143     if (components <= 0) {
144         DeinitOmxCore();
145         return VLC_EGENERIC;
146     }
147
148     /* Allocate structure */
149     vout_display_sys_t *p_sys = (struct vout_display_sys_t*) calloc(1, sizeof(*p_sys));
150     if (!p_sys) {
151         DeinitOmxCore();
152         return VLC_ENOMEM;
153     }
154
155     vd->sys     = p_sys;
156     strcpy(p_sys->psz_component, ppsz_components[0]);
157
158     /* Load component */
159     OMX_ERRORTYPE omx_error = pf_get_handle(&p_sys->omx_handle,
160                                             p_sys->psz_component, vd, &callbacks);
161     CHECK_ERROR(omx_error, "OMX_GetHandle(%s) failed (%x: %s)",
162                 p_sys->psz_component, omx_error, ErrorToString(omx_error));
163
164     InitOmxEventQueue(&p_sys->event_queue);
165     vlc_mutex_init (&p_sys->port.fifo.lock);
166     vlc_cond_init (&p_sys->port.fifo.wait);
167     p_sys->port.fifo.offset = offsetof(OMX_BUFFERHEADERTYPE, pOutputPortPrivate) / sizeof(void *);
168     p_sys->port.fifo.pp_last = &p_sys->port.fifo.p_first;
169     p_sys->port.b_direct = false;
170     p_sys->port.b_flushed = true;
171
172     OMX_PORT_PARAM_TYPE param;
173     OMX_INIT_STRUCTURE(param);
174     omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamVideoInit, &param);
175     CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamVideoInit) failed (%x: %s)",
176                 omx_error, ErrorToString(omx_error));
177
178     p_sys->port.i_port_index = param.nStartPortNumber;
179     p_sys->port.b_valid = true;
180     p_sys->port.omx_handle = p_sys->omx_handle;
181
182     OMX_INIT_STRUCTURE(p_sys->port.definition);
183     p_sys->port.definition.nPortIndex = p_sys->port.i_port_index;
184     omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
185     CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
186                 omx_error, ErrorToString(omx_error));
187
188 #define ALIGN_16_PIXELS(x) (((x) + 15) / 16 * 16)
189
190     p_sys->port.definition.format.video.nFrameWidth = ALIGN_16_PIXELS(vd->cfg->display.width);
191     p_sys->port.definition.format.video.nFrameHeight = ALIGN_16_PIXELS(vd->cfg->display.height);
192     p_sys->port.definition.format.video.nStride = p_sys->port.definition.format.video.nFrameWidth;
193     p_sys->port.definition.format.video.nSliceHeight = p_sys->port.definition.format.video.nFrameHeight;
194     p_sys->port.definition.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
195
196     omx_error = OMX_SetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
197     CHECK_ERROR(omx_error, "OMX_SetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
198                 omx_error, ErrorToString(omx_error));
199     OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
200
201     p_sys->port.pp_buffers =
202             malloc(p_sys->port.definition.nBufferCountActual *
203                    sizeof(OMX_BUFFERHEADERTYPE*));
204     p_sys->port.i_buffers = p_sys->port.definition.nBufferCountActual;
205
206     omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
207     CHECK_ERROR(omx_error, "OMX_CommandStateSet Idle failed (%x: %s)",
208                 omx_error, ErrorToString(omx_error));
209
210     unsigned int i;
211     for (i = 0; i < p_sys->port.i_buffers; i++) {
212         omx_error = OMX_AllocateBuffer(p_sys->omx_handle, &p_sys->port.pp_buffers[i],
213                                        p_sys->port.i_port_index, 0,
214                                        p_sys->port.definition.nBufferSize);
215         if (omx_error != OMX_ErrorNone)
216             break;
217         OMX_FIFO_PUT(&p_sys->port.fifo, p_sys->port.pp_buffers[i]);
218     }
219     if (omx_error != OMX_ErrorNone) {
220         p_sys->port.i_buffers = i;
221         for (i = 0; i < p_sys->port.i_buffers; i++)
222             OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_sys->port.pp_buffers[i]);
223         msg_Err(vd, "OMX_AllocateBuffer failed (%x: %s)",
224                 omx_error, ErrorToString(omx_error));
225         goto error;
226     }
227
228     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
229     CHECK_ERROR(omx_error, "Wait for Idle failed (%x: %s)",
230                 omx_error, ErrorToString(omx_error));
231
232     omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet,
233                                 OMX_StateExecuting, 0);
234     CHECK_ERROR(omx_error, "OMX_CommandStateSet Executing failed (%x: %s)",
235                 omx_error, ErrorToString(omx_error));
236     omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
237     CHECK_ERROR(omx_error, "Wait for Executing failed (%x: %s)",
238                 omx_error, ErrorToString(omx_error));
239
240     if (!strcmp(p_sys->psz_component, "OMX.broadcom.video_render")) {
241         OMX_CONFIG_DISPLAYREGIONTYPE config_display;
242         OMX_INIT_STRUCTURE(config_display);
243         config_display.nPortIndex = p_sys->port.i_port_index;
244
245         config_display.set = OMX_DISPLAY_SET_SRC_RECT;
246         config_display.src_rect.width = vd->cfg->display.width;
247         config_display.src_rect.height = vd->cfg->display.height;
248         OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
249         config_display.set = OMX_DISPLAY_SET_FULLSCREEN;
250         config_display.fullscreen = OMX_TRUE;
251         OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
252     }
253
254
255     /* Setup chroma */
256     video_format_t fmt = vd->fmt;
257
258     fmt.i_chroma = VLC_CODEC_I420;
259     video_format_FixRgb(&fmt);
260
261     /* Setup vout_display */
262     vd->fmt     = fmt;
263     vd->pool    = Pool;
264     vd->display = Display;
265     vd->control = Control;
266     vd->prepare = NULL;
267     vd->manage  = NULL;
268
269     /* Create the associated picture */
270     pictures = calloc(sizeof(*pictures), p_sys->port.i_buffers);
271     if (!pictures)
272         goto error;
273     for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
274         picture_resource_t resource = { 0 };
275         picture_resource_t *rsc = &resource;
276         rsc->p_sys = malloc(sizeof(*rsc->p_sys));
277         if (!rsc->p_sys)
278             goto error;
279         rsc->p_sys->sys = p_sys;
280
281         for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
282             rsc->p[i].p_pixels = NULL;
283             rsc->p[i].i_pitch = 0;
284             rsc->p[i].i_lines = 0;
285         }
286         picture_t *picture = picture_NewFromResource(&fmt, rsc);
287         if (!picture)
288             goto error;
289         pictures[i] = picture;
290     }
291
292     /* Wrap it into a picture pool */
293     picture_pool_configuration_t pool_cfg;
294     memset(&pool_cfg, 0, sizeof(pool_cfg));
295     pool_cfg.picture_count = p_sys->port.i_buffers;
296     pool_cfg.picture       = pictures;
297     pool_cfg.lock          = LockSurface;
298     pool_cfg.unlock        = UnlockSurface;
299
300     p_sys->pool = picture_pool_NewExtended(&pool_cfg);
301     if (!p_sys->pool) {
302         for (unsigned int i = 0; i < p_sys->port.i_buffers; i++)
303             picture_Release(pictures[i]);
304         goto error;
305     }
306
307     /* Fix initial state */
308     vout_display_SendEventFullscreen(vd, true);
309
310     free(pictures);
311     return VLC_SUCCESS;
312
313 error:
314     free(pictures);
315     Close(p_this);
316     return VLC_EGENERIC;
317 }
318
319 static void Close(vlc_object_t *p_this)
320 {
321     vout_display_t *vd = (vout_display_t *)p_this;
322     vout_display_sys_t *p_sys = vd->sys;
323
324     if (p_sys->omx_handle) {
325         OMX_STATETYPE state;
326         OMX_GetState(p_sys->omx_handle, &state);
327         if (state == OMX_StateExecuting) {
328             OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
329             while (1) {
330                 OMX_U32 cmd, state;
331                 WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, &cmd, &state, 0);
332                 if (cmd == OMX_CommandStateSet && state == OMX_StateIdle)
333                     break;
334             }
335         }
336         OMX_GetState(p_sys->omx_handle, &state);
337         if (state == OMX_StateIdle) {
338             OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateLoaded, 0);
339             for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
340                 OMX_BUFFERHEADERTYPE *p_buffer;
341                 OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
342                 OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_buffer);
343             }
344             WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
345         }
346         free(p_sys->port.pp_buffers);
347         pf_free_handle(p_sys->omx_handle);
348         DeinitOmxEventQueue(&p_sys->event_queue);
349         vlc_mutex_destroy(&p_sys->port.fifo.lock);
350         vlc_cond_destroy(&p_sys->port.fifo.wait);
351     }
352
353     if (p_sys->pool)
354         picture_pool_Delete(p_sys->pool);
355     free(p_sys);
356     DeinitOmxCore();
357 }
358
359 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
360 {
361     VLC_UNUSED(count);
362
363     return vd->sys->pool;
364 }
365
366 static int LockSurface(picture_t *picture)
367 {
368     picture_sys_t *picsys = picture->p_sys;
369     vout_display_sys_t *p_sys = picsys->sys;
370     OMX_BUFFERHEADERTYPE *p_buffer;
371
372     OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
373     for (int i = 0; i < 3; i++) {
374         picture->p[i].p_pixels = p_buffer->pBuffer;
375         picture->p[i].i_pitch = p_sys->port.definition.format.video.nStride;
376         picture->p[i].i_lines = p_sys->port.definition.format.video.nSliceHeight;
377         if (i > 0) {
378             picture->p[i].p_pixels = picture->p[i-1].p_pixels + picture->p[i-1].i_pitch*picture->p[i-1].i_lines;
379             picture->p[i].i_pitch /= 2;
380             picture->p[i].i_lines /= 2;
381         }
382     }
383     p_buffer->nOffset = 0;
384     p_buffer->nFlags = 0;
385     p_buffer->nTimeStamp = ToOmxTicks(0);
386     p_buffer->nFilledLen = 0;
387     picsys->buf = p_buffer;
388
389     return VLC_SUCCESS;
390 }
391
392 static void UnlockSurface(picture_t *picture)
393 {
394     picture_sys_t *picsys = picture->p_sys;
395     vout_display_sys_t *p_sys = picsys->sys;
396     OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
397
398     if (!p_buffer->nFilledLen)
399         OMX_FIFO_PUT(&p_sys->port.fifo, p_buffer);
400     else
401         OMX_EmptyThisBuffer(p_sys->omx_handle, p_buffer);
402 }
403
404 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
405 {
406     VLC_UNUSED(vd);
407     VLC_UNUSED(subpicture);
408     picture_sys_t *picsys = picture->p_sys;
409     vout_display_sys_t *p_sys = picsys->sys;
410     OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
411
412     p_buffer->nFilledLen = 3*p_sys->port.definition.format.video.nStride*p_sys->port.definition.format.video.nSliceHeight/2;
413     p_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
414
415     /* refcount lowers to 0, and pool_cfg.unlock is called */
416
417     picture_Release(picture);
418 }
419
420 static int Control(vout_display_t *vd, int query, va_list args)
421 {
422     VLC_UNUSED(args);
423
424     switch (query) {
425     case VOUT_DISPLAY_HIDE_MOUSE:
426         return VLC_SUCCESS;
427
428     default:
429         msg_Err(vd, "Unknown request in omxil vout display");
430
431     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
432     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
433     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
434     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
435     case VOUT_DISPLAY_CHANGE_ZOOM:
436     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
437     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
438     case VOUT_DISPLAY_GET_OPENGL:
439         return VLC_EGENERIC;
440     }
441 }