mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 15:02:58 +02:00
apparently, all ports need to be disabled before the inter-component tunnels are teared down, otherwise video_decodeRIL:image pool objects will leak see http://www.raspberrypi.org/phpBB3/viewtopic.php?f=70&t=48177
1180 lines
43 KiB
C
1180 lines
43 KiB
C
/*
|
|
* this code is originally from
|
|
* pidvbip - tvheadend client for the Raspberry Pi
|
|
* (C) Dave Chapman 2012-2013
|
|
*
|
|
* adaption for libstb-hal
|
|
* (C) Stefan Seyfried 2013
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* omx_utils.c -- OMX helper functions for the Raspberry Pi
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "omx_utils.h"
|
|
|
|
#if 1
|
|
#define DEBUGF(...)
|
|
#else
|
|
#define DEBUGF(...) fprintf(stderr,__VA_ARGS__)
|
|
#endif
|
|
|
|
OMX_TICKS pts_to_omx(uint64_t pts)
|
|
{
|
|
OMX_TICKS ticks;
|
|
ticks.nLowPart = pts;
|
|
ticks.nHighPart = pts >> 32;
|
|
return ticks;
|
|
};
|
|
|
|
static int is_port_enabled(OMX_HANDLETYPE handle, int port)
|
|
{
|
|
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
|
|
|
OMX_INIT_STRUCTURE(portdef);
|
|
portdef.nPortIndex = port;
|
|
OERR(OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef));
|
|
|
|
return (portdef.bEnabled == 0 ? 0 : 1);;
|
|
}
|
|
|
|
static void omx_show_state(struct omx_component_t* component, int port1, int port2, int port3)
|
|
{
|
|
OMX_STATETYPE state;
|
|
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
|
|
|
OERR(OMX_GetState(component->h, &state));
|
|
|
|
DEBUGF("%s is in state ",component->name);
|
|
|
|
switch (state) {
|
|
case OMX_StateInvalid: DEBUGF("OMX_StateInvalid\n"); break;
|
|
case OMX_StateLoaded: DEBUGF("OMX_StateLoaded\n"); break;
|
|
case OMX_StateIdle: DEBUGF("OMX_StateIdle\n"); break;
|
|
case OMX_StateExecuting: DEBUGF("OMX_StateExecuting\n"); break;
|
|
case OMX_StatePause: DEBUGF("OMX_StatePause\n"); break;
|
|
case OMX_StateWaitForResources: DEBUGF("OMX_StateWaitForResources\n"); break;
|
|
|
|
default:
|
|
DEBUGF("0x%08x\n",(unsigned int)state);
|
|
}
|
|
|
|
OMX_INIT_STRUCTURE(portdef);
|
|
|
|
if (port1) {
|
|
portdef.nPortIndex = port1;
|
|
OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef));
|
|
DEBUGF("Port %d is %s\n",port1, (portdef.bEnabled == 0 ? "disabled" : "enabled"));
|
|
}
|
|
|
|
if (port2) {
|
|
portdef.nPortIndex = port2;
|
|
OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef));
|
|
DEBUGF("Port %d is %s\n",port2, (portdef.bEnabled == 0 ? "disabled" : "enabled"));
|
|
}
|
|
|
|
if (port3) {
|
|
portdef.nPortIndex = port3;
|
|
OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef));
|
|
DEBUGF("Port %d is %s\n",port3, (portdef.bEnabled == 0 ? "disabled" : "enabled"));
|
|
}
|
|
}
|
|
|
|
|
|
/* From omxtx */
|
|
/* Print some useful information about the state of the port: */
|
|
static void dumpport(OMX_HANDLETYPE handle, int port)
|
|
{
|
|
OMX_VIDEO_PORTDEFINITIONTYPE *viddef;
|
|
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
|
|
|
OMX_INIT_STRUCTURE(portdef);
|
|
portdef.nPortIndex = port;
|
|
OERR(OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &portdef));
|
|
|
|
printf("Port %d is %s, %s\n", portdef.nPortIndex,
|
|
(portdef.eDir == 0 ? "input" : "output"),
|
|
(portdef.bEnabled == 0 ? "disabled" : "enabled"));
|
|
printf("Wants %d bufs, needs %d, size %d, enabled: %d, pop: %d, "
|
|
"aligned %d\n", portdef.nBufferCountActual,
|
|
portdef.nBufferCountMin, portdef.nBufferSize,
|
|
portdef.bEnabled, portdef.bPopulated,
|
|
portdef.nBufferAlignment);
|
|
viddef = &portdef.format.video;
|
|
|
|
switch (portdef.eDomain) {
|
|
case OMX_PortDomainVideo:
|
|
printf("Video type is currently:\n"
|
|
"\tMIME:\t\t%s\n"
|
|
"\tNative:\t\t%p\n"
|
|
"\tWidth:\t\t%d\n"
|
|
"\tHeight:\t\t%d\n"
|
|
"\tStride:\t\t%d\n"
|
|
"\tSliceHeight:\t%d\n"
|
|
"\tBitrate:\t%d\n"
|
|
"\tFramerate:\t%d (%x); (%f)\n"
|
|
"\tError hiding:\t%d\n"
|
|
"\tCodec:\t\t%d\n"
|
|
"\tColour:\t\t%d\n",
|
|
viddef->cMIMEType, viddef->pNativeRender,
|
|
viddef->nFrameWidth, viddef->nFrameHeight,
|
|
viddef->nStride, viddef->nSliceHeight,
|
|
viddef->nBitrate,
|
|
viddef->xFramerate, viddef->xFramerate,
|
|
((float)viddef->xFramerate/(float)65536),
|
|
viddef->bFlagErrorConcealment,
|
|
viddef->eCompressionFormat, viddef->eColorFormat);
|
|
break;
|
|
case OMX_PortDomainImage:
|
|
printf("Image type is currently:\n"
|
|
"\tMIME:\t\t%s\n"
|
|
"\tNative:\t\t%p\n"
|
|
"\tWidth:\t\t%d\n"
|
|
"\tHeight:\t\t%d\n"
|
|
"\tStride:\t\t%d\n"
|
|
"\tSliceHeight:\t%d\n"
|
|
"\tError hiding:\t%d\n"
|
|
"\tCodec:\t\t%d\n"
|
|
"\tColour:\t\t%d\n",
|
|
portdef.format.image.cMIMEType,
|
|
portdef.format.image.pNativeRender,
|
|
portdef.format.image.nFrameWidth,
|
|
portdef.format.image.nFrameHeight,
|
|
portdef.format.image.nStride,
|
|
portdef.format.image.nSliceHeight,
|
|
portdef.format.image.bFlagErrorConcealment,
|
|
portdef.format.image.eCompressionFormat,
|
|
portdef.format.image.eColorFormat);
|
|
break;
|
|
/* Feel free to add others. */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_send_command_and_wait0(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData)
|
|
{
|
|
pthread_mutex_lock(&component->cmd_queue_mutex);
|
|
component->cmd.hComponent = component->h;
|
|
component->cmd.Cmd = Cmd;
|
|
component->cmd.nParam = nParam;
|
|
component->cmd.pCmdData = pCmdData;
|
|
pthread_mutex_unlock(&component->cmd_queue_mutex);
|
|
|
|
OMX_SendCommand(component->h, Cmd, nParam, pCmdData);
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_send_command_and_wait1(struct omx_component_t* component, __attribute__((unused)) OMX_COMMANDTYPE Cmd, __attribute__((unused)) OMX_U32 nParam, __attribute__((unused)) OMX_PTR pCmdData)
|
|
{
|
|
pthread_mutex_lock(&component->cmd_queue_mutex);
|
|
while (component->cmd.hComponent) {
|
|
/* pthread_cond_wait releases the mutex (which must be locked) and blocks on the condition variable */
|
|
pthread_cond_wait(&component->cmd_queue_count_cv,&component->cmd_queue_mutex);
|
|
}
|
|
pthread_mutex_unlock(&component->cmd_queue_mutex);
|
|
|
|
//fprintf(stderr,"Command completed\n");
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_send_command_and_wait(struct omx_component_t* component, OMX_COMMANDTYPE Cmd, OMX_U32 nParam, OMX_PTR pCmdData)
|
|
{
|
|
omx_send_command_and_wait0(component,Cmd,nParam,pCmdData);
|
|
omx_send_command_and_wait1(component,Cmd,nParam,pCmdData);
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
//void omx_wait_for_event(struct omx_pipeleine_t* pipe, OMX_HANDLETYPE *hComponent
|
|
// //&pipe,video_render, OMXEventBufferFlag, 90, OMX_BUFFERFLAG_EOS)
|
|
//{
|
|
//
|
|
//}
|
|
|
|
/* The event handler is called from the OMX component thread */
|
|
static OMX_ERRORTYPE omx_event_handler(OMX_IN OMX_HANDLETYPE hComponent,
|
|
OMX_IN OMX_PTR pAppData,
|
|
OMX_IN OMX_EVENTTYPE eEvent,
|
|
OMX_IN OMX_U32 nData1,
|
|
OMX_IN OMX_U32 nData2,
|
|
OMX_IN OMX_PTR pEventData)
|
|
{
|
|
struct omx_component_t* component = (struct omx_component_t*)pAppData;
|
|
|
|
// fprintf(stderr,"[EVENT]: threadid=%u\n",(unsigned int)pthread_self());
|
|
|
|
switch (eEvent) {
|
|
case OMX_EventError:
|
|
fprintf(stderr,"[EVENT] %s %p has errored: %x\n", component->name, hComponent, (unsigned int)nData1);
|
|
//exit(1);
|
|
//fprintf(stderr,"[EVENT] Waiting for lock\n");
|
|
pthread_mutex_lock(&component->cmd_queue_mutex);
|
|
//fprintf(stderr,"[EVENT] Got lock - cmd.hComponent=%x\n",(unsigned int)pi->cmd.hComponent);
|
|
memset(&component->cmd,0,sizeof(component->cmd));
|
|
//fprintf(stderr,"[EVENT] Clearing cmd\n");
|
|
pthread_cond_signal(&component->cmd_queue_count_cv);
|
|
pthread_mutex_unlock(&component->cmd_queue_mutex);
|
|
//fprintf(stderr,"[EVENT] Returning from event\n");
|
|
return nData1;
|
|
break;
|
|
|
|
case OMX_EventCmdComplete:
|
|
DEBUGF("[EVENT] %s %p has completed the last command (%x).\n", component->name, hComponent, nData1);
|
|
|
|
//fprintf(stderr,"[EVENT] Waiting for lock\n");
|
|
pthread_mutex_lock(&component->cmd_queue_mutex);
|
|
//fprintf(stderr,"[EVENT] Got lock\n");
|
|
if ((nData1 == component->cmd.Cmd) &&
|
|
(nData2 == component->cmd.nParam)) {
|
|
memset(&component->cmd,0,sizeof(component->cmd));
|
|
pthread_cond_signal(&component->cmd_queue_count_cv);
|
|
}
|
|
pthread_mutex_unlock(&component->cmd_queue_mutex);
|
|
|
|
break;
|
|
|
|
case OMX_EventPortSettingsChanged:
|
|
DEBUGF("[EVENT] %s %p port %d settings changed.\n", component->name, hComponent, (unsigned int)nData1);
|
|
if (component->port_settings_changed == 0) { component->port_settings_changed = 1; }
|
|
break;
|
|
|
|
case OMX_EventBufferFlag:
|
|
DEBUGF("[EVENT] Got an EOS event on %s %p (port %d, d2 %x)\n", component->name, hComponent, (unsigned int)nData1, (unsigned int)nData2);
|
|
|
|
if (nData2 & OMX_BUFFERFLAG_EOS) {
|
|
pthread_mutex_lock(&component->eos_mutex);
|
|
component->eos = 1;
|
|
pthread_cond_signal(&component->eos_cv);
|
|
pthread_mutex_unlock(&component->eos_mutex);
|
|
DEBUGF("Sent cond signal for EOS event\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case OMX_EventMark:
|
|
/* TODO: this cast is clearly wrong */
|
|
component->aspect = (int)((OMX_MARKTYPE*)pEventData);
|
|
//fprintf(stderr,"[EVENT] OMX_EventMark - component->aspect=%d\n",component->aspect);
|
|
break;
|
|
|
|
case OMX_EventParamOrConfigChanged:
|
|
DEBUGF("[EVENT] OMX_EventParamOrConfigChanged on component \"%s\" - d1=%x, d2=%x\n",component->name,(unsigned int)nData1, (unsigned int)nData2);
|
|
component->config_changed = 1;
|
|
break;
|
|
|
|
default:
|
|
DEBUGF("[EVENT] Got an event of type %x on %s %p (d1: %x, d2 %x)\n", eEvent,
|
|
component->name, hComponent, (unsigned int)nData1, (unsigned int)nData2);
|
|
}
|
|
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
static OMX_ERRORTYPE omx_empty_buffer_done(__attribute__((unused)) OMX_IN OMX_HANDLETYPE hComponent,
|
|
OMX_IN OMX_PTR pAppData,
|
|
__attribute__((unused)) OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
|
|
{
|
|
struct omx_component_t* component = (struct omx_component_t*)pAppData;
|
|
|
|
if (component->buf_notempty == 0) {
|
|
pthread_mutex_lock(&component->buf_mutex);
|
|
component->buf_notempty = 1;
|
|
pthread_cond_signal(&component->buf_notempty_cv);
|
|
pthread_mutex_unlock(&component->buf_mutex);
|
|
}
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
void omx_clock_set_speed(struct omx_component_t *clk, int v)
|
|
{
|
|
OMX_TIME_CONFIG_SCALETYPE scale;
|
|
OMX_INIT_STRUCTURE(scale);
|
|
if (! clk->h) {
|
|
fprintf(stderr, "omx_clock_set_speed: clock->h is null!\n");
|
|
return;
|
|
}
|
|
|
|
scale.xScale = v;
|
|
OERR(OMX_SetConfig(clk->h, OMX_IndexConfigTimeScale, &scale));
|
|
}
|
|
|
|
static OMX_ERRORTYPE omx_fill_buffer_done(__attribute__((unused)) OMX_IN OMX_HANDLETYPE hComponent,
|
|
__attribute__((unused)) OMX_IN OMX_PTR pAppData,
|
|
__attribute__((unused)) OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
|
|
{
|
|
DEBUGF("[omx_fill_buffer_done]\n");
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
/* Function based on ilclient_create_component() */
|
|
static void omx_disable_all_ports(struct omx_component_t* component)
|
|
{
|
|
OMX_PORT_PARAM_TYPE ports;
|
|
OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit};
|
|
int i;
|
|
|
|
ports.nSize = sizeof(OMX_PORT_PARAM_TYPE);
|
|
ports.nVersion.nVersion = OMX_VERSION;
|
|
|
|
for(i=0; i<4; i++) {
|
|
OMX_ERRORTYPE error = OMX_GetParameter(component->h, types[i], &ports);
|
|
if(error == OMX_ErrorNone) {
|
|
uint32_t j;
|
|
for(j=0; j<ports.nPorts; j++) {
|
|
//fprintf(stderr,"Disabling port %lu\n",ports.nStartPortNumber+j);
|
|
omx_send_command_and_wait(component, OMX_CommandPortDisable, ports.nStartPortNumber+j, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Based on allocbufs from omxtx.
|
|
Buffers are connected as a one-way linked list using pAppPrivate as the pointer to the next element */
|
|
void omx_alloc_buffers(struct omx_component_t *component, int port)
|
|
{
|
|
unsigned int i;
|
|
OMX_BUFFERHEADERTYPE *list = NULL, **end = &list;
|
|
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
|
|
|
OMX_INIT_STRUCTURE(portdef);
|
|
portdef.nPortIndex = port;
|
|
|
|
OERR(OMX_GetParameter(component->h, OMX_IndexParamPortDefinition, &portdef));
|
|
|
|
if (component == &component->pipe->audio_render) {
|
|
DEBUGF("Allocating %d buffers of %d bytes\n",(int)portdef.nBufferCountActual,(int)portdef.nBufferSize);
|
|
DEBUGF("portdef.bEnabled=%d\n",portdef.bEnabled);
|
|
}
|
|
|
|
for (i = 0; i < portdef.nBufferCountActual; i++) {
|
|
OMX_U8 *buf;
|
|
|
|
buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, "buffer");
|
|
|
|
// printf("Allocated a buffer of %u bytes\n",(unsigned int)portdef.nBufferSize);
|
|
|
|
OERR(OMX_UseBuffer(component->h, end, port, NULL, portdef.nBufferSize, buf));
|
|
|
|
end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate);
|
|
}
|
|
|
|
component->buffers = list;
|
|
}
|
|
|
|
void omx_free_buffers(struct omx_component_t *component, int port)
|
|
{
|
|
OMX_BUFFERHEADERTYPE *buf, *prev;
|
|
// int i=0;
|
|
|
|
buf = component->buffers;
|
|
while (buf) {
|
|
prev = buf->pAppPrivate;
|
|
OERR(OMX_FreeBuffer(component->h, port, buf)); /* This also calls free() */
|
|
buf = prev;
|
|
}
|
|
}
|
|
|
|
int omx_get_free_buffer_count(struct omx_component_t* component)
|
|
{
|
|
int n = 0;
|
|
OMX_BUFFERHEADERTYPE *buf = component->buffers;
|
|
|
|
pthread_mutex_lock(&component->buf_mutex);
|
|
while (buf) {
|
|
if (buf->nFilledLen == 0) n++;
|
|
buf = buf->pAppPrivate;
|
|
}
|
|
pthread_mutex_unlock(&component->buf_mutex);
|
|
|
|
return n;
|
|
}
|
|
|
|
#if 0
|
|
static void dump_buffer_status(OMX_BUFFERHEADERTYPE *buffers)
|
|
{
|
|
OMX_BUFFERHEADERTYPE *buf = buffers;
|
|
|
|
while (buf) {
|
|
fprintf(stderr,"*****\n");
|
|
fprintf(stderr,"buf->pAppPrivate=%u\n",(unsigned int)buf->pAppPrivate);
|
|
fprintf(stderr,"buf->nAllocLen=%u\n",(unsigned int)buf->nAllocLen);
|
|
fprintf(stderr,"buf->nFilledLen=%u\n",(unsigned int)buf->nFilledLen);
|
|
fprintf(stderr,"buf->pBuffer=%u\n",(unsigned int)buf->pBuffer);
|
|
fprintf(stderr,"buf->nOffset=%u\n",(unsigned int)buf->nOffset);
|
|
fprintf(stderr,"buf->nFlags=%u\n",(unsigned int)buf->nFlags);
|
|
fprintf(stderr,"buf->nInputPortIndex=%u\n",(unsigned int)buf->nInputPortIndex);
|
|
fprintf(stderr,"buf->nOutputPortIndex=%u\n",(unsigned int)buf->nOutputPortIndex);
|
|
buf = buf->pAppPrivate;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void summarise_buffers(OMX_BUFFERHEADERTYPE *buffers)
|
|
{
|
|
OMX_BUFFERHEADERTYPE *buf = buffers;
|
|
|
|
fprintf(stderr,"*******\n");
|
|
while (buf) {
|
|
fprintf(stderr,"buf->nFilledLen=%u\n",(unsigned int)buf->nFilledLen);
|
|
buf = buf->pAppPrivate;
|
|
}
|
|
}
|
|
|
|
/* Return the next free buffer, or NULL if none are free */
|
|
OMX_BUFFERHEADERTYPE *get_next_buffer(struct omx_component_t* component)
|
|
{
|
|
OMX_BUFFERHEADERTYPE *ret;
|
|
|
|
retry:
|
|
pthread_mutex_lock(&component->buf_mutex);
|
|
|
|
ret = component->buffers;
|
|
while (ret && ret->nFilledLen > 0)
|
|
ret = ret->pAppPrivate;
|
|
|
|
if (!ret)
|
|
component->buf_notempty = 0;
|
|
|
|
if (ret) {
|
|
pthread_mutex_unlock(&component->buf_mutex);
|
|
return ret;
|
|
}
|
|
|
|
//summarise_buffers(pi->video_buffers);
|
|
while (component->buf_notempty == 0)
|
|
pthread_cond_wait(&component->buf_notempty_cv,&component->buf_mutex);
|
|
|
|
pthread_mutex_unlock(&component->buf_mutex);
|
|
|
|
goto retry;
|
|
|
|
/* We never get here, but keep GCC happy */
|
|
return NULL;
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_flush_tunnel(struct omx_component_t* source, int source_port, struct omx_component_t* sink, int sink_port)
|
|
{
|
|
omx_send_command_and_wait(source,OMX_CommandFlush,source_port,NULL);
|
|
omx_send_command_and_wait(sink,OMX_CommandFlush,sink_port,NULL);
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
static void omx_config_pcm(struct omx_component_t* audio_render, int samplerate, int channels, int bitdepth, char* dest)
|
|
{
|
|
OMX_AUDIO_PARAM_PCMMODETYPE pcm;
|
|
// int32_t s;
|
|
|
|
OMX_INIT_STRUCTURE(pcm);
|
|
pcm.nPortIndex = 100;
|
|
pcm.nChannels = channels;
|
|
pcm.eNumData = OMX_NumericalDataSigned;
|
|
pcm.eEndian = OMX_EndianLittle;
|
|
pcm.nSamplingRate = samplerate;
|
|
pcm.bInterleaved = OMX_TRUE;
|
|
pcm.nBitPerSample = bitdepth;
|
|
pcm.ePCMMode = OMX_AUDIO_PCMModeLinear;
|
|
|
|
switch(channels) {
|
|
case 1:
|
|
pcm.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
|
|
break;
|
|
case 8:
|
|
pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
|
pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
|
pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF;
|
|
pcm.eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
|
|
pcm.eChannelMapping[4] = OMX_AUDIO_ChannelLR;
|
|
pcm.eChannelMapping[5] = OMX_AUDIO_ChannelRR;
|
|
pcm.eChannelMapping[6] = OMX_AUDIO_ChannelLS;
|
|
pcm.eChannelMapping[7] = OMX_AUDIO_ChannelRS;
|
|
break;
|
|
case 4:
|
|
pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
|
pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
|
pcm.eChannelMapping[2] = OMX_AUDIO_ChannelLR;
|
|
pcm.eChannelMapping[3] = OMX_AUDIO_ChannelRR;
|
|
break;
|
|
case 2:
|
|
pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
|
|
pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
|
|
break;
|
|
}
|
|
|
|
OERR(OMX_SetParameter(audio_render->h, OMX_IndexParamAudioPcm, &pcm));
|
|
|
|
OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest;
|
|
OMX_INIT_STRUCTURE(ar_dest);
|
|
strcpy((char *)ar_dest.sName, dest);
|
|
OERR(OMX_SetConfig(audio_render->h, OMX_IndexConfigBrcmAudioDestination, &ar_dest));
|
|
}
|
|
|
|
void omx_audio_volume(struct omx_component_t* audio_render, long vol)
|
|
{
|
|
OMX_AUDIO_CONFIG_VOLUMETYPE volume;
|
|
OMX_INIT_STRUCTURE(volume);
|
|
volume.nPortIndex = 100;
|
|
volume.sVolume.nValue = vol;
|
|
|
|
OMX_ERRORTYPE omx_err = OMX_SetParameter(audio_render->h, OMX_IndexConfigAudioVolume, &volume);
|
|
|
|
if (omx_err != OMX_ErrorNone)
|
|
printf("[acodec] omx_audio_volume failed 0x%x\n", omx_err);
|
|
}
|
|
|
|
void omx_set_display_region(struct omx_pipeline_t* pi, int x, int y, int width, int height)
|
|
{
|
|
OMX_CONFIG_DISPLAYREGIONTYPE region;
|
|
|
|
OMX_INIT_STRUCTURE(region);
|
|
region.nPortIndex = 90; /* Video render input port */
|
|
|
|
region.set = OMX_DISPLAY_SET_DEST_RECT | OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_NOASPECT;
|
|
|
|
region.fullscreen = OMX_FALSE;
|
|
region.noaspect = OMX_TRUE;
|
|
|
|
region.dest_rect.x_offset = x;
|
|
region.dest_rect.y_offset = y;
|
|
region.dest_rect.width = width;
|
|
region.dest_rect.height = height;
|
|
|
|
DEBUGF("Setting display region\n");
|
|
OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexConfigDisplayRegion, ®ion));
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_init_component(struct omx_pipeline_t* pi, struct omx_component_t* component, char* compname)
|
|
{
|
|
memset(component,0,sizeof(struct omx_component_t));
|
|
|
|
pthread_mutex_init(&component->cmd_queue_mutex, NULL);
|
|
pthread_cond_init(&component->cmd_queue_count_cv,NULL);
|
|
component->buf_notempty = 1;
|
|
pthread_cond_init(&component->buf_notempty_cv,NULL);
|
|
pthread_cond_init(&component->eos_cv,NULL);
|
|
pthread_mutex_init(&component->eos_mutex,NULL);
|
|
|
|
component->callbacks.EventHandler = omx_event_handler;
|
|
component->callbacks.EmptyBufferDone = omx_empty_buffer_done;
|
|
component->callbacks.FillBufferDone = omx_fill_buffer_done;
|
|
|
|
component->pipe = pi;
|
|
|
|
component->name = compname;
|
|
|
|
/* Create OMX component */
|
|
OERR(OMX_GetHandle(&component->h, compname, component, &component->callbacks));
|
|
|
|
/* Disable all ports */
|
|
omx_disable_all_ports(component);
|
|
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
|
|
/* From: http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/camera.html
|
|
|
|
In order to optimise loading of relevant drivers, the recommended initialisation sequence is:
|
|
|
|
Create component.
|
|
Use OMX_IndexConfigRequestCallback to request callbacks on OMX_IndexParamCameraDeviceNumber.
|
|
Set OMX_IndexParamISPTunerName.
|
|
Set OMX_IndexParamCameraFlashType.
|
|
Set OMX_IndexParamCameraDeviceNumber.
|
|
Wait for the callback that OMX_IndexParamCameraDeviceNumber has changed. At this point, all the drivers have been loaded. Other settings can be applied whilst waiting for this event.
|
|
Query for OMX_IndexConfigCameraSensorModes as required.
|
|
Change state to IDLE, and proceed as required.
|
|
*/
|
|
|
|
OMX_ERRORTYPE omx_setup_camera_pipeline(struct omx_pipeline_t* pi)
|
|
{
|
|
|
|
// Create component.
|
|
omx_init_component(pi, &pi->camera, "OMX.broadcom.camera");
|
|
|
|
// Use OMX_IndexConfigRequestCallback to request callbacks on OMX_IndexParamCameraDeviceNumber.
|
|
OMX_CONFIG_REQUESTCALLBACKTYPE cbtype;
|
|
OMX_INIT_STRUCTURE(cbtype);
|
|
cbtype.nPortIndex=OMX_ALL;
|
|
cbtype.nIndex=OMX_IndexParamCameraDeviceNumber;
|
|
cbtype.bEnable = OMX_TRUE;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigRequestCallback, &cbtype));
|
|
|
|
// Set OMX_IndexParamISPTunerName.
|
|
|
|
// Set OMX_IndexParamCameraFlashType.
|
|
|
|
// Set OMX_IndexParamCameraDeviceNumber.
|
|
OMX_PARAM_U32TYPE device;
|
|
OMX_INIT_STRUCTURE(device);
|
|
device.nPortIndex = OMX_ALL;
|
|
device.nU32 = 0;
|
|
OERR(OMX_SetParameter(pi->camera.h, OMX_IndexParamCameraDeviceNumber, &device));
|
|
|
|
dumpport(pi->camera.h, 71);
|
|
|
|
/* Set the resolution */
|
|
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
|
OMX_INIT_STRUCTURE(portdef);
|
|
portdef.nPortIndex = 71;
|
|
OERR(OMX_GetParameter(pi->camera.h, OMX_IndexParamPortDefinition, &portdef));
|
|
portdef.format.image.nFrameWidth = 640;
|
|
portdef.format.image.nFrameHeight = 360;
|
|
portdef.format.image.nStride = 640;
|
|
OERR(OMX_SetParameter(pi->camera.h, OMX_IndexParamPortDefinition, &portdef));
|
|
|
|
/* Set the framerate */
|
|
OMX_CONFIG_FRAMERATETYPE framerate;
|
|
OMX_INIT_STRUCTURE(framerate);
|
|
framerate.nPortIndex = 71;
|
|
framerate.xEncodeFramerate = 25 << 16; // Q16 format - 25fps
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigVideoFramerate, &framerate));
|
|
|
|
/* Set the sharpness */
|
|
OMX_CONFIG_SHARPNESSTYPE sharpness;
|
|
OMX_INIT_STRUCTURE(sharpness);
|
|
sharpness.nPortIndex = OMX_ALL;
|
|
sharpness.nSharpness = -50; /* -100 to 100 */
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonSharpness, &sharpness));
|
|
|
|
/* Set the contrast */
|
|
OMX_CONFIG_CONTRASTTYPE contrast;
|
|
OMX_INIT_STRUCTURE(contrast);
|
|
contrast.nPortIndex = OMX_ALL;
|
|
contrast.nContrast = -10; /* -100 to 100 */
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonContrast, &contrast));
|
|
|
|
/* Set the brightness */
|
|
OMX_CONFIG_BRIGHTNESSTYPE brightness;
|
|
OMX_INIT_STRUCTURE(brightness);
|
|
brightness.nPortIndex = OMX_ALL;
|
|
brightness.nBrightness = 50; /* 0 to 100 */
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonBrightness, &brightness));
|
|
|
|
/* Set the saturation */
|
|
OMX_CONFIG_SATURATIONTYPE saturation;
|
|
OMX_INIT_STRUCTURE(saturation);
|
|
saturation.nPortIndex = OMX_ALL;
|
|
saturation.nSaturation = 0; /* -100 to 100 */
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonSaturation, &saturation));
|
|
|
|
/* Video stabilisation */
|
|
OMX_CONFIG_FRAMESTABTYPE framestab;
|
|
OMX_INIT_STRUCTURE(framestab);
|
|
framestab.nPortIndex = OMX_ALL;
|
|
framestab.bStab = OMX_FALSE;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonFrameStabilisation, &framestab));
|
|
|
|
/* Set EV compensation, ISO and metering mode */
|
|
OMX_CONFIG_EXPOSUREVALUETYPE exposurevalue;
|
|
OMX_INIT_STRUCTURE(exposurevalue);
|
|
exposurevalue.nPortIndex = OMX_ALL;
|
|
OERR(OMX_GetConfig(pi->camera.h, OMX_IndexConfigCommonExposureValue, &exposurevalue));
|
|
fprintf(stderr,"nSensitivity=%d\n",exposurevalue.nSensitivity);
|
|
exposurevalue.xEVCompensation = 0; /* Fixed point value stored as Q16 */
|
|
exposurevalue.nSensitivity = 100; /**< e.g. nSensitivity = 100 implies "ISO 100" */
|
|
exposurevalue.bAutoSensitivity = OMX_FALSE;
|
|
exposurevalue.eMetering = OMX_MeteringModeAverage;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonExposureValue, &exposurevalue));
|
|
|
|
/* Set exposure mode */
|
|
OMX_CONFIG_EXPOSURECONTROLTYPE exposure;
|
|
OMX_INIT_STRUCTURE(exposure);
|
|
exposure.nPortIndex = OMX_ALL;
|
|
exposure.eExposureControl = OMX_ExposureControlAuto;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonExposure, &exposure));
|
|
|
|
/* Set AWB mode */
|
|
OMX_CONFIG_WHITEBALCONTROLTYPE awb;
|
|
OMX_INIT_STRUCTURE(awb);
|
|
awb.nPortIndex = OMX_ALL;
|
|
awb.eWhiteBalControl = OMX_WhiteBalControlAuto;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonWhiteBalance, &awb));
|
|
|
|
/* Set image effect */
|
|
OMX_CONFIG_IMAGEFILTERTYPE imagefilter;
|
|
OMX_INIT_STRUCTURE(imagefilter);
|
|
imagefilter.nPortIndex = OMX_ALL;
|
|
imagefilter.eImageFilter = OMX_ImageFilterNone;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonImageFilter, &imagefilter));
|
|
|
|
/* Set colour effect */
|
|
OMX_CONFIG_COLORENHANCEMENTTYPE colour;
|
|
OMX_INIT_STRUCTURE(colour);
|
|
colour.nPortIndex = OMX_ALL;
|
|
colour.bColorEnhancement = OMX_FALSE;
|
|
colour.nCustomizedU = 128;
|
|
colour.nCustomizedV = 128;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigCommonColorEnhancement, &colour));
|
|
|
|
/* Turn off the LED - doesn't work! */
|
|
OMX_CONFIG_PRIVACYINDICATORTYPE privacy;
|
|
OMX_INIT_STRUCTURE(privacy);
|
|
privacy.ePrivacyIndicatorMode = OMX_PrivacyIndicatorOff;
|
|
OERR(OMX_SetConfig(pi->camera.h, OMX_IndexConfigPrivacyIndicator, &privacy));
|
|
|
|
// Wait for the callback that OMX_IndexParamCameraDeviceNumber has
|
|
// changed. At this point, all the drivers have been loaded. Other
|
|
// settings can be applied whilst waiting for this event.
|
|
fprintf(stderr,"Waiting for camera config to change\n");
|
|
while (!pi->camera.config_changed); /* TODO: Use a condition variable */
|
|
fprintf(stderr,"Config changed\n");
|
|
|
|
// Query for OMX_IndexConfigCameraSensorModes as required.
|
|
|
|
// Change state to IDLE, and proceed as required.
|
|
omx_send_command_and_wait(&pi->camera, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
OMX_CONFIG_PORTBOOLEANTYPE cameraport;
|
|
OMX_INIT_STRUCTURE(cameraport);
|
|
cameraport.nPortIndex = 71;
|
|
cameraport.bEnabled = OMX_TRUE;
|
|
OERR(OMX_SetParameter(pi->camera.h, OMX_IndexConfigPortCapturing, &cameraport));
|
|
|
|
omx_init_component(pi, &pi->video_render, "OMX.broadcom.video_render");
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
OERR(OMX_SetupTunnel(pi->camera.h, 71, pi->video_render.h, 90)); /* Camera capture port to video render */
|
|
|
|
omx_send_command_and_wait(&pi->camera, OMX_CommandPortEnable, 71, NULL);
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandPortEnable, 90, NULL);
|
|
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
|
omx_send_command_and_wait(&pi->camera, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
|
|
|
omx_set_display_region(pi, 1200, 180, 640, 360);
|
|
|
|
OMX_CONFIG_DISPLAYREGIONTYPE region;
|
|
OMX_INIT_STRUCTURE(region);
|
|
region.nPortIndex = 90; /* Video render input port */
|
|
region.set = OMX_DISPLAY_SET_LAYER;
|
|
region.layer = 10;
|
|
OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexConfigDisplayRegion, ®ion));
|
|
|
|
fprintf(stderr,"Camera pipeline configured\n");
|
|
|
|
dumpport(pi->camera.h, 71);
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
OMX_ERRORTYPE omx_setup_pipeline(struct omx_pipeline_t* pi, OMX_VIDEO_CODINGTYPE video_codec, char* audio_dest, __attribute__((unused)) int is_hd)
|
|
{
|
|
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
|
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
|
|
|
|
OMX_CONFIG_BOOLEANTYPE configBoolTrue;
|
|
OMX_INIT_STRUCTURE(configBoolTrue);
|
|
configBoolTrue.bEnabled = OMX_TRUE;
|
|
|
|
pi->do_deinterlace = 0;
|
|
|
|
#if 0
|
|
if (((is_hd == 0) && (global_settings.deinterlace_sd)) || ((is_hd == 1) && (global_settings.deinterlace_hd))) {
|
|
DEBUGF("Enabling de-interlace\n");
|
|
pi->do_deinterlace = 1;
|
|
}
|
|
#endif
|
|
|
|
omx_init_component(pi, &pi->video_decode, "OMX.broadcom.video_decode");
|
|
omx_init_component(pi, &pi->video_render, "OMX.broadcom.video_render");
|
|
|
|
if (pi->do_deinterlace) {
|
|
DEBUGF("Enabling de-interlacer\n");
|
|
/* De-interlacer. Input port 190, Output port 191. Insert between decoder and scheduler */
|
|
omx_init_component(pi, &pi->image_fx, "OMX.broadcom.image_fx");
|
|
|
|
/* Configure image_fx */
|
|
omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
OMX_CONFIG_IMAGEFILTERPARAMSTYPE imagefilter;
|
|
OMX_INIT_STRUCTURE(imagefilter);
|
|
imagefilter.nPortIndex=191;
|
|
imagefilter.nNumParams=1;
|
|
imagefilter.nParams[0]=3; //???
|
|
imagefilter.eImageFilter=OMX_ImageFilterDeInterlaceAdvanced;
|
|
|
|
OERR(OMX_SetConfig(pi->image_fx.h, OMX_IndexConfigCommonImageFilterParameters, &imagefilter));
|
|
} else {
|
|
memset(&pi->image_fx,0,sizeof(struct omx_component_t));
|
|
}
|
|
|
|
|
|
omx_init_component(pi, &pi->clock, "OMX.broadcom.clock");
|
|
|
|
OMX_INIT_STRUCTURE(cstate);
|
|
cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
|
|
cstate.nWaitMask = OMX_CLOCKPORT0|OMX_CLOCKPORT1;
|
|
OERR(OMX_SetParameter(pi->clock.h, OMX_IndexConfigTimeClockState, &cstate));
|
|
|
|
OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock;
|
|
OMX_INIT_STRUCTURE(refClock);
|
|
refClock.eClock = OMX_TIME_RefClockAudio;
|
|
OERR(OMX_SetConfig(pi->clock.h, OMX_IndexConfigTimeActiveRefClock, &refClock));
|
|
|
|
omx_init_component(pi, &pi->video_scheduler, "OMX.broadcom.video_scheduler");
|
|
|
|
/* Initialise audio output - hardcoded to 48000/Stereo/16-bit */
|
|
omx_init_component(pi, &pi->audio_render, "OMX.broadcom.audio_render");
|
|
|
|
OMX_PARAM_PORTDEFINITIONTYPE param;
|
|
OMX_INIT_STRUCTURE(param);
|
|
param.nPortIndex = 100;
|
|
|
|
OERR(OMX_GetParameter(pi->audio_render.h, OMX_IndexParamPortDefinition, ¶m));
|
|
param.nBufferSize = 8192; /* Needs to be big enough for one frame of data */
|
|
param.nBufferCountActual = 32; /* Arbitrary */
|
|
OERR(OMX_SetParameter(pi->audio_render.h, OMX_IndexParamPortDefinition, ¶m));
|
|
|
|
omx_config_pcm(&pi->audio_render, 48000, 2, 16, audio_dest);
|
|
|
|
OERR(OMX_SetConfig(pi->audio_render.h, OMX_IndexConfigBrcmClockReferenceSource, &configBoolTrue));
|
|
|
|
omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
omx_send_command_and_wait0(&pi->audio_render, OMX_CommandPortEnable, 100, NULL);
|
|
omx_alloc_buffers(&pi->audio_render, 100);
|
|
omx_send_command_and_wait1(&pi->audio_render, OMX_CommandPortEnable, 100, NULL);
|
|
|
|
|
|
/* Setup clock tunnels first */
|
|
omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
OERR(OMX_SetupTunnel(pi->clock.h, 80, pi->audio_render.h, 101));
|
|
OERR(OMX_SetupTunnel(pi->clock.h, 81, pi->video_scheduler.h, 12));
|
|
|
|
OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortEnable, 80, NULL));
|
|
OERR(OMX_SendCommand(pi->video_scheduler.h, OMX_CommandPortEnable, 12, NULL));
|
|
|
|
OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortEnable, 81, NULL));
|
|
OERR(OMX_SendCommand(pi->audio_render.h, OMX_CommandPortEnable, 101, NULL));
|
|
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
|
|
|
/* Configure video_decoder */
|
|
omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
/* Enable lazy image pool destroying */
|
|
OERR(OMX_SetConfig(pi->video_decode.h, OMX_IndexParamBrcmLazyImagePoolDestroy, &configBoolTrue));
|
|
|
|
OMX_INIT_STRUCTURE(format);
|
|
format.nPortIndex = 130;
|
|
format.eCompressionFormat = video_codec;
|
|
|
|
OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamVideoPortFormat, &format));
|
|
|
|
/* Enable error concealment for H264 only - without this, HD channels don't work reliably */
|
|
if (video_codec == OMX_VIDEO_CodingAVC) {
|
|
OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ec;
|
|
OMX_INIT_STRUCTURE(ec);
|
|
ec.bStartWithValidFrame = OMX_FALSE;
|
|
OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ec));
|
|
}
|
|
|
|
/* Enable video decoder input port */
|
|
omx_send_command_and_wait0(&pi->video_decode, OMX_CommandPortEnable, 130, NULL);
|
|
|
|
/* Allocate input buffers */
|
|
omx_alloc_buffers(&pi->video_decode, 130);
|
|
|
|
/* Wait for input port to be enabled */
|
|
omx_send_command_and_wait1(&pi->video_decode, OMX_CommandPortEnable, 130, NULL);
|
|
|
|
/* Change video_decode to OMX_StateExecuting */
|
|
omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
|
|
|
/* Change audio_render to OMX_StateExecuting */
|
|
omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateExecuting, NULL);
|
|
|
|
/* Enable passing of buffer marks */
|
|
OERR(OMX_SetParameter(pi->video_decode.h, OMX_IndexParamPassBufferMarks, &configBoolTrue));
|
|
OERR(OMX_SetParameter(pi->video_render.h, OMX_IndexParamPassBufferMarks, &configBoolTrue));
|
|
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
void omx_teardown_pipeline(struct omx_pipeline_t* pi)
|
|
{
|
|
// OMX_BUFFERHEADERTYPE *buf;
|
|
// int i=1;
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline:\n");
|
|
DEBUGF("pipe->video_decode.port_settings_changed = %d\n",pi->video_decode.port_settings_changed);
|
|
DEBUGF("pipe->image_fx.port_settings_changed = %d\n",pi->image_fx.port_settings_changed);
|
|
DEBUGF("pipe->video_scheduler.port_settings_changed = %d\n",pi->video_scheduler.port_settings_changed);
|
|
//dumpport(pi->video_decode.h,130);
|
|
|
|
#if 0
|
|
/* Indicate end of video stream */
|
|
buf = get_next_buffer(&pi->video_decode);
|
|
|
|
buf->nFilledLen = 0;
|
|
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
|
|
|
|
OERR(OMX_EmptyThisBuffer(pi->video_decode.h, buf));
|
|
|
|
/* NOTE: Three events are sent after the previous command:
|
|
|
|
[EVENT] Got an event of type 4 on video_decode 0x426a10 (d1: 83, d2 1)
|
|
[EVENT] Got an event of type 4 on video_scheduler 0x430d10 (d1: b, d2 1)
|
|
[EVENT] Got an event of type 4 on video_render 0x430b30 (d1: 5a, d2 1) 5a = port (90) 1 = OMX_BUFFERFLAG_EOS
|
|
*/
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
DEBUGF("[vcodec] omx_teardown pipeline 2\n");
|
|
/* Wait for video_decode to shutdown */
|
|
pthread_mutex_lock(&pi->video_decode.eos_mutex);
|
|
while (!pi->video_decode.eos)
|
|
pthread_cond_wait(&pi->video_decode.eos_cv,&pi->video_decode.eos_mutex);
|
|
pthread_mutex_unlock(&pi->video_decode.eos_mutex);
|
|
#endif
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 1\n");
|
|
|
|
/* Transition all components to Idle, if they have been initialised */
|
|
omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
DEBUGF("pipe->do_deinterlace=%d, pipe->image_fx=%d\n",pi->do_deinterlace,(int)pi->image_fx.h);
|
|
if (pi->video_decode.port_settings_changed == 2) {
|
|
if (pi->do_deinterlace) {
|
|
omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
} else {
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
}
|
|
}
|
|
if (pi->image_fx.port_settings_changed == 2) {
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
}
|
|
if (pi->video_scheduler.port_settings_changed == 2) {
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
}
|
|
omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateIdle, NULL);
|
|
|
|
#if 0
|
|
DEBUGF("[vcodec] omx_teardown pipeline 2\n");
|
|
/* Wait for video_render to shutdown */
|
|
pthread_mutex_lock(&pi->video_render.eos_mutex);
|
|
while (!pi->video_render.eos)
|
|
pthread_cond_wait(&pi->video_render.eos_cv,&pi->video_render.eos_mutex);
|
|
pthread_mutex_unlock(&pi->video_render.eos_mutex);
|
|
#endif
|
|
|
|
/*
|
|
Pipeline is as follows:
|
|
|
|
[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render
|
|
clock 81 -> 12 video_scheduler
|
|
clock 80 -> 101 audio_render
|
|
[audio data] -> 100 audio_render
|
|
*/
|
|
|
|
/* Flush entrances to pipeline */
|
|
omx_send_command_and_wait(&pi->video_decode,OMX_CommandFlush,130,NULL);
|
|
omx_send_command_and_wait(&pi->audio_render,OMX_CommandFlush,100,NULL);
|
|
|
|
/* Flush all tunnels */
|
|
DEBUGF("[vcodec] omx_teardown pipeline 3\n");
|
|
if (pi->do_deinterlace) {
|
|
omx_flush_tunnel(&pi->video_decode, 131, &pi->image_fx, 190);
|
|
omx_flush_tunnel(&pi->image_fx, 191, &pi->video_scheduler, 10);
|
|
} else {
|
|
omx_flush_tunnel(&pi->video_decode, 131, &pi->video_scheduler, 10);
|
|
}
|
|
DEBUGF("[vcodec] omx_teardown pipeline 4\n");
|
|
omx_flush_tunnel(&pi->video_scheduler, 11, &pi->video_render, 90);
|
|
omx_flush_tunnel(&pi->clock, 81, &pi->video_scheduler, 12);
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 2b\n");
|
|
|
|
omx_send_command_and_wait(&pi->video_scheduler,OMX_CommandFlush,10,NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 5\n");
|
|
|
|
omx_flush_tunnel(&pi->clock, 80, &pi->audio_render, 101);
|
|
|
|
/* Disable audio_render input port and buffers */
|
|
omx_send_command_and_wait0(&pi->audio_render, OMX_CommandPortDisable, 100, NULL);
|
|
omx_free_buffers(&pi->audio_render, 100);
|
|
omx_send_command_and_wait1(&pi->audio_render, OMX_CommandPortDisable, 100, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 9\n");
|
|
|
|
/* Scheduler -> render tunnel */
|
|
if (pi->video_scheduler.port_settings_changed == 2) {
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 11, NULL);
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandPortDisable, 90, NULL);
|
|
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 10, NULL);
|
|
}
|
|
|
|
if ((pi->image_fx.port_settings_changed == 2) && (pi->do_deinterlace)) {
|
|
omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortDisable, 190, NULL);
|
|
omx_send_command_and_wait(&pi->image_fx, OMX_CommandPortDisable, 191, NULL);
|
|
}
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 10\n");
|
|
|
|
/* NOTE: The clock disable doesn't complete until after the video scheduler port is
|
|
disabled (but it completes before the video scheduler port disabling completes). */
|
|
OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortDisable, 80, NULL));
|
|
omx_send_command_and_wait(&pi->audio_render, OMX_CommandPortDisable, 101, NULL);
|
|
OERR(OMX_SendCommand(pi->clock.h, OMX_CommandPortDisable, 81, NULL));
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandPortDisable, 12, NULL);
|
|
if (pi->video_decode.port_settings_changed == 2) {
|
|
//dumpport(pi->video_decode.h,131);
|
|
omx_send_command_and_wait(&pi->video_decode, OMX_CommandPortDisable, 131, NULL);
|
|
}
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 11\n");
|
|
|
|
/* Disable video_decode input port and buffers */
|
|
//dumpport(pi->video_decode.h,130);
|
|
omx_send_command_and_wait0(&pi->video_decode, OMX_CommandPortDisable, 130, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 6\n");
|
|
omx_free_buffers(&pi->video_decode, 130);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 7\n");
|
|
omx_send_command_and_wait1(&pi->video_decode, OMX_CommandPortDisable, 130, NULL);
|
|
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 8a\n");
|
|
|
|
//dumpport(pi->video_scheduler.h,10);
|
|
#if 0
|
|
/* ports need to be disabled before tunnels are shut down. Otherwise,
|
|
* "video_decodeRIL:image pool" buffers will leak (check with "vcdbg reloc"
|
|
* the following paranoia checks are not necessary, because
|
|
* omx_send_command_and_wait() waits for completion
|
|
*/
|
|
fprintf(stderr,"is_enabled 130\n");
|
|
while (is_port_enabled(pi->video_decode.h, 130))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 131\n");
|
|
while (is_port_enabled(pi->video_decode.h, 131))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 10\n");
|
|
while (is_port_enabled(pi->video_scheduler.h, 10))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 11\n");
|
|
while (is_port_enabled(pi->video_scheduler.h, 11))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 90\n");
|
|
while (is_port_enabled(pi->video_render.h, 90))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 101\n");
|
|
while (is_port_enabled(pi->audio_render.h, 101))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
fprintf(stderr,"is_enabled 100\n");
|
|
while (is_port_enabled(pi->audio_render.h, 100))
|
|
{fprintf(stderr, "."); usleep(1);}
|
|
DEBUGF("[vcodec] omx_teardown pipeline 8b\n");
|
|
#endif
|
|
omx_show_state(&pi->video_decode,130,131,0);
|
|
dumpport(pi->video_decode.h,131);
|
|
omx_show_state(&pi->video_scheduler,10,11,12);
|
|
if (pi->do_deinterlace) { omx_show_state(&pi->image_fx,190,191,0); }
|
|
omx_show_state(&pi->video_render,90,0,0);
|
|
omx_show_state(&pi->audio_render,100,101,0);
|
|
omx_show_state(&pi->clock,80,81,0);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 8c\n");
|
|
|
|
/* Teardown tunnels */
|
|
/*
|
|
Pipeline is as follows:
|
|
|
|
[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render
|
|
clock 81 -> 12 video_scheduler
|
|
clock 80 -> 101 audio_render
|
|
[audio data] -> 100 audio_render
|
|
*/
|
|
//dumpport(pi->video_decode.h,131);
|
|
OERR(OMX_SetupTunnel(pi->video_scheduler.h, 10, NULL, 0));
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 12b\n");
|
|
|
|
if (pi->do_deinterlace) {
|
|
OERR(OMX_SetupTunnel(pi->image_fx.h, 190, NULL, 0));
|
|
OERR(OMX_SetupTunnel(pi->image_fx.h, 191, NULL, 0));
|
|
}
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 13\n");
|
|
|
|
OERR(OMX_SetupTunnel(pi->video_scheduler.h, 11, NULL, 0));
|
|
OERR(OMX_SetupTunnel(pi->video_render.h, 90, NULL, 0));
|
|
|
|
OERR(OMX_SetupTunnel(pi->clock.h, 81, NULL, 0));
|
|
OERR(OMX_SetupTunnel(pi->video_scheduler.h, 12, NULL, 0));
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 13b\n");
|
|
|
|
OERR(OMX_SetupTunnel(pi->clock.h, 80, NULL, 0));
|
|
OERR(OMX_SetupTunnel(pi->audio_render.h, 101, NULL, 0));
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 8b\n");
|
|
|
|
|
|
/*
|
|
Pipeline is as follows:
|
|
|
|
[video data] -> 130 video_decode 131 -> 190 image_fx 191 -> 10 video_scheduler 11 -> 90 video_render
|
|
clock 81 -> 12 video_scheduler
|
|
clock 80 -> 101 audio_render
|
|
[audio data] -> 100 audio_render
|
|
*/
|
|
|
|
//dumpport(pi->video_decode.h,130);
|
|
if (is_port_enabled(pi->video_decode.h, 130)) {
|
|
fprintf(stderr,"Unexpected error video_decode port 130 is not disabled\n");
|
|
exit(1);
|
|
}
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 12\n");
|
|
|
|
OERR(OMX_SetupTunnel(pi->video_decode.h, 131, NULL, 0));
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15\n");
|
|
|
|
omx_show_state(&pi->video_decode,130,131,0);
|
|
|
|
/* Transition all components to Loaded */
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15a\n");
|
|
omx_send_command_and_wait(&pi->video_decode, OMX_CommandStateSet, OMX_StateLoaded, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15b\n");
|
|
omx_send_command_and_wait(&pi->video_scheduler, OMX_CommandStateSet, OMX_StateLoaded, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15c\n");
|
|
if (((pi->video_decode.port_settings_changed == 2) && (pi->do_deinterlace)) || (pi->image_fx.port_settings_changed == 2)) {
|
|
omx_send_command_and_wait(&pi->video_render, OMX_CommandStateSet, OMX_StateLoaded, NULL);
|
|
}
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15d\n");
|
|
omx_send_command_and_wait(&pi->audio_render, OMX_CommandStateSet, OMX_StateLoaded, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15e\n");
|
|
omx_send_command_and_wait(&pi->clock, OMX_CommandStateSet, OMX_StateLoaded, NULL);
|
|
DEBUGF("[vcodec] omx_teardown pipeline 15f\n");
|
|
if (pi->do_deinterlace) { omx_send_command_and_wait(&pi->image_fx, OMX_CommandStateSet, OMX_StateLoaded, NULL); }
|
|
|
|
DEBUGF("[vcodec] omx_teardown pipeline 16\n");
|
|
/* Finally free the component handles */
|
|
OERR(OMX_FreeHandle(pi->video_decode.h));
|
|
OERR(OMX_FreeHandle(pi->video_scheduler.h));
|
|
OERR(OMX_FreeHandle(pi->video_render.h));
|
|
OERR(OMX_FreeHandle(pi->audio_render.h));
|
|
OERR(OMX_FreeHandle(pi->clock.h));
|
|
if (pi->do_deinterlace) { OERR(OMX_FreeHandle(pi->image_fx.h)); }
|
|
DEBUGF("[vcodec] omx_teardown pipeline 17\n");
|
|
}
|