Last active
February 19, 2019 07:44
-
-
Save zoff99/ff0dad829b912f12308db2bbf3d8a5d1 to your computer and use it in GitHub Desktop.
raspi omx turn90 problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/bash | |
gcc -O0 \ | |
-DRASPBERRY_PI -DOMX_SKIP64BIT -DUSE_VCHIQ_ARM \ | |
-I/opt/vc/include -I/opt/vc/interface/vmcs_host/linux -I/opt/vc/interface/vcos/pthreads \ | |
-fPIC -export-dynamic -o test_omx \ | |
-lm \ | |
test_omx.c omx.c \ | |
-I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads \ | |
-I/opt/vc/include/interface/vmcs_host/linux -lbrcmEGL -lbrcmGLESv2 \ | |
-lbcm_host -L/opt/vc/lib \ | |
-std=gnu99 \ | |
-lrt \ | |
-lpthread -lv4lconvert \ | |
-lmmal -lmmal_core -lmmal_vc_client -lmmal_components -lmmal_util \ | |
-L/opt/vc/lib -lbcm_host -lvcos -lopenmaxil -ldl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* ToxBlinkenwall | |
* (C)Zoff <zoff@zoff.cc> in 2017 - 2019 | |
* | |
* https://github.com/zoff99/ToxBlinkenwall | |
* | |
*/ | |
/** | |
* @file omx.c Raspberry Pi VideoCoreIV OpenMAX interface | |
* | |
* Copyright (C) 2016 - 2017 Creytiv.com | |
* Copyright (C) 2016 - 2017 Jonathan Sieber | |
* Copyright (C) 2018 - 2019 Zoff <zoff@zoff.cc> | |
*/ | |
#include "omx.h" | |
/* | |
* forward func declarations (acutal functions in toxblinkenwall.c) | |
*/ | |
void dbg(int level, const char *fmt, ...); | |
void usleep_usec(uint64_t msec); | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* Avoids a VideoCore header warning about clock_gettime() */ | |
#include <time.h> | |
#include <sys/time.h> | |
#include <bcm_host.h> | |
/** | |
* @defgroup omx omx | |
* | |
* TODO: | |
* * Proper sync OMX events across threads, instead of busy waiting | |
*/ | |
static const int VIDEO_RENDER_PORT = 90; | |
static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, | |
OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, | |
OMX_PTR pEventData) | |
{ | |
(void) hComponent; | |
switch (eEvent) | |
{ | |
case OMX_EventCmdComplete: | |
dbg(9, "omx.EventHandler: Previous command completed\n" | |
"d1=%x\td2=%x\teventData=%p\tappdata=%p\n", | |
nData1, nData2, pEventData, pAppData); | |
/* TODO: Put these event into a multithreaded queue, | |
* properly wait for them in the issuing code */ | |
break; | |
case OMX_EventError: | |
dbg(9, "omx.EventHandler: Error event type " | |
"data1=%x\tdata2=%x\n", nData1, nData2); | |
break; | |
default: | |
dbg(9, "omx.EventHandler: Unknown event type %d\t" | |
"data1=%x data2=%x\n", eEvent, nData1, nData2); | |
return -1; | |
break; | |
} | |
return 0; | |
} | |
static OMX_ERRORTYPE EmptyBufferDone(OMX_HANDLETYPE hComponent, | |
OMX_PTR pAppData, | |
OMX_BUFFERHEADERTYPE *pBuffer) | |
{ | |
(void) hComponent; | |
(void) pAppData; | |
(void) pBuffer; | |
/* TODO: Wrap every call that can generate an event, | |
* and panic if an unexpected event arrives */ | |
return 0; | |
} | |
static OMX_ERRORTYPE FillBufferDone(OMX_HANDLETYPE hComponent, | |
OMX_PTR pAppData, OMX_BUFFERHEADERTYPE *pBuffer) | |
{ | |
(void) hComponent; | |
(void) pAppData; | |
(void) pBuffer; | |
dbg(9, "FillBufferDone\n"); | |
return 0; | |
} | |
static struct OMX_CALLBACKTYPE callbacks = | |
{ | |
EventHandler, | |
EmptyBufferDone, | |
&FillBufferDone | |
}; | |
int omx_init(struct omx_state *st) | |
{ | |
OMX_ERRORTYPE err; | |
bcm_host_init(); | |
st->buffers = NULL; | |
err = OMX_Init(); | |
err |= OMX_GetHandle(&st->video_render, | |
"OMX.broadcom.video_render", 0, &callbacks); | |
if (!st->video_render || err != 0) | |
{ | |
dbg(9, "omx: Failed to create OMX video_render component\n"); | |
return ENOENT; | |
} | |
else | |
{ | |
dbg(9, "omx: created video_render component\n"); | |
return 0; | |
} | |
} | |
/* Some busy loops to verify we're running in order */ | |
static void block_until_state_changed(OMX_HANDLETYPE hComponent, | |
OMX_STATETYPE wanted_eState) | |
{ | |
OMX_STATETYPE eState; | |
unsigned int i = 0; | |
uint32_t loop_counter = 0; | |
while (i++ == 0 || eState != wanted_eState) | |
{ | |
OMX_GetState(hComponent, &eState); | |
if (eState != wanted_eState) | |
{ | |
usleep_usec(10000); | |
} | |
loop_counter++; | |
if (loop_counter > 50) | |
{ | |
// we don't want to get stuck here | |
break; | |
} | |
} | |
} | |
void omx_deinit(struct omx_state *st) | |
{ | |
if (!st) | |
{ | |
return; | |
} | |
OMX_SendCommand(st->video_render, | |
OMX_CommandStateSet, OMX_StateIdle, NULL); | |
block_until_state_changed(st->video_render, OMX_StateIdle); | |
dbg(9, "omx_deinit:003\n"); | |
OMX_SendCommand(st->video_render, | |
OMX_CommandStateSet, OMX_StateLoaded, NULL); | |
dbg(9, "omx_deinit:004\n"); | |
usleep_usec(150000); | |
dbg(9, "omx_deinit:005\n"); | |
OMX_FreeHandle(st->video_render); | |
dbg(9, "omx_deinit:006\n"); | |
OMX_Deinit(); | |
dbg(9, "omx_deinit:099\n"); | |
} | |
void omx_display_disable(struct omx_state *st) | |
{ | |
dbg(9, "omx_display_disable\n"); | |
OMX_ERRORTYPE err; | |
OMX_CONFIG_DISPLAYREGIONTYPE config; | |
if (!st) | |
{ | |
return; | |
} | |
memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); | |
config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); | |
config.nVersion.nVersion = OMX_VERSION; | |
config.nPortIndex = VIDEO_RENDER_PORT; | |
config.fullscreen = OMX_FALSE; | |
config.set = OMX_DISPLAY_SET_FULLSCREEN; | |
err = OMX_SetParameter(st->video_render, | |
OMX_IndexConfigDisplayRegion, &config); | |
if (err != 0) | |
{ | |
dbg(9, "omx_display_disable command failed\n"); | |
} | |
} | |
static void block_until_port_changed(OMX_HANDLETYPE hComponent, | |
OMX_U32 nPortIndex, OMX_BOOL bEnabled) | |
{ | |
OMX_ERRORTYPE r; | |
OMX_PARAM_PORTDEFINITIONTYPE portdef; | |
OMX_U32 i = 0; | |
memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); | |
portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); | |
portdef.nVersion.nVersion = OMX_VERSION; | |
portdef.nPortIndex = nPortIndex; | |
uint32_t loop_counter = 0; | |
while (i++ == 0 || portdef.bEnabled != bEnabled) | |
{ | |
r = OMX_GetParameter(hComponent, | |
OMX_IndexParamPortDefinition, &portdef); | |
if (r != OMX_ErrorNone) | |
{ | |
dbg(9, "block_until_port_changed: OMX_GetParameter " | |
" failed with Result=%d\n", r); | |
} | |
if (portdef.bEnabled != bEnabled) | |
{ | |
usleep_usec(10000); | |
} | |
loop_counter++; | |
if (loop_counter > 50) | |
{ | |
// we don't want to get stuck here | |
break; | |
} | |
} | |
} | |
int omx_display_xy(int flag, struct omx_state *st, | |
int width, int height, int stride) | |
{ | |
unsigned int i; | |
OMX_PARAM_PORTDEFINITIONTYPE portdef; | |
OMX_CONFIG_DISPLAYREGIONTYPE config; | |
OMX_ERRORTYPE err = 0; | |
dbg(9, "omx_update_size %d %d %d\n", width, height, stride); | |
memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); | |
config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); | |
config.nVersion.nVersion = OMX_VERSION; | |
config.nPortIndex = VIDEO_RENDER_PORT; | |
// | |
// | |
// | |
// | |
config.dest_rect.x_offset = 0; | |
config.dest_rect.y_offset = 0; | |
config.dest_rect.width = (int)(1920 / 2); | |
config.dest_rect.height = (int)(1080 / 2); | |
config.mode = OMX_DISPLAY_MODE_LETTERBOX; | |
if (flag == 0) | |
{ | |
config.transform = OMX_DISPLAY_ROT0; | |
} | |
else | |
{ | |
config.transform = OMX_DISPLAY_ROT90; | |
} | |
config.fullscreen = OMX_FALSE; | |
config.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_TRANSFORM | OMX_DISPLAY_SET_DEST_RECT | | |
OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_MODE); | |
// | |
err |= OMX_SetParameter(st->video_render, | |
OMX_IndexConfigDisplayRegion, &config); | |
} | |
int omx_display_enable(struct omx_state *st, | |
int width, int height, int stride) | |
{ | |
unsigned int i; | |
OMX_PARAM_PORTDEFINITIONTYPE portdef; | |
OMX_CONFIG_DISPLAYREGIONTYPE config; | |
OMX_ERRORTYPE err = 0; | |
dbg(9, "omx_update_size %d %d %d\n", width, height, stride); | |
memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); | |
config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); | |
config.nVersion.nVersion = OMX_VERSION; | |
config.nPortIndex = VIDEO_RENDER_PORT; | |
// | |
// | |
#define DEBUG_VIDEO_IN_FRAME 1 | |
// | |
// | |
#ifdef DEBUG_VIDEO_IN_FRAME | |
config.dest_rect.x_offset = 0; | |
config.dest_rect.y_offset = 0; | |
config.dest_rect.width = (int)(1920 / 2); | |
config.dest_rect.height = (int)(1080 / 2); | |
config.mode = OMX_DISPLAY_MODE_LETTERBOX; | |
config.transform = OMX_DISPLAY_ROT0; | |
// config.transform = OMX_DISPLAY_ROT90; | |
config.fullscreen = OMX_FALSE; | |
config.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_TRANSFORM | OMX_DISPLAY_SET_DEST_RECT | | |
OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_MODE); | |
#else | |
config.fullscreen = OMX_TRUE; | |
config.mode = OMX_DISPLAY_MODE_LETTERBOX; | |
config.set = (OMX_DISPLAYSETTYPE)(OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_MODE); | |
#endif | |
// | |
err |= OMX_SetParameter(st->video_render, | |
OMX_IndexConfigDisplayRegion, &config); | |
if (err != 0) | |
{ | |
dbg(9, "omx_display_enable: couldn't configure display region\n"); | |
} | |
memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); | |
portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); | |
portdef.nVersion.nVersion = OMX_VERSION; | |
portdef.nPortIndex = VIDEO_RENDER_PORT; | |
/* specify buffer requirements */ | |
err |= OMX_GetParameter(st->video_render, | |
OMX_IndexParamPortDefinition, &portdef); | |
if (err != 0) | |
{ | |
dbg(9, "omx_display_enable: couldn't retrieve port def\n"); | |
err = ENOMEM; | |
goto exit; | |
} | |
// | |
dbg(9, "omx port definition(before): h=%d w=%d s=%d sh=%d\n", | |
portdef.format.video.nFrameWidth, | |
portdef.format.video.nFrameHeight, | |
portdef.format.video.nStride, | |
portdef.format.video.nSliceHeight); | |
// | |
portdef.format.video.nFrameWidth = width; | |
portdef.format.video.nFrameHeight = height; | |
portdef.format.video.nStride = stride; | |
portdef.format.video.nSliceHeight = height; | |
// | |
portdef.bEnabled = 1; | |
// | |
dbg(9, "omx port definition(after): h=%d w=%d s=%d sh=%d\n", | |
portdef.format.video.nFrameWidth, | |
portdef.format.video.nFrameHeight, | |
portdef.format.video.nStride, | |
portdef.format.video.nSliceHeight); | |
// | |
err |= OMX_SetParameter(st->video_render, | |
OMX_IndexParamPortDefinition, &portdef); | |
if (err) | |
{ | |
dbg(9, "omx_display_enable: could not set port definition\n"); | |
goto exit; | |
} | |
block_until_port_changed(st->video_render, VIDEO_RENDER_PORT, 1); | |
err |= OMX_GetParameter(st->video_render, | |
OMX_IndexParamPortDefinition, &portdef); | |
if (portdef.format.video.nFrameWidth != width || | |
portdef.format.video.nFrameHeight != height || | |
portdef.format.video.nStride != stride) | |
{ | |
dbg(9, "could not set requested resolution\n"); | |
err = EINVAL; | |
goto exit; | |
} | |
if (err != 0 || !portdef.bEnabled) | |
{ | |
dbg(9, "omx_display_enable: failed to set up video port\n"); | |
err = ENOMEM; | |
goto exit; | |
} | |
/* HACK: This state-change sometimes hangs for unknown reasons, | |
* so we just send the state command and wait 50 ms */ | |
/* block_until_state_changed(st->video_render, OMX_StateIdle); */ | |
OMX_SendCommand(st->video_render, OMX_CommandStateSet, | |
OMX_StateIdle, NULL); | |
usleep_usec(150000); | |
if (!st->buffers) | |
{ | |
st->buffers = | |
malloc(portdef.nBufferCountActual * sizeof(void *)); | |
st->num_buffers = portdef.nBufferCountActual; | |
st->current_buffer = 0; | |
for (i = 0; i < portdef.nBufferCountActual; i++) | |
{ | |
err = OMX_AllocateBuffer(st->video_render, | |
&st->buffers[i], VIDEO_RENDER_PORT, | |
st, portdef.nBufferSize); | |
if (err) | |
{ | |
dbg(9, "OMX_AllocateBuffer failed: %d\n", | |
err); | |
err = ENOMEM; | |
goto exit; | |
} | |
} | |
} | |
dbg(9, "omx_update_size: send to execute state\n"); | |
OMX_SendCommand(st->video_render, OMX_CommandStateSet, | |
OMX_StateExecuting, NULL); | |
block_until_state_changed(st->video_render, OMX_StateExecuting); | |
exit: | |
return err; | |
} | |
int omx_display_input_buffer(struct omx_state *st, | |
void **pbuf, uint32_t *plen) | |
{ | |
if (!st->buffers) | |
{ | |
return EINVAL; | |
} | |
*pbuf = st->buffers[0]->pBuffer; | |
*plen = st->buffers[0]->nAllocLen; | |
st->buffers[0]->nFilledLen = *plen; | |
st->buffers[0]->nOffset = 0; | |
return 0; | |
} | |
int omx_display_flush_buffer(struct omx_state *st, uint32_t data_len) | |
{ | |
st->buffers[0]->nFlags = OMX_BUFFERFLAG_STARTTIME; | |
st->buffers[0]->nOffset = 0; | |
st->buffers[0]->nFilledLen = data_len; | |
st->buffers[0]->nTimeStamp = omx_ticks_from_s64 (0); | |
if (OMX_EmptyThisBuffer(st->video_render, st->buffers[0]) | |
!= OMX_ErrorNone) | |
{ | |
dbg(9, "OMX_EmptyThisBuffer error\n"); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* ToxBlinkenwall | |
* (C)Zoff <zoff@zoff.cc> in 2017 - 2019 | |
* | |
* https://github.com/zoff99/ToxBlinkenwall | |
* | |
*/ | |
/** | |
* @file omx.h Raspberry Pi VideoCoreIV OpenMAX interface | |
* | |
* Copyright (C) 2016 - 2017 Creytiv.com | |
* Copyright (C) 2016 - 2017 Jonathan Sieber | |
*/ | |
#include <semaphore.h> | |
#include <IL/OMX_Core.h> | |
#include <IL/OMX_Video.h> | |
#include <IL/OMX_Broadcom.h> | |
#include <pthread.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <unistd.h> | |
struct omx_state | |
{ | |
OMX_HANDLETYPE video_render; | |
OMX_BUFFERHEADERTYPE **buffers; | |
int num_buffers; | |
int current_buffer; | |
}; | |
int omx_init(struct omx_state *st); | |
void omx_deinit(struct omx_state *st); | |
int omx_display_input_buffer(struct omx_state *st, | |
void **pbuf, uint32_t *plen); | |
int omx_display_flush_buffer(struct omx_state *st, uint32_t data_len); | |
int omx_display_enable(struct omx_state *st, | |
int width, int height, int stride); | |
void omx_display_disable(struct omx_state *st); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "omx.h" | |
#include <time.h> | |
#include <assert.h> | |
#include <signal.h> | |
/* | |
* | |
* | |
gcc -O0 \ | |
-DRASPBERRY_PI -DOMX_SKIP64BIT -DUSE_VCHIQ_ARM \ | |
-I/opt/vc/include -I/opt/vc/interface/vmcs_host/linux -I/opt/vc/interface/vcos/pthreads \ | |
-fPIC -export-dynamic -o test_omx \ | |
-lm \ | |
test_omx.c omx.c \ | |
-I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads \ | |
-I/opt/vc/include/interface/vmcs_host/linux -lbrcmEGL -lbrcmGLESv2 \ | |
-lbcm_host -L/opt/vc/lib \ | |
-std=gnu99 \ | |
-lrt \ | |
-lpthread -lv4lconvert \ | |
-lmmal -lmmal_core -lmmal_vc_client -lmmal_components -lmmal_util \ | |
-L/opt/vc/lib -lbcm_host -lvcos -lopenmaxil -ldl | |
* | |
*/ | |
void dbg(int level, const char *fmt, ...) | |
{ | |
} | |
void usleep_usec(uint64_t usec) | |
{ | |
struct timespec ts; | |
ts.tv_sec = usec / 1000000; | |
ts.tv_nsec = (usec % 1000000) * 1000; | |
nanosleep(&ts, NULL); | |
} | |
// https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/hello_pi/hello_encode/encode.c | |
#define WIDTH 640 | |
#define HEIGHT 480 | |
#define STRIDE 640 | |
// generate an animated test card in YUV format | |
static int | |
generate_test_card(void *buf, uint32_t* filledLen, int frame) | |
{ | |
int i, j; | |
char *y = buf; | |
char *u = y + STRIDE * HEIGHT; | |
char *v = u + (STRIDE >> 1) * (HEIGHT >> 1); | |
for (j = 0; j < HEIGHT / 2; j++) { | |
char *py = y + 2 * j * STRIDE; | |
char *pu = u + j * (STRIDE>> 1); | |
char *pv = v + j * (STRIDE>> 1); | |
for (i = 0; i < WIDTH / 2; i++) { | |
int z = (((i + frame) >> 4) ^ ((j + frame) >> 4)) & 15; | |
py[0] = py[1] = py[STRIDE] = py[STRIDE + 1] = 0x80 + z * 0x8; | |
if(i < 4 || i > (WIDTH / 2 - 20)) py[0] = py[1] = py[STRIDE] = py[STRIDE + 1] = 0x0f; // mark horizontal border | |
if(j < 4 || j > (HEIGHT / 2 - 40)) py[0] |= py[1] |= py[STRIDE] |= py[STRIDE + 1] |= 0xf0; // mark vertical border | |
pu[0] = 0x00 + z * 0x10; | |
pv[0] = 0x80 + z * 0x30; | |
py += 2; | |
pu++; | |
pv++; | |
} | |
} | |
*filledLen = (STRIDE* HEIGHT * 3) >> 1; | |
return 1; | |
} | |
struct omx_state omx; | |
void deinit(int sig) | |
{ | |
omx_deinit(&omx); | |
} | |
int main() | |
{ | |
void* pbuf; | |
uint32_t buf_len, fill_len, i; | |
int ret; | |
i = 1000; | |
ret = omx_init(&omx); | |
assert(ret == 0); | |
signal(SIGINT, deinit); | |
ret = omx_display_enable(&omx, WIDTH, HEIGHT, STRIDE); | |
assert(ret == 0); | |
while (i--) { | |
ret = omx_display_input_buffer(&omx, &pbuf, &buf_len); | |
assert(ret == 0); | |
generate_test_card(pbuf, &fill_len, i); | |
assert(fill_len == buf_len); | |
ret = omx_display_flush_buffer(&omx, fill_len); | |
assert(ret == 0); | |
if (i == 800) | |
{ | |
omx_display_xy(1, &omx, WIDTH, HEIGHT, STRIDE); | |
} | |
if (i == 400) | |
{ | |
omx_display_xy(0, &omx, WIDTH, HEIGHT, STRIDE); | |
} | |
} | |
deinit(0); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment