1 /*****************************************************************************
2 * vout.c: OpenMAX IL video output
3 *****************************************************************************
4 * Copyright © 2013 VideoLAN
6 * Authors: Martin Storsjo <martin@martin.st>
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.
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.
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 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <vlc_plugin.h>
29 #include <vlc_vout_display.h>
30 #include <vlc_picture_pool.h>
33 #include "omxil_core.h"
34 #include "OMX_Broadcom.h"
36 // Defined in the broadcom version of OMX_Index.h
37 #define OMX_IndexConfigDisplayRegion 0x7f000010
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open (vlc_object_t *);
43 static void Close(vlc_object_t *);
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)
54 /*****************************************************************************
56 *****************************************************************************/
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);
63 struct vout_display_sys_t {
66 OMX_HANDLETYPE omx_handle;
68 char psz_component[OMX_MAX_STRINGNAME_SIZE];
72 OmxEventQueue event_queue;
75 struct picture_sys_t {
76 OMX_BUFFERHEADERTYPE *buf;
77 vout_display_sys_t *sys;
80 static int LockSurface(picture_t *);
81 static void UnlockSurface(picture_t *);
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 *);
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)
94 vout_display_t *vd = (vout_display_t *)app_data;
95 vout_display_sys_t *p_sys = vd->sys;
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;
103 static OMX_ERRORTYPE OmxEmptyBufferDone(OMX_HANDLETYPE omx_handle,
104 OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
106 vout_display_t *vd = (vout_display_t *)app_data;
107 vout_display_sys_t *p_sys = vd->sys;
111 msg_Dbg(vd, "OmxEmptyBufferDone %p, %p", omx_header, omx_header->pBuffer);
114 OMX_FIFO_PUT(&p_sys->port.fifo, omx_header);
116 return OMX_ErrorNone;
119 static OMX_ERRORTYPE OmxFillBufferDone(OMX_HANDLETYPE omx_handle,
120 OMX_PTR app_data, OMX_BUFFERHEADERTYPE *omx_header)
126 return OMX_ErrorNone;
129 static void UpdateDisplaySize(vout_display_t *vd, vout_display_cfg_t *cfg)
131 OMX_CONFIG_DISPLAYREGIONTYPE config_display;
132 OMX_INIT_STRUCTURE(config_display);
134 /* OMX_DISPLAY_SET_PIXEL is broadcom specific */
135 if (strcmp(vd->sys->psz_component, "OMX.broadcom.video_render"))
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);
145 static int Open(vlc_object_t *p_this)
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;
153 static OMX_CALLBACKTYPE callbacks =
154 { OmxEventHandler, OmxEmptyBufferDone, OmxFillBufferDone };
156 if (InitOmxCore(p_this) != VLC_SUCCESS)
159 int components = CreateComponentsList(p_this, "iv_renderer", ppsz_components);
160 if (components <= 0) {
165 /* Allocate structure */
166 vout_display_sys_t *p_sys = (struct vout_display_sys_t*) calloc(1, sizeof(*p_sys));
173 strcpy(p_sys->psz_component, ppsz_components[0]);
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));
181 InitOmxEventQueue(&p_sys->event_queue);
182 vlc_mutex_init (&p_sys->port.fifo.lock);
183 vlc_cond_init (&p_sys->port.fifo.wait);
184 p_sys->port.fifo.offset = offsetof(OMX_BUFFERHEADERTYPE, pOutputPortPrivate) / sizeof(void *);
185 p_sys->port.fifo.pp_last = &p_sys->port.fifo.p_first;
186 p_sys->port.b_direct = false;
187 p_sys->port.b_flushed = true;
189 OMX_PORT_PARAM_TYPE param;
190 OMX_INIT_STRUCTURE(param);
191 omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamVideoInit, ¶m);
192 CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamVideoInit) failed (%x: %s)",
193 omx_error, ErrorToString(omx_error));
195 p_sys->port.i_port_index = param.nStartPortNumber;
196 p_sys->port.b_valid = true;
197 p_sys->port.omx_handle = p_sys->omx_handle;
199 def = &p_sys->port.definition;
200 OMX_INIT_STRUCTURE(*def);
201 def->nPortIndex = p_sys->port.i_port_index;
202 omx_error = OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, def);
203 CHECK_ERROR(omx_error, "OMX_GetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
204 omx_error, ErrorToString(omx_error));
206 #define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
208 def->format.video.nFrameWidth = vd->fmt.i_width;
209 def->format.video.nFrameHeight = vd->fmt.i_height;
210 def->format.video.nStride = 0;
211 def->format.video.nSliceHeight = 0;
212 p_sys->port.definition.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
214 if (!strcmp(p_sys->psz_component, "OMX.broadcom.video_render")) {
215 def->format.video.nSliceHeight = ALIGN(def->format.video.nFrameHeight, 16);
218 omx_error = OMX_SetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
219 CHECK_ERROR(omx_error, "OMX_SetParameter(OMX_IndexParamPortDefinition) failed (%x: %s)",
220 omx_error, ErrorToString(omx_error));
221 OMX_GetParameter(p_sys->omx_handle, OMX_IndexParamPortDefinition, &p_sys->port.definition);
223 if (def->format.video.nStride < (int) def->format.video.nFrameWidth)
224 def->format.video.nStride = def->format.video.nFrameWidth;
225 if (def->format.video.nSliceHeight < def->format.video.nFrameHeight)
226 def->format.video.nSliceHeight = def->format.video.nFrameHeight;
228 p_sys->port.pp_buffers =
229 malloc(p_sys->port.definition.nBufferCountActual *
230 sizeof(OMX_BUFFERHEADERTYPE*));
231 p_sys->port.i_buffers = p_sys->port.definition.nBufferCountActual;
233 omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
234 CHECK_ERROR(omx_error, "OMX_CommandStateSet Idle failed (%x: %s)",
235 omx_error, ErrorToString(omx_error));
238 for (i = 0; i < p_sys->port.i_buffers; i++) {
239 omx_error = OMX_AllocateBuffer(p_sys->omx_handle, &p_sys->port.pp_buffers[i],
240 p_sys->port.i_port_index, 0,
241 p_sys->port.definition.nBufferSize);
242 if (omx_error != OMX_ErrorNone)
244 OMX_FIFO_PUT(&p_sys->port.fifo, p_sys->port.pp_buffers[i]);
246 if (omx_error != OMX_ErrorNone) {
247 p_sys->port.i_buffers = i;
248 for (i = 0; i < p_sys->port.i_buffers; i++)
249 OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_sys->port.pp_buffers[i]);
250 msg_Err(vd, "OMX_AllocateBuffer failed (%x: %s)",
251 omx_error, ErrorToString(omx_error));
255 omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
256 CHECK_ERROR(omx_error, "Wait for Idle failed (%x: %s)",
257 omx_error, ErrorToString(omx_error));
259 omx_error = OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet,
260 OMX_StateExecuting, 0);
261 CHECK_ERROR(omx_error, "OMX_CommandStateSet Executing failed (%x: %s)",
262 omx_error, ErrorToString(omx_error));
263 omx_error = WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
264 CHECK_ERROR(omx_error, "Wait for Executing failed (%x: %s)",
265 omx_error, ErrorToString(omx_error));
267 if (!strcmp(p_sys->psz_component, "OMX.broadcom.video_render")) {
268 OMX_CONFIG_DISPLAYREGIONTYPE config_display;
269 OMX_INIT_STRUCTURE(config_display);
270 config_display.nPortIndex = p_sys->port.i_port_index;
272 config_display.set = OMX_DISPLAY_SET_SRC_RECT;
273 config_display.src_rect.width = vd->cfg->display.width;
274 config_display.src_rect.height = vd->cfg->display.height;
275 OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
276 config_display.set = OMX_DISPLAY_SET_FULLSCREEN;
277 config_display.fullscreen = OMX_TRUE;
278 OMX_SetConfig(p_sys->omx_handle, OMX_IndexConfigDisplayRegion, &config_display);
280 UpdateDisplaySize(vd, vd->cfg);
285 video_format_t fmt = vd->fmt;
287 fmt.i_chroma = VLC_CODEC_I420;
288 video_format_FixRgb(&fmt);
290 /* Setup vout_display */
293 vd->display = Display;
294 vd->control = Control;
298 /* Create the associated picture */
299 pictures = calloc(sizeof(*pictures), p_sys->port.i_buffers);
302 for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
303 picture_sys_t *picsys = malloc(sizeof(*picsys));
304 if (unlikely(picsys == NULL))
308 picture_resource_t resource = { .p_sys = picsys };
310 picture_t *picture = picture_NewFromResource(&fmt, &resource);
311 if (unlikely(picture == NULL))
316 pictures[i] = picture;
319 /* Wrap it into a picture pool */
320 picture_pool_configuration_t pool_cfg;
321 memset(&pool_cfg, 0, sizeof(pool_cfg));
322 pool_cfg.picture_count = p_sys->port.i_buffers;
323 pool_cfg.picture = pictures;
324 pool_cfg.lock = LockSurface;
325 pool_cfg.unlock = UnlockSurface;
327 p_sys->pool = picture_pool_NewExtended(&pool_cfg);
329 for (unsigned int i = 0; i < p_sys->port.i_buffers; i++)
330 picture_Release(pictures[i]);
334 /* Fix initial state */
335 vout_display_SendEventFullscreen(vd, true);
346 static void Close(vlc_object_t *p_this)
348 vout_display_t *vd = (vout_display_t *)p_this;
349 vout_display_sys_t *p_sys = vd->sys;
351 if (p_sys->omx_handle) {
353 OMX_GetState(p_sys->omx_handle, &state);
354 if (state == OMX_StateExecuting) {
355 OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateIdle, 0);
358 WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, &cmd, &state, 0);
359 if (cmd == OMX_CommandStateSet && state == OMX_StateIdle)
363 OMX_GetState(p_sys->omx_handle, &state);
364 if (state == OMX_StateIdle) {
365 OMX_SendCommand(p_sys->omx_handle, OMX_CommandStateSet, OMX_StateLoaded, 0);
366 for (unsigned int i = 0; i < p_sys->port.i_buffers; i++) {
367 OMX_BUFFERHEADERTYPE *p_buffer;
368 OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
369 OMX_FreeBuffer(p_sys->omx_handle, p_sys->port.i_port_index, p_buffer);
371 WaitForSpecificOmxEvent(&p_sys->event_queue, OMX_EventCmdComplete, 0, 0, 0);
373 free(p_sys->port.pp_buffers);
374 pf_free_handle(p_sys->omx_handle);
375 DeinitOmxEventQueue(&p_sys->event_queue);
376 vlc_mutex_destroy(&p_sys->port.fifo.lock);
377 vlc_cond_destroy(&p_sys->port.fifo.wait);
381 picture_pool_Delete(p_sys->pool);
386 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
390 return vd->sys->pool;
393 static int LockSurface(picture_t *picture)
395 picture_sys_t *picsys = picture->p_sys;
396 vout_display_sys_t *p_sys = picsys->sys;
397 OMX_BUFFERHEADERTYPE *p_buffer;
399 OMX_FIFO_GET(&p_sys->port.fifo, p_buffer);
400 for (int i = 0; i < 3; i++) {
401 picture->p[i].p_pixels = p_buffer->pBuffer;
402 picture->p[i].i_pitch = p_sys->port.definition.format.video.nStride;
403 picture->p[i].i_lines = p_sys->port.definition.format.video.nSliceHeight;
405 picture->p[i].p_pixels = picture->p[i-1].p_pixels + picture->p[i-1].i_pitch*picture->p[i-1].i_lines;
406 picture->p[i].i_pitch /= 2;
407 picture->p[i].i_lines /= 2;
410 p_buffer->nOffset = 0;
411 p_buffer->nFlags = 0;
412 p_buffer->nTimeStamp = ToOmxTicks(0);
413 p_buffer->nFilledLen = 0;
414 picsys->buf = p_buffer;
419 static void UnlockSurface(picture_t *picture)
421 picture_sys_t *picsys = picture->p_sys;
422 vout_display_sys_t *p_sys = picsys->sys;
423 OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
425 if (!p_buffer->nFilledLen)
426 OMX_FIFO_PUT(&p_sys->port.fifo, p_buffer);
428 OMX_EmptyThisBuffer(p_sys->omx_handle, p_buffer);
431 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
434 VLC_UNUSED(subpicture);
435 picture_sys_t *picsys = picture->p_sys;
436 vout_display_sys_t *p_sys = picsys->sys;
437 OMX_BUFFERHEADERTYPE *p_buffer = picsys->buf;
439 p_buffer->nFilledLen = 3*p_sys->port.definition.format.video.nStride*p_sys->port.definition.format.video.nSliceHeight/2;
440 p_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
442 /* refcount lowers to 0, and pool_cfg.unlock is called */
444 picture_Release(picture);
447 static int Control(vout_display_t *vd, int query, va_list args)
452 case VOUT_DISPLAY_HIDE_MOUSE:
456 msg_Err(vd, "Unknown request in omxil vout display");
458 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
460 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
462 const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
463 UpdateDisplaySize(vd, cfg);
466 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
467 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
468 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
469 case VOUT_DISPLAY_CHANGE_ZOOM:
470 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
471 case VOUT_DISPLAY_GET_OPENGL: