/*************************************************************************** * Copyright (c) 2012, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "nexus_platform.h" #include "nexus_video_decoder.h" #include "nexus_video_window.h" #include "nexus_stc_channel.h" #include "nexus_timebase.h" #include "nexus_display.h" #include "nexus_display_vbi.h" #include "nexus_core_utils.h" #include "nexus_video_window.h" #include "nexus_video_decoder_userdata.h" #include "nexus_video_input_vbi.h" #include "nexus_video_decoder_trick.h" #include "bstd.h" #include "bkni.h" #include "ts_psi.h" #include "bavc.h" #include "bsettop.h" #include "bsettop_stream_n_priv.h" #include "bsettop_display_n_priv.h" #include "xds_ctrl.h" BDBG_MODULE(bdecode); #define MAX_TRIPLETS 0x1F struct bdecode { NEXUS_VideoDecoderHandle videoDecoder; /* following two members are from bdisplay_t when opening bdecode_window_open */ NEXUS_DisplayHandle display[eBDISPLAY_ID_MAX]; NEXUS_VideoWindowHandle window[eBDISPLAY_ID_MAX]; bool video_decoding; bstream_info_t *p_stream_info; bstream_t stream; bdecode_config config; bdecode_status status; unsigned int start_ticks; bool seq_pending; bool first_pts_pending; bool b708cc; NEXUS_ClosedCaptionData captionData[MAX_TRIPLETS]; unsigned char triplets[MAX_TRIPLETS*3]; BKNI_EventHandle wdEvent; bool bypass_tsm; /* flag to bypass TSM mode if video is H.264 to show video as long as got the GOP */ xds_ctrl_t xds_ctrl; }; static struct bdecode s_decode; bool g_disable_fcc = false; static void bdecode_first_pts_passed_cb(void *context, int param); static void bdecode_stream_changed_cb(void *context, int param); static void bdecode_userdata_ready(void *context, int param); static void bdecode_first_pts_cb(void *context, int param); extern NEXUS_DisplayHandle bdisplay_p_get_handle(bdisplay_t display, int id); extern NEXUS_VideoWindowHandle bdisplay_p_get_window(bdisplay_t display, int index); extern bresult bdisplay_vbi_clear_cc(void); /* * Summary: * Open a decode engine. * Description: **/ bdecode_t bdecode_open(bobject_t decode_id) { NEXUS_VideoDecoderSettings videoSettings; NEXUS_VideoInputVbiSettings vbiSettings; BSTD_UNUSED(decode_id); BKNI_Memset(&s_decode, 0, sizeof(struct bdecode)); s_decode.videoDecoder = NEXUS_VideoDecoder_Open(0, NULL); NEXUS_VideoDecoder_GetSettings(s_decode.videoDecoder, &videoSettings); videoSettings.channelChangeMode = NEXUS_VideoDecoder_ChannelChangeMode_eHoldUntilFirstPicture; videoSettings.firstPtsPassed.callback = bdecode_first_pts_passed_cb; videoSettings.firstPtsPassed.context = &s_decode; videoSettings.firstPts.callback = bdecode_first_pts_cb; videoSettings.firstPts.context = &s_decode; videoSettings.streamChanged.callback = bdecode_stream_changed_cb; videoSettings.streamChanged.context = &s_decode; NEXUS_VideoDecoder_SetSettings(s_decode.videoDecoder, &videoSettings); NEXUS_VideoInput_GetVbiSettings(NEXUS_VideoDecoder_GetConnector(s_decode.videoDecoder), &vbiSettings); vbiSettings.closedCaptionEnabled = true; vbiSettings.closedCaptionBufferSize = 200; vbiSettings.closedCaptionDataReady.callback = bdecode_userdata_ready; vbiSettings.closedCaptionDataReady.context = &s_decode; NEXUS_VideoInput_SetVbiSettings(NEXUS_VideoDecoder_GetConnector(s_decode.videoDecoder), &vbiSettings); bstream_init(s_decode.videoDecoder); xds_ctrl_init(&s_decode.xds_ctrl); return (&s_decode); } /* * Summary: * Close a decode engine. * Description: * The decode should be stopped before closing. After closing, the bdecode_t handle is invalid */ void bdecode_close( bdecode_t decode /* handle returned by bdecode_open */ ) { int i; BDBG_ASSERT(decode); if (decode->videoDecoder) NEXUS_VideoDecoder_Close(decode->videoDecoder); for (i=0; iwindow[i]); } return; } /* * Summary: * Start decoding a stream. **/ bresult bdecode_start( bdecode_t decode, /* handle returned by bdecode_open */ bstream_t p_stream, /* source for the decode, either analog or digital */ bdecode_window_t window /* window to render decode <= not used */ ) { bstream_status status; NEXUS_TimebaseSettings timebaseSettings; NEXUS_VideoDecoderSettings vsettings; BSTD_UNUSED(window); BDBG_ASSERT(p_stream); #ifdef CONFIG_EIA_708 decode->b708cc = false; #endif decode->stream = p_stream; bstream_get_status(p_stream, &status); if (status.mpeg.video[0].pid == 0) { BDBG_WRN(("%s (0x%04x)", __FUNCTION__, status.mpeg.video[0].pid)); return eBAPP_RESULT_FAILURE; } if (decode->video_decoding) { BDBG_WRN(("%s (0x%04x) video is already decoding", __FUNCTION__, status.mpeg.video[0].pid)); return eBAPP_RESULT_FAILURE; } decode->p_stream_info = bstream_get_info(p_stream); if (!decode->p_stream_info) { BDBG_WRN(("%s invalid nexus handles", __FUNCTION__)); return eBAPP_RESULT_FAILURE; } BDBG_ASSERT(decode->p_stream_info->videoPidChannel); BDBG_ASSERT(decode->p_stream_info->stcChannel); NEXUS_VideoDecoder_GetDefaultStartSettings(&(decode->p_stream_info->decodeSettings)); decode->p_stream_info->decodeSettings.codec = status.mpeg.video[0].format; decode->p_stream_info->decodeSettings.pidChannel = decode->p_stream_info->videoPidChannel; decode->p_stream_info->decodeSettings.stcChannel = decode->p_stream_info->stcChannel; decode->p_stream_info->decodeSettings.prerollRate = 1; NEXUS_Timebase_GetSettings(NEXUS_Timebase_e0, &timebaseSettings); timebaseSettings.sourceType = NEXUS_TimebaseSourceType_ePcr; timebaseSettings.sourceSettings.pcr.pidChannel = decode->p_stream_info->pcrPidChannel; timebaseSettings.sourceSettings.pcr.maxPcrError = 0xFF; timebaseSettings.sourceSettings.pcr.trackRange = NEXUS_TimebaseTrackRange_e61ppm; NEXUS_Timebase_SetSettings(NEXUS_Timebase_e0, &timebaseSettings); NEXUS_VideoDecoder_GetSettings(decode->videoDecoder, &vsettings); vsettings.channelChangeMode = NEXUS_VideoDecoder_ChannelChangeMode_eHoldUntilTsmLock; NEXUS_VideoDecoder_SetSettings(decode->videoDecoder, &vsettings); if (status.mpeg.video[0].format == NEXUS_VideoCodec_eH264) { NEXUS_VideoDecoderTrickState trick; NEXUS_VideoDecoder_GetTrickState(decode->videoDecoder, &trick); trick.tsmEnabled = NEXUS_TsmMode_eDisabled; NEXUS_VideoDecoder_SetTrickState(decode->videoDecoder, &trick); decode->bypass_tsm = true; } decode->config.channel_change = 1; NEXUS_VideoDecoder_StartDecodeWithPrimer(decode->videoDecoder, decode->p_stream_info->primerHandle); decode->video_decoding = true; decode->status.bVideoDecoding = true; decode->first_pts_pending = true; decode->start_ticks = bos_getticks(); NEXUS_VideoInput_FlushClosedCaption(NEXUS_VideoDecoder_GetConnector(decode->videoDecoder)); decode->b708cc = false; return b_ok; } /* * Summary: * Stop decoding a stream. * Description: * The stream remains valid after decode is stopped. Decode could be restarted * without retuning or restarting playback. **/ void bdecode_stop( bdecode_t decode /* handle returned by bdecode_open */ ) { if (!decode->video_decoding) return; BDBG_ASSERT(decode->p_stream_info); BDBG_ASSERT(decode->p_stream_info->primerHandle); NEXUS_VideoDecoder_Stop(decode->videoDecoder); NEXUS_VideoDecoder_StartPrimer(decode->videoDecoder, decode->p_stream_info->primerHandle, &(decode->p_stream_info->decodeSettings)); decode->video_decoding = false; } /* * Summary: * Get the status of the decoder and current source. */ bresult bdecode_get_status( bdecode_t decode, /* handle returned by bdecode_open */ bdecode_status *status /* [out] status to be populated */ ) { NEXUS_VideoDecoderStatus nstatus; NEXUS_VideoDecoderStreamInformation streamInfo; bstream_status stream_status; BDBG_ASSERT(decode); BDBG_ASSERT(status); NEXUS_VideoDecoder_GetStatus(decode->videoDecoder, &nstatus); NEXUS_VideoDecoder_GetStreamInformation(decode->videoDecoder, &streamInfo); if (decode->stream) bstream_get_status(decode->stream, &stream_status); else BKNI_Memset(&stream_status, 0, sizeof(bstream_status)); status->source_width = nstatus.source.width; status->source_height = nstatus.source.height; status->display_width = nstatus.display.width; status->display_height = nstatus.display.height; status->video_fifo_depth = nstatus.fifoDepth; status->video_fifo_size = nstatus.fifoSize; status->video_pts = nstatus.pts; status->video_stc = nstatus.pts + nstatus.ptsStcDifference; status->video_pts_error = 0; /*TODO: see VideoDecoder_P_GetStatus_Avd. ptsInfo.uiPCROffset. */ status->video_aspect_ratio = streamInfo.aspectRatio; /* need to be covnerted */ status->video_format = nstatus.format; /* need to be converted */ status->video_framerate = nstatus.frameRate; /* need to be converted */ status->first_pts_ticks = decode->status.first_pts_ticks; status->first_pts_cnt = decode->status.first_pts_cnt; status->seq_ticks = decode->status.seq_ticks; status->hPanScan = streamInfo.horizontalPanScan; status->vPanScan = streamInfo.verticalPanScan; status->watchdog_cnt = nstatus.numWatchdogs; status->pts_err_cnt = nstatus.ptsErrorCount; status->req_stc_cnt = decode->status.req_stc_cnt; status->offset_cnt = 0; /* TODO:: ePtsStcOffset, NEXUS_VideoDecoder_P_PtsStcOffset_isr */ status->pic_info_cnt = 0; /* TODO::ePictureParameters, NEXUS_VideoDecoder_P_PictureParams_isr */ status->TSM_Mode = !nstatus.tsm; status->decode_err_cnt = nstatus.numDecodeErrors; status->decoder_drop_cnt = nstatus.numDecodeDrops; status->display_drop_cnt = nstatus.numDisplayDrops; status->pic_received = nstatus.numPicturesReceived; status->underflow_cnt = nstatus.numDisplayUnderflows; status->bStreamProgressive = !(nstatus.interlaced); #if 0 status->bitRate = status.bPCR_Lock = #endif status->bVideoDecoding = decode->status.bVideoDecoding; status->profile = nstatus.protocolProfile; status->level = nstatus.protocolLevel; status->bFrameProgressive = !nstatus.interlaced; status->xvdch_status = nstatus.avdStatusBlock; status->vPID = stream_status.mpeg.video[0].pid; status->pcrPID = stream_status.mpeg.pcr_pid; return b_ok; } bresult bdecode_window_get(bdecode_window_t window, bdecode_window_settings *settings) { NEXUS_VideoWindowSettings windowSettings; int id = (int)window; NEXUS_VideoWindowHandle handle = s_decode.window[id]; NEXUS_VideoWindow_GetSettings(handle, &windowSettings); settings->position.x = windowSettings.position.x; settings->position.y = windowSettings.position.y; settings->position.width = windowSettings.position.width; settings->position.height = windowSettings.position.height; #if HAS_HDMI if (id == eBDISPLAY_HDMI) { NEXUS_VideoFormatInfo vinfo; NEXUS_DisplaySettings dispSettings; NEXUS_Display_GetSettings(s_decode.display[id], &dispSettings); NEXUS_VideoFormat_GetInfo(dispSettings.format, &vinfo); settings->def_position.x = settings->def_position.y = 0; settings->def_position.width = vinfo.digitalWidth; settings->def_position.height = vinfo.digitalHeight; } else #endif { settings->def_position.x = windowSettings.clipBase.x; settings->def_position.y = windowSettings.clipBase.y; settings->def_position.width = windowSettings.clipBase.width; settings->def_position.height = windowSettings.clipBase.height; } return b_ok; } bresult bdecode_window_set(bdecode_window_t window, bdecode_window_settings *settings) { NEXUS_VideoWindowSettings windowSettings; NEXUS_VideoWindowHandle handle = s_decode.window[(int)window]; BDBG_ASSERT(settings); NEXUS_VideoWindow_GetSettings(handle, &windowSettings); windowSettings.position.x = settings->position.x; windowSettings.position.y = settings->position.y; windowSettings.position.width = settings->position.width; windowSettings.position.height = settings->position.height; NEXUS_VideoWindow_SetSettings(handle, &windowSettings); NEXUS_DisplayModule_SetUpdateMode(NEXUS_DisplayUpdateMode_eAuto); return b_ok; } bresult bdecode_window_preset(bdecode_window_t window, bdecode_window_settings *settings) { NEXUS_VideoWindowSettings windowSettings; NEXUS_VideoWindowHandle handle = s_decode.window[(int)window]; BDBG_ASSERT(settings); NEXUS_DisplayModule_SetUpdateMode(NEXUS_DisplayUpdateMode_eManual); NEXUS_VideoWindow_GetSettings(handle, &windowSettings); windowSettings.position.x = settings->position.x; windowSettings.position.y = settings->position.y; windowSettings.position.width = settings->position.width; windowSettings.position.height = settings->position.height; NEXUS_VideoWindow_SetSettings(handle, &windowSettings); return b_ok; } #define B_MAX_VBI_CC_COUNT 32 /* required by VBI lib */ static void bdecode_userdata_ready(void *context, int param) { bdecode_t decode = (bdecode_t)context; NEXUS_VideoDecoderHandle videoDecoder = decode->videoDecoder; NEXUS_Error rc; uint32_t numEntries, numValidEntries, i, size; uint8_t *buffer; BSTD_UNUSED(param); NEXUS_VideoInput_ReadClosedCaption(NEXUS_VideoDecoder_GetConnector(videoDecoder), decode->captionData, MAX_TRIPLETS, &numEntries); if (!decode->config.cc_enabled) return; for (i=0, numValidEntries = 0; icaptionData[i].field > 3) || decode->captionData[i].noData) continue; if ((decode->captionData[i].field & 0x2)==0x2) { /* 708 CC */ /* primary CC is 708 */ if ((i!=(numEntries-1)) && ((decode->captionData[i+1].field & 0x2) == 0x2) && (decode->b708cc == false)) { BDBG_WRN(("Switch to 708CC mode")); numValidEntries = 0; decode->b708cc = true; } } else if (decode->b708cc) { continue; } if (!decode->captionData[i].noData) { decode->triplets[numValidEntries] = decode->captionData[i].field; decode->triplets[numValidEntries+1] = decode->captionData[i].data[0]; decode->triplets[numValidEntries+2] = decode->captionData[i].data[1]; numValidEntries+=3; } } if (decode->config.cc_callback && decode->config.cc_enabled && numValidEntries>0) { decode->config.cc_callback((unsigned char *)decode->triplets, numValidEntries, decode->b708cc?false:true); } } /* * Summary: * Get the config for the decoder. */ void bdecode_get_config( bdecode_t decode, /* handle returned by bdecode_open */ bdecode_config *cfg /* [out] configuration structure to be populated */ ) { unsigned int flags; flags = bos_enter_critical(); *cfg = decode->config; bos_exit_critical(flags); } /* * Summary: * Set the config for the decoder. */ void bdecode_set_config( bdecode_t decode, /* handle returned by bdecode_open */ bdecode_config *cfg /*configuration */ ) { NEXUS_VideoDecoderSettings settings; NEXUS_VideoDecoder_GetSettings(decode->videoDecoder, &settings); if (cfg->mute != settings.mute) { settings.mute = cfg->mute; } if ((cfg->channel_change != decode->config.channel_change) && !(cfg->channel_change&decode->config.channel_change)) { switch (cfg->channel_change) { case 0: settings.channelChangeMode = NEXUS_VideoDecoder_ChannelChangeMode_eMute; break; case 1: settings.channelChangeMode = NEXUS_VideoDecoder_ChannelChangeMode_eHoldUntilTsmLock; break; case 2: settings.channelChangeMode = NEXUS_VideoDecoder_ChannelChangeMode_eMuteUntilFirstPicture; break; default: break; } } NEXUS_VideoDecoder_SetSettings(decode->videoDecoder, &settings); xds_ctrl_set_cb(&decode->xds_ctrl, (xds_callback_t)cfg->xds_callback); if (!decode->config.block_vbi && cfg->block_vbi) { #ifdef CONFIG_EIA_708 bdisplay_vbi_clear_cc(); #endif } decode->config = *cfg; return; } bdecode_window_t bdecode_window_open(int id, bdisplay_t display) { NEXUS_DisplayVbiSettings displayVbiSettings; s_decode.display[id] = bdisplay_p_get_handle(display, id); s_decode.window[id] = bdisplay_p_get_window(display, id); /* always enable CC routing on SD path */ if (id == eBDISPLAY_COMPOSITE) { NEXUS_Display_GetVbiSettings(s_decode.display[id], &displayVbiSettings); displayVbiSettings.vbiSource = NEXUS_VideoDecoder_GetConnector(s_decode.videoDecoder); displayVbiSettings.closedCaptionRouting = true; displayVbiSettings.closedCaptionEnabled = true; NEXUS_Display_SetVbiSettings(s_decode.display[id], &displayVbiSettings); } NEXUS_VideoWindow_AddInput(s_decode.window[id], NEXUS_VideoDecoder_GetConnector(s_decode.videoDecoder)); return (bdecode_window_t)id; } static void bdecode_first_pts_passed_cb(void *context, int param) { bdecode_t decode = (bdecode_t)context; BSTD_UNUSED(param); decode->status.first_pts_cnt++; if (decode->first_pts_pending) { decode->status.first_pts_ticks = bos_getticks()-decode->start_ticks; if (decode->config.first_pts_callback) decode->config.first_pts_callback(); } } static void bdecode_stream_changed_cb(void *context, int param) { bdecode_t decode = (bdecode_t)context; BSTD_UNUSED(param); if (decode->seq_pending) { decode->seq_pending = false; decode->status.seq_ticks = bos_getticks()-decode->start_ticks; if (decode->config.seq_hdr_callback) decode->config.seq_hdr_callback(); } if (decode->bypass_tsm) { NEXUS_VideoDecoderTrickState trick; NEXUS_VideoDecoder_GetTrickState(decode->videoDecoder, &trick); trick.tsmEnabled = NEXUS_TsmMode_eEnabled; NEXUS_VideoDecoder_SetTrickState(decode->videoDecoder, &trick); decode->bypass_tsm = false; } } static void bdecode_first_pts_cb(void *context, int param) { bdecode_t decode = (bdecode_t)context; BSTD_UNUSED(param); decode->status.req_stc_cnt++; } void bsettop_pvt(void) { } const bsettop_av_stream_type_t s_video_stream_types[] = { { TS_PSI_ST_13818_2_Video, BAVC_VideoCompressionStd_eMPEG2, 0,"MPEG-2"}, { TS_PSI_ST_ATSC_Video, BAVC_VideoCompressionStd_eMPEG2, 0,"MPEG-2"}, { TS_PSI_ST_AVS_Video, BAVC_VideoCompressionStd_eAVS, 0,"AVS"}, { TS_PSI_ST_14496_10_Video, BAVC_VideoCompressionStd_eH264, 0,"H.264"}, { TS_PSI_ST_SMPTE_VC1, BAVC_VideoCompressionStd_eVC1, 0,"VC1"}, { TS_PSI_ST_14496_2_Video, BAVC_VideoCompressionStd_eMPEG4Part2, 0,"MP4p2"} }; const int s_video_stream_types_num = sizeof(s_video_stream_types)/sizeof(s_video_stream_types[0]); /** * Summary: * Supported video format. Returns NULL if stream_type (from PMT) not supported. * **/ bsettop_av_stream_type_t *bdecode_supported_video(unsigned char stream_type) { int i; for (i = 0; i < s_video_stream_types_num; ++i) { if (stream_type == s_video_stream_types[i].format) { BDBG_MSG(("Video format[0x%02x]: %s\n",s_video_stream_types[i].format,s_video_stream_types[i].format_name)); return(bsettop_av_stream_type_t*)&s_video_stream_types[i]; } } BDBG_MSG(("Unsupported Video format[0x%02x]\n",stream_type)); return NULL; }