From 19b0ff20e16e73fdfea9b7e1842d35418fb16453 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Thu, 10 Oct 2013 00:10:21 +0200 Subject: [PATCH] raspi: add first try of video decoder implementation This is very raw and unfinished, it clearly needs some more polishing. But it decodes h264 channels :-) --- raspi/video.cpp | 259 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/raspi/video.cpp b/raspi/video.cpp index 91c18d1..48b58cd 100644 --- a/raspi/video.cpp +++ b/raspi/video.cpp @@ -23,7 +23,15 @@ #include #include +#include + +#include +extern "C" { +#include "ilclient.h" +} + #include "video_lib.h" +#include "dmx_lib.h" #include "lt_debug.h" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args) @@ -32,12 +40,40 @@ cVideo *videoDecoder = NULL; int system_rev = 0; +extern cDemux *videoDemux; +static int dec_running = false; + + +class Dec: public OpenThreads::Thread +{ + public: + Dec(); + ~Dec(); + private: + void run(); +}; + +Dec::Dec() +{ + start(); +} + +Dec::~Dec() +{ + dec_running = false; + join(); +} + + +static Dec *dec = NULL; + cVideo::cVideo(int, void *, void *, unsigned int) { lt_debug("%s\n", __func__); display_aspect = DISPLAY_AR_16_9; display_crop = DISPLAY_AR_MODE_LETTERBOX; v_format = VIDEO_FORMAT_MPEG2; + bcm_host_init(); } cVideo::~cVideo(void) @@ -64,12 +100,19 @@ int cVideo::setCroppingMode(int) int cVideo::Start(void *, unsigned short, unsigned short, void *) { lt_debug("%s running %d >\n", __func__, thread_running); + if (!dec) { + dec = new Dec(); + } return 0; } int cVideo::Stop(bool) { lt_debug("%s running %d >\n", __func__, thread_running); + if (dec) { + delete dec; + dec = NULL; + } return 0; } @@ -187,3 +230,219 @@ void cVideo::SetDemux(cDemux *) { lt_debug("%s: not implemented yet\n", __func__); } + + +void Dec::run() +{ + hal_set_threadname("hal:vdec"); + int ret = 0; + /* write to file instead of decoding. For testing only. */ + if (getenv("DEC_OUT")) { + lt_info_c("Dec::run %d\n", __LINE__); + FILE *ff = fopen("/tmp/video.pes", "w"); + unsigned char buf[65536]; + dec_running = true; + while (dec_running) + { + ret = videoDemux->Read(buf, 65536, 10); + if (ret <= 0) { + if (!dec_running) + break; + continue; + } + fwrite(buf, 1, ret, ff); + } + lt_info_c("Dec::run %d\n", __LINE__); + return; + } + lt_info_c("Dec::run %d\n", __LINE__); + + /* this code is mostly copied from hello_pi/hello_video example */ + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE conc; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + int status = 0; + unsigned int data_len = 0; + int packet_size = 80<<10; /* 80kB */ + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if ((client = ilclient_init()) == NULL) { + lt_info_c("Dec::run %d\n", __LINE__); + return; + } + + if (OMX_Init() != OMX_ErrorNone) { + lt_info_c("Dec::run %d\n", __LINE__); + ilclient_destroy(client); + return; + } + // create video_decode + if (ilclient_create_component(client, &video_decode, (char *)"video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create video_render + if (status == 0 && ilclient_create_component(client, &video_render, (char *)"video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[1] = video_render; + + // create clock + if (status == 0 && ilclient_create_component(client, &clock, (char *)"clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if (clock != NULL && + OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if (status == 0 && + ilclient_create_component(client, &video_scheduler, (char *)"video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, video_render, 90); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if (status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if (status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&conc, 0, sizeof(conc)); + conc.nSize = sizeof(conc); + conc.nVersion.nVersion = OMX_VERSION; + conc.bStartWithValidFrame = OMX_FALSE; + if (status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmVideoDecodeErrorConcealment, &conc) != OMX_ErrorNone) + status = -16; + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + // format.xFramerate = 50 * (1 << 16); + + lt_info_c("Dec::run %d status %d\n", __LINE__, status); + if (status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + dec_running = true; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + lt_info_c("Dec::run %d\n", __LINE__); + while ((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + again: + //lt_info_c("Dec::run %d\n", __LINE__); + ret = videoDemux->Read(dest, packet_size-data_len, 10); + if (ret <= 0) { + if (!dec_running) + break; + goto again; + } + + data_len += ret; + //lt_info_c("Dec::run %d data_len %d\n", __LINE__, data_len); + + if (port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if (ilclient_setup_tunnel(tunnel, 0, 0) != 0) { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to video_render + if (ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) { + status = -12; + break; + } + + ilclient_change_component_state(video_render, OMX_StateExecuting); + } + if (!data_len) + break; + if (! dec_running) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if (first_packet) { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) { + status = -6; + break; + } + } + lt_info_c("Dec::run %d\n", __LINE__); + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // wait for EOS from render + ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0, + ILCLIENT_BUFFER_FLAG_EOS, 10000); + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + lt_info_c("Dec::run %d ends\n", __LINE__); + // return status; +}