From 79db7c3476e4aa446c1a5c4d84eb275546d7ae91 Mon Sep 17 00:00:00 2001 From: Udaya Bhaskara Reddy Mallavarapu Date: Tue, 17 Jan 2017 10:20:00 +0530 Subject: media: platform: msm: Add demux support for mediabox Add audio stream buffer interface to write the Audio demux data to audio decoder buffers. Add support in TSIF driver to append LPASS subsystem time stamps to Transport Stream packets in TSIF HW. This is required for AV sync & clock recovery handling for mediabox project based on LE platform. CR's-Fixed: 2005724 Change-Id: I39d2544e958c31ff1abdd2ad77412a87bc2a189b Signed-off-by: Udaya Bhaskara Reddy Mallavarapu Signed-off-by: Arun Menon --- drivers/media/dvb-core/dvb_demux.c | 91 +- drivers/media/dvb-core/dvb_demux.h | 25 + drivers/media/platform/msm/broadcast/tspp.c | 166 +- drivers/media/platform/msm/dvb/adapter/Makefile | 1 + drivers/media/platform/msm/dvb/demux/Kconfig | 8 + .../platform/msm/dvb/demux/mpq_dmx_plugin_common.c | 2130 +++++++++++++++++--- .../platform/msm/dvb/demux/mpq_dmx_plugin_common.h | 108 +- .../msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c | 28 +- .../media/platform/msm/dvb/include/mpq_adapter.h | 29 +- .../platform/msm/dvb/include/mpq_stream_buffer.h | 36 +- include/linux/qcom_tspp.h | 11 +- include/uapi/linux/dvb/dmx.h | 3 + 12 files changed, 2297 insertions(+), 339 deletions(-) diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c index 60f7cfbd9f9b..65eaf4066149 100644 --- a/drivers/media/dvb-core/dvb_demux.c +++ b/drivers/media/dvb-core/dvb_demux.c @@ -131,6 +131,39 @@ static const struct dvb_dmx_video_patterns h264_non_idr = { DMX_IDX_H264_NON_IDR_START }; +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * I-Slice NAL idc = 3, NAL type = 5, 01100101 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_idr_islice = { + {0x00, 0x00, 0x01, 0x65, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_IDR_ISLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * P-Slice NAL idc = 2, NAL type = 1, 01000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_pslice = { + {0x00, 0x00, 0x01, 0x41, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_PSLICE_START +}; + +/* + * Forbidden (1 bit) + NAL idc (2 bits) + NAL type (5 bits) + * B-Slice NAL idc = 0, NAL type = 1, 00000001 mask 0x7F + */ +static const struct dvb_dmx_video_patterns h264_non_idr_bslice = { + {0x00, 0x00, 0x01, 0x01, 0x80}, + {0xFF, 0xFF, 0xFF, 0x7F, 0x80}, + 5, + DMX_IDX_H264_NON_IDR_BSLICE_START +}; + static const struct dvb_dmx_video_patterns h264_non_access_unit_del = { {0x00, 0x00, 0x01, 0x09}, {0xFF, 0xFF, 0xFF, 0x1F}, @@ -1011,6 +1044,18 @@ static void dvb_dmx_process_pattern_result(struct dvb_demux_feed *feed, } else if (feed->prev_frame_type & DMX_IDX_H264_NON_IDR_START) { idx_event.type = DMX_IDX_H264_NON_IDR_END; frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_IDR_ISLICE_START) { + idx_event.type = DMX_IDX_H264_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_PSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; + } else if (feed->prev_frame_type & + DMX_IDX_H264_NON_IDR_BSLICE_START) { + idx_event.type = DMX_IDX_H264_NON_IDR_END; + frame_end_in_seq = DMX_IDX_H264_FIRST_SPS_FRAME_END; } else { idx_event.type = DMX_IDX_VC1_FRAME_END; frame_end_in_seq = DMX_IDX_VC1_FIRST_SEQ_FRAME_END; @@ -1848,6 +1893,15 @@ const struct dvb_dmx_video_patterns *dvb_dmx_get_pattern(u64 dmx_idx_pattern) case DMX_IDX_H264_NON_IDR_START: return &h264_non_idr; + case DMX_IDX_H264_IDR_ISLICE_START: + return &h264_idr_islice; + + case DMX_IDX_H264_NON_IDR_PSLICE_START: + return &h264_non_idr_pslice; + + case DMX_IDX_H264_NON_IDR_BSLICE_START: + return &h264_non_idr_bslice; + case DMX_IDX_H264_ACCESS_UNIT_DEL: return &h264_non_access_unit_del; @@ -1975,6 +2029,40 @@ static void dvb_dmx_init_idx_state(struct dvb_demux_feed *feed) feed->pattern_num++; } + /* H264 IDR ISlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_IDR_ISLICE_START | DMX_IDX_H264_IDR_END | + DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_IDR_ISLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR PSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_PSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_PSLICE_START); + feed->pattern_num++; + } + /* H264 non-IDR BSlice */ + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && + (feed->idx_params.types & + (DMX_IDX_H264_NON_IDR_BSLICE_START | DMX_IDX_H264_NON_IDR_END | + DMX_IDX_H264_IDR_END | + DMX_IDX_H264_FIRST_SPS_FRAME_START | + DMX_IDX_H264_FIRST_SPS_FRAME_END))) { + feed->patterns[feed->pattern_num] = + dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_BSLICE_START); + feed->pattern_num++; + } + if ((feed->pattern_num < DVB_DMX_MAX_SEARCH_PATTERN_NUM) && (feed->idx_params.types & DMX_IDX_H264_ACCESS_UNIT_DEL)) { feed->patterns[feed->pattern_num] = @@ -3253,7 +3341,8 @@ static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) { struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); + /* 4 Demux Instances each with group of 5 pids */ + memcpy(pids, dvbdemux->pids, DMX_PES_OTHER*sizeof(u16)); return 0; } diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h index 779de7ed078a..10a4cbbba2e7 100644 --- a/drivers/media/dvb-core/dvb_demux.h +++ b/drivers/media/dvb-core/dvb_demux.h @@ -359,6 +359,31 @@ static inline int dvb_dmx_is_video_feed(struct dvb_demux_feed *feed) return 0; } +/** + * dvb_dmx_is_audio_feed - Returns whether the PES feed + * is audio one. + * + * @feed: The feed to be checked. + * + * Return 1 if feed is audio feed, 0 otherwise. + */ +static inline int dvb_dmx_is_audio_feed(struct dvb_demux_feed *feed) +{ + if (feed->type != DMX_TYPE_TS) + return 0; + + if (feed->ts_type & (~TS_DECODER)) + return 0; + + if ((feed->pes_type == DMX_PES_AUDIO0) || + (feed->pes_type == DMX_PES_AUDIO1) || + (feed->pes_type == DMX_PES_AUDIO2) || + (feed->pes_type == DMX_PES_AUDIO3)) + return 1; + + return 0; +} + /** * dvb_dmx_is_pcr_feed - Returns whether the PES feed * is PCR one. diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c index 275b8b90af05..b706d598e6b8 100644 --- a/drivers/media/platform/msm/broadcast/tspp.c +++ b/drivers/media/platform/msm/broadcast/tspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -45,6 +45,8 @@ #include #include #include /* tasklet */ +#include /* Timer */ +#include /* Timer */ /* * General defines @@ -100,6 +102,7 @@ #define TSIF_TEST_RESET_OFF (0x1c) #define TSIF_TEST_EXPORT_OFF (0x20) #define TSIF_TEST_CURRENT_OFF (0x24) +#define TSIF_TTS_CTL_OFF (0x38) #define TSIF_DATA_PORT_OFF (0x100) @@ -128,6 +131,12 @@ #define TSIF_STS_CTL_STOP BIT(3) #define TSIF_STS_CTL_START BIT(0) +/* bits for TSIF_TTS_CTRL register */ +#define TSIF_TTS_CTL_TTS_ENDIANNESS BIT(4) +#define TSIF_TTS_CTL_TTS_SOURCE BIT(3) +#define TSIF_TTS_CTL_TTS_LENGTH_1 BIT(1) +#define TSIF_TTS_CTL_TTS_LENGTH_0 BIT(0) + /* * TSPP register offsets */ @@ -255,6 +264,7 @@ static const struct debugfs_entry debugfs_tsif_regs[] = { {"test_export", S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF}, {"test_current", S_IRUGO, TSIF_TEST_CURRENT_OFF}, {"data_port", S_IRUSR, TSIF_DATA_PORT_OFF}, + {"tts_source", S_IRUSR | S_IWUSR, TSIF_TTS_CTL_OFF}, }; static const struct debugfs_entry debugfs_tspp_regs[] = { @@ -369,6 +379,8 @@ struct tspp_tsif_device { u32 stat_overflow; u32 stat_lost_sync; u32 stat_timeout; + enum tsif_tts_source tts_source; + u32 lpass_timer_enable; }; enum tspp_buf_state { @@ -477,6 +489,7 @@ struct tspp_device { /* pinctrl */ struct mutex mutex; struct tspp_pinctrl pinctrl; + unsigned int tts_source; /* Time stamp source type LPASS timer/TCR */ struct dentry *dent; struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)]; @@ -911,6 +924,8 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) { int start_hardware = 0; u32 ctl; + u32 tts_ctl; + int retval; if (tsif_device->ref_count == 0) { start_hardware = 1; @@ -949,19 +964,57 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) TSIF_STS_CTL_TEST_MODE; break; case TSPP_TSIF_MODE_1: - ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR; + ctl |= TSIF_STS_CTL_EN_TIME_LIM; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; case TSPP_TSIF_MODE_2: ctl |= TSIF_STS_CTL_EN_TIME_LIM | - TSIF_STS_CTL_EN_TCR | TSIF_STS_CTL_MODE_2; + if (tsif_device->tts_source != TSIF_TTS_LPASS_TIMER) + ctl |= TSIF_STS_CTL_EN_TCR; break; default: pr_warn("tspp: unknown tsif mode 0x%x", tsif_device->mode); } + /* Set 4bytes Time Stamp for TCR */ + if (tsif_device->tts_source == TSIF_TTS_LPASS_TIMER) { + if (tsif_device->lpass_timer_enable == 0) { + retval = avcs_core_open(); + if (retval < 0) { + pr_warn("tspp: avcs open fail:%d\n", + retval); + return retval; + } + retval = avcs_core_disable_power_collapse(1); + if (retval < 0) { + pr_warn("tspp: avcs power enable:%d\n", + retval); + return retval; + } + tsif_device->lpass_timer_enable = 1; + } + + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + tts_ctl = 0; + /* Set LPASS Timer TTS source */ + tts_ctl |= TSIF_TTS_CTL_TTS_SOURCE; + /* Set 4 byte TTS */ + tts_ctl |= TSIF_TTS_CTL_TTS_LENGTH_0; + + writel_relaxed(tts_ctl, tsif_device->base + + TSIF_TTS_CTL_OFF); + /* write TTS control register */ + wmb(); + tts_ctl = readl_relaxed(tsif_device->base + + TSIF_TTS_CTL_OFF); + } + writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF); + /* write Status control register */ + wmb(); writel_relaxed(tsif_device->time_limit, tsif_device->base + TSIF_TIME_LIMIT_OFF); /* assure register configuration is done before starting TSIF */ @@ -982,8 +1035,13 @@ static int tspp_start_tsif(struct tspp_tsif_device *tsif_device) static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device) { - if (tsif_device->ref_count == 0) + if (tsif_device->ref_count == 0) { + if (tsif_device->lpass_timer_enable == 1) { + if (avcs_core_disable_power_collapse(0) == 0) + tsif_device->lpass_timer_enable = 0; + } return; + } tsif_device->ref_count--; @@ -1113,6 +1171,7 @@ static int tspp_global_reset(struct tspp_device *pdev) pdev->tsif[i].data_inverse = 0; pdev->tsif[i].sync_inverse = 0; pdev->tsif[i].enable_inverse = 0; + pdev->tsif[i].lpass_timer_enable = 0; } writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST); /* assure state is reset before continuing with configuration */ @@ -1884,6 +1943,89 @@ int tspp_get_ref_clk_counter(u32 dev, enum tspp_source source, u32 *tcr_counter) } EXPORT_SYMBOL(tspp_get_ref_clk_counter); +/** + * tspp_get_lpass_time_counter - return the LPASS Timer counter value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @source: The TSIF source from which the counter should be read + * @tcr_counter: the value of TCR counter + * + * Return error status + * + * If source is neither TSIF 0 or TSIF1 0 is returned. + */ +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter) +{ + struct tspp_device *pdev; + struct tspp_tsif_device *tsif_device; + + if (!lpass_time_counter) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_lpass_time_counter: can't find device %i\n", + dev); + return -ENODEV; + } + + switch (source) { + case TSPP_SOURCE_TSIF0: + tsif_device = &pdev->tsif[0]; + break; + + case TSPP_SOURCE_TSIF1: + tsif_device = &pdev->tsif[1]; + break; + + default: + tsif_device = NULL; + break; + } + + if (tsif_device && tsif_device->ref_count) { + if (avcs_core_query_timer(lpass_time_counter) < 0) { + pr_err("tspp_get_lpass_time_counter: read error\n"); + *lpass_time_counter = 0; + return -ENETRESET; + } + } else + *lpass_time_counter = 0; + + return 0; +} +EXPORT_SYMBOL(tspp_get_lpass_time_counter); + +/** + * tspp_get_tts_source - Return the TTS source value. + * + * @dev: TSPP device (up to TSPP_MAX_DEVICES) + * @tts_source:Updated TTS source type + * + * Return error status + * + */ +int tspp_get_tts_source(u32 dev, int *tts_source) +{ + struct tspp_device *pdev; + + if (tts_source == NULL) + return -EINVAL; + + pdev = tspp_find_by_id(dev); + if (!pdev) { + pr_err("tspp_get_tts_source: can't find device %i\n", + dev); + return -ENODEV; + } + + *tts_source = pdev->tts_source; + + return 0; +} +EXPORT_SYMBOL(tspp_get_tts_source); + /** * tspp_add_filter - add a TSPP filter to a channel. * @@ -2891,6 +3033,20 @@ static int msm_tspp_probe(struct platform_device *pdev) goto err_irq; device->req_irqs = false; + /* Check whether AV timer time stamps are enabled */ + if (!of_property_read_u32(pdev->dev.of_node, "qcom,lpass-timer-tts", + &device->tts_source)) { + if (device->tts_source == 1) + device->tts_source = TSIF_TTS_LPASS_TIMER; + else + device->tts_source = TSIF_TTS_TCR; + } else { + device->tts_source = TSIF_TTS_TCR; + } + + for (i = 0; i < TSPP_TSIF_INSTANCES; i++) + device->tsif[i].tts_source = device->tts_source; + /* power management */ pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/msm/dvb/adapter/Makefile b/drivers/media/platform/msm/dvb/adapter/Makefile index f7da6b5b2f06..662bf99c4d7e 100644 --- a/drivers/media/platform/msm/dvb/adapter/Makefile +++ b/drivers/media/platform/msm/dvb/adapter/Makefile @@ -1,5 +1,6 @@ ccflags-y += -Idrivers/media/dvb-core/ ccflags-y += -Idrivers/media/platform/msm/dvb/include/ +ccflags-y += -Idrivers/media/platform/msm/dvb/demux/ obj-$(CONFIG_DVB_MPQ) += mpq-adapter.o diff --git a/drivers/media/platform/msm/dvb/demux/Kconfig b/drivers/media/platform/msm/dvb/demux/Kconfig index 319e2ab2eb96..d160c820d190 100644 --- a/drivers/media/platform/msm/dvb/demux/Kconfig +++ b/drivers/media/platform/msm/dvb/demux/Kconfig @@ -44,3 +44,11 @@ choice TSPP hardware support. All demux tasks will be performed in SW. endchoice + +config DVB_MPQ_MEDIA_BOX_DEMUX + bool "Media box demux support" + depends on DVB_MPQ_DEMUX + + help + Use this option if your HW is Qualcomm media box and demux + support is required on that media box. diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c index 0188637af85c..ef0ef1512211 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -64,6 +64,11 @@ static int video_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; module_param(video_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(video_nonsecure_ion_heap, "ION heap for non-secure video buffer allocation"); +/* ION heap IDs used for allocating audio output buffer */ +static int audio_nonsecure_ion_heap = ION_IOMMU_HEAP_ID; +module_param(audio_nonsecure_ion_heap, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(audio_nonsecure_ion_heap, "ION heap for non-secure audio buffer allocation"); + /* Value of TS packet scramble bits field for even key */ static int mpq_sdmx_scramble_even = 0x2; module_param(mpq_sdmx_scramble_even, int, S_IRUGO | S_IWUSR); @@ -104,6 +109,25 @@ module_param(tsif_mode, int, S_IRUGO | S_IWUSR); static int clock_inv; module_param(clock_inv, int, S_IRUGO | S_IWUSR); +/* TSIF Timestamp source: 0 = TSIF Clock Reference, 1 = LPASS time counter */ +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + +/* Store all mpq feeds corresponding to 4 TS programs in a Transport Stream */ +static struct mpq_feed *store_mpq_audio_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static struct mpq_feed *store_mpq_video_feed[CONFIG_DVB_MPQ_NUM_DMX_DEVICES] = { + NULL, NULL, NULL, NULL}; +static int non_predicted_video_frame; +/* trigger video ES frame events on MPEG2 B frames and H264 non-IDR frames */ +#ifdef CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX +static int video_b_frame_events = 1; +#else +static int video_b_frame_events; +#endif + /* Global data-structure for managing demux devices */ static struct { @@ -147,6 +171,96 @@ int mpq_dmx_get_param_clock_inv(void) return clock_inv; } +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_video_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_video) { + case DMX_PES_VIDEO0: + if (store_mpq_video_feed[0] != NULL) { + feed_data = &store_mpq_video_feed[0]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO0_STREAM_IF; + } + break; + case DMX_PES_VIDEO1: + if (store_mpq_video_feed[1] != NULL) { + feed_data = &store_mpq_video_feed[1]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO1_STREAM_IF; + } + break; + case DMX_PES_VIDEO2: + if (store_mpq_video_feed[2] != NULL) { + feed_data = &store_mpq_video_feed[2]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO2_STREAM_IF; + } + break; + case DMX_PES_VIDEO3: + if (store_mpq_video_feed[3] != NULL) { + feed_data = &store_mpq_video_feed[3]->video_info; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_video_streambuffer); + +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio) +{ + struct mpq_streambuffer *streambuffer = NULL; + struct mpq_audio_feed_info *feed_data = NULL; + + switch (dmx_ts_pes_audio) { + case DMX_PES_AUDIO0: + if (store_mpq_audio_feed[0] != NULL) { + feed_data = &store_mpq_audio_feed[0]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + } + break; + case DMX_PES_AUDIO1: + if (store_mpq_audio_feed[1] != NULL) { + feed_data = &store_mpq_audio_feed[1]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + } + break; + case DMX_PES_AUDIO2: + if (store_mpq_audio_feed[2] != NULL) { + feed_data = &store_mpq_audio_feed[2]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + } + break; + case DMX_PES_AUDIO3: + if (store_mpq_audio_feed[3] != NULL) { + feed_data = &store_mpq_audio_feed[3]->audio_info; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + } + break; + } + + if (feed_data != NULL) + mpq_adapter_get_stream_if(feed_data->stream_interface, + &streambuffer); + + return streambuffer; +} +EXPORT_SYMBOL(consumer_audio_streambuffer); + + + /* Check that PES header is valid and that it is a video PES */ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) { @@ -163,6 +277,25 @@ static int mpq_dmx_is_valid_video_pes(struct pes_packet_header *pes_header) return 0; } +static int mpq_dmx_is_valid_audio_pes(struct pes_packet_header *pes_header) +{ + /* start-code valid? */ + if ((pes_header->packet_start_code_prefix_1 != 0) || + (pes_header->packet_start_code_prefix_2 != 0) || + (pes_header->packet_start_code_prefix_3 != 1)) + return -EINVAL; + + /* Note: AC3 stream ID = 0xBD */ + if (pes_header->stream_id == 0xBD) + return 0; + + /* stream_id is audio? */ /* 110x xxxx = Audio Stream IDs */ + if ((pes_header->stream_id & 0xE0) != 0xC0) + return -EINVAL; + + return 0; +} + /* Check if a framing pattern is a video frame pattern or a header pattern */ static inline int mpq_dmx_is_video_frame( enum dmx_video_codec codec, @@ -170,6 +303,10 @@ static inline int mpq_dmx_is_video_frame( { switch (codec) { case DMX_VIDEO_CODEC_MPEG2: + if (video_b_frame_events == 1) + if (pattern_type == DMX_IDX_MPEG_B_FRAME_START) + non_predicted_video_frame = 1; + if ((pattern_type == DMX_IDX_MPEG_I_FRAME_START) || (pattern_type == DMX_IDX_MPEG_P_FRAME_START) || (pattern_type == DMX_IDX_MPEG_B_FRAME_START)) @@ -177,9 +314,20 @@ static inline int mpq_dmx_is_video_frame( return 0; case DMX_VIDEO_CODEC_H264: - if ((pattern_type == DMX_IDX_H264_IDR_START) || - (pattern_type == DMX_IDX_H264_NON_IDR_START)) - return 1; + if (video_b_frame_events == 1) { + if (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START) + non_predicted_video_frame = 1; + + if ((pattern_type == DMX_IDX_H264_IDR_ISLICE_START) || + (pattern_type == + DMX_IDX_H264_NON_IDR_PSLICE_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_BSLICE_START)) + return 1; + } else { + if ((pattern_type == DMX_IDX_H264_IDR_START) || + (pattern_type == DMX_IDX_H264_NON_IDR_START)) + return 1; + } return 0; case DMX_VIDEO_CODEC_VC1: @@ -219,10 +367,23 @@ static inline int mpq_dmx_get_pattern_params( case DMX_VIDEO_CODEC_H264: patterns[0] = dvb_dmx_get_pattern(DMX_IDX_H264_SPS); patterns[1] = dvb_dmx_get_pattern(DMX_IDX_H264_PPS); - patterns[2] = dvb_dmx_get_pattern(DMX_IDX_H264_IDR_START); - patterns[3] = dvb_dmx_get_pattern(DMX_IDX_H264_NON_IDR_START); - patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); - *patterns_num = 5; + if (video_b_frame_events != 1) { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_START); + patterns[4] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 5; + } else { + patterns[2] = dvb_dmx_get_pattern + (DMX_IDX_H264_IDR_ISLICE_START); + patterns[3] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_PSLICE_START); + patterns[4] = dvb_dmx_get_pattern + (DMX_IDX_H264_NON_IDR_BSLICE_START); + patterns[5] = dvb_dmx_get_pattern(DMX_IDX_H264_SEI); + *patterns_num = 6; + } break; case DMX_VIDEO_CODEC_VC1: @@ -254,12 +415,20 @@ void mpq_dmx_update_decoder_stat(struct mpq_feed *mpq_feed) struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; enum mpq_adapter_stream_if idx; - if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) || - (mpq_feed->video_info.stream_interface > - MPQ_ADAPTER_VIDEO3_STREAM_IF)) + if (!dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + !dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed)) return; - idx = mpq_feed->video_info.stream_interface; + if (dvb_dmx_is_video_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->video_info.stream_interface <= + MPQ_ADAPTER_VIDEO3_STREAM_IF) + idx = mpq_feed->video_info.stream_interface; + else if (dvb_dmx_is_audio_feed(mpq_feed->dvb_demux_feed) && + mpq_feed->audio_info.stream_interface <= + MPQ_ADAPTER_AUDIO3_STREAM_IF) + idx = mpq_feed->audio_info.stream_interface; + else + return; curr_time = current_kernel_time(); if (unlikely(!mpq_demux->decoder_stat[idx].out_count)) { @@ -703,6 +872,7 @@ int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func) mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE; mpq_demux->sdmx_eos = 0; mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT; + mpq_demux->ts_packet_timestamp_source = 0; if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) { MPQ_DVB_ERR_PRINT( @@ -1050,9 +1220,33 @@ int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie) mutex_unlock(&mpq_demux->mutex); return ret; - } + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + struct mpq_streambuffer *stream_buffer; + int ret; - /* else */ + mutex_lock(&mpq_demux->mutex); + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: invalid feed, feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1); + spin_unlock(&feed_data->audio_buffer_lock); + mutex_unlock(&mpq_demux->mutex); + + return ret; + } MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", __func__, feed->pes_type); @@ -1383,107 +1577,531 @@ int mpq_dmx_flush_stream_buffer(struct dvb_demux_feed *feed) return ret; } -static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) +static int mpq_dmx_init_audio_internal_buffers( + struct mpq_demux *mpq_demux, + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs) { - struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - struct dvb_demux *demux = feed->demux; + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; int ret = 0; - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; + MPQ_DVB_DBG_PRINT("%s: Internal audio decoder buffer allocation\n", + __func__); - dvbdmx_ts_reset_pes_state(feed); + actual_buffer_size = dec_buffs->buffers_size; + actual_buffer_size += (SZ_4K - 1); + actual_buffer_size &= ~(SZ_4K - 1); - if (dvb_dmx_is_video_feed(feed)) { - MPQ_DVB_DBG_PRINT("%s: flushing video buffer\n", __func__); + temp_handle = ion_alloc(mpq_demux->ion_client, + actual_buffer_size, SZ_4K, + ION_HEAP(audio_nonsecure_ion_heap), + mpq_demux->decoder_alloc_flags); - ret = mpq_dmx_flush_stream_buffer(feed); + if (IS_ERR_OR_NULL(temp_handle)) { + ret = PTR_ERR(temp_handle); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto end; } - mutex_unlock(&demux->mutex); + payload_buffer = ion_map_kernel(mpq_demux->ion_client, temp_handle); + + if (IS_ERR_OR_NULL(payload_buffer)) { + ret = PTR_ERR(payload_buffer); + MPQ_DVB_ERR_PRINT( + "%s: FAILED to map audio payload buffer %d\n", + __func__, ret); + if (!ret) + ret = -ENOMEM; + goto init_failed_free_payload_buffer; + } + feed_data->buffer_desc.decoder_buffers_num = 1; + feed_data->buffer_desc.ion_handle[0] = temp_handle; + feed_data->buffer_desc.desc[0].base = payload_buffer; + feed_data->buffer_desc.desc[0].size = actual_buffer_size; + feed_data->buffer_desc.desc[0].read_ptr = 0; + feed_data->buffer_desc.desc[0].write_ptr = 0; + feed_data->buffer_desc.desc[0].handle = + ion_share_dma_buf_fd(mpq_demux->ion_client, temp_handle); + if (IS_ERR_VALUE(feed_data->buffer_desc.desc[0].handle)) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to share audio payload buffer %d\n", + __func__, ret); + ret = -ENOMEM; + goto init_failed_unmap_payload_buffer; + } + + feed_data->buffer_desc.shared_file = fget( + feed_data->buffer_desc.desc[0].handle); + + return 0; + +init_failed_unmap_payload_buffer: + ion_unmap_kernel(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.desc[0].base = NULL; +init_failed_free_payload_buffer: + ion_free(mpq_demux->ion_client, temp_handle); + feed_data->buffer_desc.ion_handle[0] = NULL; + feed_data->buffer_desc.desc[0].size = 0; + feed_data->buffer_desc.decoder_buffers_num = 0; + feed_data->buffer_desc.shared_file = NULL; +end: return ret; } -/** - * mpq_dmx_init_video_feed - Initializes of video feed information - * used to pass data directly to decoder. - * - * @mpq_feed: The mpq feed object - * - * Return error code. - */ -int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) +static int mpq_dmx_init_audio_external_buffers( + struct mpq_audio_feed_info *feed_data, + struct dmx_decoder_buffers *dec_buffs, + struct ion_client *client) { - int ret; - struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; - struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; - struct mpq_streambuffer *stream_buffer; + struct ion_handle *temp_handle = NULL; + void *payload_buffer = NULL; + int actual_buffer_size = 0; + int ret = 0; + int i; - /* get and store framing information if required */ - if (video_framing) { - mpq_dmx_get_pattern_params( - mpq_feed->dvb_demux_feed->video_codec, - feed_data->patterns, &feed_data->patterns_num); - if (!feed_data->patterns_num) { - MPQ_DVB_ERR_PRINT( - "%s: FAILED to get framing pattern parameters\n", + /* + * Payload buffer was allocated externally (through ION). + * Map the ion handles to kernel memory + */ + MPQ_DVB_DBG_PRINT("%s: External audio decoder buffer allocation\n", __func__); - ret = -EINVAL; - goto init_failed_free_priv_data; - } + actual_buffer_size = dec_buffs->buffers_size; + if (!dec_buffs->is_linear) { + MPQ_DVB_DBG_PRINT("%s: Ex. Ring-buffer\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = 1; + } else { + MPQ_DVB_DBG_PRINT("%s: Ex. Linear\n", __func__); + feed_data->buffer_desc.decoder_buffers_num = + dec_buffs->buffers_num; } - /* Register the new stream-buffer interface to MPQ adapter */ - switch (mpq_feed->dvb_demux_feed->pes_type) { - case DMX_PES_VIDEO0: - feed_data->stream_interface = - MPQ_ADAPTER_VIDEO0_STREAM_IF; - break; - - case DMX_PES_VIDEO1: - feed_data->stream_interface = - MPQ_ADAPTER_VIDEO1_STREAM_IF; - break; - - case DMX_PES_VIDEO2: - feed_data->stream_interface = - MPQ_ADAPTER_VIDEO2_STREAM_IF; - break; - - case DMX_PES_VIDEO3: - feed_data->stream_interface = - MPQ_ADAPTER_VIDEO3_STREAM_IF; - break; + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + ret = mpq_map_buffer_to_kernel( + client, + dec_buffs->handles[i], + &temp_handle, + &payload_buffer); + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: Failed mapping audio buffer %d\n", + __func__, i); + goto init_failed; + } + feed_data->buffer_desc.ion_handle[i] = temp_handle; + feed_data->buffer_desc.desc[i].base = payload_buffer; + feed_data->buffer_desc.desc[i].handle = + dec_buffs->handles[i]; + feed_data->buffer_desc.desc[i].size = + dec_buffs->buffers_size; + feed_data->buffer_desc.desc[i].read_ptr = 0; + feed_data->buffer_desc.desc[i].write_ptr = 0; - default: - MPQ_DVB_ERR_PRINT( - "%s: Invalid pes type %d\n", - __func__, - mpq_feed->dvb_demux_feed->pes_type); - ret = -EINVAL; - goto init_failed_free_priv_data; + MPQ_DVB_DBG_PRINT( + "%s: Audio Buffer #%d: base=0x%p, handle=%d, size=%d\n", + __func__, i, + feed_data->buffer_desc.desc[i].base, + feed_data->buffer_desc.desc[i].handle, + feed_data->buffer_desc.desc[i].size); } - /* make sure not occupied already */ - stream_buffer = NULL; - mpq_adapter_get_stream_if( - feed_data->stream_interface, - &stream_buffer); - if (stream_buffer != NULL) { - MPQ_DVB_ERR_PRINT( - "%s: Video interface %d already occupied!\n", - __func__, - feed_data->stream_interface); - ret = -EBUSY; - goto init_failed_free_priv_data; - } + return 0; - feed_data->video_buffer = +init_failed: + for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } + return ret; +} +static int mpq_dmx_init_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer) +{ + int ret; + void *packet_buffer = NULL; + struct mpq_demux *mpq_demux = feed->mpq_demux; + struct ion_client *client = mpq_demux->ion_client; + struct dmx_decoder_buffers *dec_buffs = NULL; + enum mpq_streambuffer_mode mode; + + dec_buffs = feed->dvb_demux_feed->feed.ts.decoder_buffers; + + /* Allocate packet buffer holding the meta-data */ + packet_buffer = vmalloc(AUDIO_META_DATA_BUFFER_SIZE); + + if (packet_buffer == NULL) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to allocate packets buffer\n", __func__); + ret = -ENOMEM; + goto end; + } + + MPQ_DVB_DBG_PRINT("%s: dec_buffs: num=%d, size=%d, linear=%d\n", + __func__, dec_buffs->buffers_num, + dec_buffs->buffers_size, + dec_buffs->is_linear); + + if (dec_buffs->buffers_num == 0) + ret = mpq_dmx_init_audio_internal_buffers( + mpq_demux, feed_data, dec_buffs); + else + ret = mpq_dmx_init_audio_external_buffers( + feed_data, dec_buffs, client); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + mode = dec_buffs->is_linear ? MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR : + MPQ_STREAMBUFFER_BUFFER_MODE_RING; + ret = mpq_streambuffer_init( + feed_data->audio_buffer, + mode, + feed_data->buffer_desc.desc, + feed_data->buffer_desc.decoder_buffers_num, + packet_buffer, + AUDIO_META_DATA_BUFFER_SIZE); + + if (ret != 0) + goto init_failed_free_packet_buffer; + + goto end; + + +init_failed_free_packet_buffer: + vfree(packet_buffer); +end: + return ret; +} + +static void mpq_dmx_release_audio_streambuffer( + struct mpq_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *audio_buffer, + struct ion_client *client) +{ + int buf_num = 0; + int i; + struct dmx_decoder_buffers *dec_buffs = + feed->dvb_demux_feed->feed.ts.decoder_buffers; + + mpq_adapter_unregister_stream_if(feed_data->stream_interface); + + mpq_streambuffer_terminate(audio_buffer); + + vfree(audio_buffer->packet_data.data); + + buf_num = feed_data->buffer_desc.decoder_buffers_num; + + for (i = 0; i < buf_num; i++) { + if (feed_data->buffer_desc.ion_handle[i]) { + if (feed_data->buffer_desc.desc[i].base) { + ion_unmap_kernel(client, + feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.desc[i].base = NULL; + } + + /* + * Un-share the buffer if kernel is the one that + * shared it. + */ + if (!dec_buffs->buffers_num && + feed_data->buffer_desc.shared_file) { + fput(feed_data->buffer_desc.shared_file); + feed_data->buffer_desc.shared_file = NULL; + } + + ion_free(client, feed_data->buffer_desc.ion_handle[i]); + feed_data->buffer_desc.ion_handle[i] = NULL; + feed_data->buffer_desc.desc[i].size = 0; + } + } +} + +int mpq_dmx_flush_audio_stream_buffer(struct dvb_demux_feed *feed) +{ + struct mpq_feed *mpq_feed = feed->priv; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *sbuff; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: not a audio feed, feed type=%d\n", + __func__, feed->pes_type); + return 0; + } + + spin_lock(&feed_data->audio_buffer_lock); + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + MPQ_DVB_DBG_PRINT("%s: feed_data->audio_buffer is NULL\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return -ENODEV; + } + + ret = mpq_streambuffer_flush(sbuff); + if (ret) + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer_flush failed, ret=%d\n", + __func__, ret); + + spin_unlock(&feed_data->audio_buffer_lock); + + return ret; +} + +static int mpq_dmx_flush_buffer(struct dmx_ts_feed *ts_feed, size_t length) +{ + struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; + struct dvb_demux *demux = feed->demux; + int ret = 0; + + if (mutex_lock_interruptible(&demux->mutex)) + return -ERESTARTSYS; + + dvbdmx_ts_reset_pes_state(feed); + + if (dvb_dmx_is_video_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: flushing video buffer\n", __func__); + + ret = mpq_dmx_flush_stream_buffer(feed); + } else if (dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: flushing audio buffer\n", __func__); + + ret = mpq_dmx_flush_audio_stream_buffer(feed); + } + + mutex_unlock(&demux->mutex); + return ret; +} + +/** + * mpq_dmx_init_video_feed - Initializes of video feed information + * used to pass data directly to decoder. + * + * @mpq_feed: The mpq feed object + * + * Return error code. + */ +int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) +{ + int ret; + struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; + struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_streambuffer *stream_buffer; + + /* get and store framing information if required */ + if (video_framing) { + mpq_dmx_get_pattern_params( + mpq_feed->dvb_demux_feed->video_codec, + feed_data->patterns, &feed_data->patterns_num); + if (!feed_data->patterns_num) { + MPQ_DVB_ERR_PRINT( + "%s: FAILED to get framing pattern parameters\n", + __func__); + + ret = -EINVAL; + goto init_failed_free_priv_data; + } + } + + /* Register the new stream-buffer interface to MPQ adapter */ + switch (mpq_feed->dvb_demux_feed->pes_type) { + case DMX_PES_VIDEO0: + store_mpq_video_feed[0] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO0_STREAM_IF; + break; + + case DMX_PES_VIDEO1: + store_mpq_video_feed[1] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO1_STREAM_IF; + break; + + case DMX_PES_VIDEO2: + store_mpq_video_feed[2] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO2_STREAM_IF; + break; + + case DMX_PES_VIDEO3: + store_mpq_video_feed[3] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_VIDEO3_STREAM_IF; + break; + + default: + MPQ_DVB_ERR_PRINT( + "%s: Invalid pes type %d\n", + __func__, + mpq_feed->dvb_demux_feed->pes_type); + ret = -EINVAL; + goto init_failed_free_priv_data; + } + + /* make sure not occupied already */ + stream_buffer = NULL; + mpq_adapter_get_stream_if( + feed_data->stream_interface, + &stream_buffer); + if (stream_buffer != NULL) { + MPQ_DVB_ERR_PRINT( + "%s: Video interface %d already occupied!\n", + __func__, + feed_data->stream_interface); + ret = -EBUSY; + goto init_failed_free_priv_data; + } + + feed_data->video_buffer = + &mpq_dmx_info.decoder_buffers[feed_data->stream_interface]; + + ret = mpq_dmx_init_streambuffer( + mpq_feed, feed_data, feed_data->video_buffer); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_streambuffer failed, err = %d\n", + __func__, ret); + goto init_failed_free_priv_data; + } + + ret = mpq_adapter_register_stream_if( + feed_data->stream_interface, + feed_data->video_buffer); + + if (ret < 0) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_adapter_register_stream_if failed, err = %d\n", + __func__, ret); + goto init_failed_free_stream_buffer; + } + + spin_lock_init(&feed_data->video_buffer_lock); + + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; + feed_data->pes_header_offset = 0; + mpq_feed->dvb_demux_feed->pusi_seen = 0; + mpq_feed->dvb_demux_feed->peslen = 0; + feed_data->fullness_wait_cancel = 0; + mpq_streambuffer_get_data_rw_offset(feed_data->video_buffer, NULL, + &feed_data->frame_offset); + feed_data->last_pattern_offset = 0; + feed_data->pending_pattern_len = 0; + feed_data->last_framing_match_type = 0; + feed_data->found_sequence_header_pattern = 0; + memset(&feed_data->prefix_size, 0, + sizeof(struct dvb_dmx_video_prefix_size_masks)); + feed_data->first_prefix_size = 0; + feed_data->saved_pts_dts_info.pts_exist = 0; + feed_data->saved_pts_dts_info.dts_exist = 0; + feed_data->new_pts_dts_info.pts_exist = 0; + feed_data->new_pts_dts_info.dts_exist = 0; + feed_data->saved_info_used = 1; + feed_data->new_info_exists = 0; + feed_data->first_pts_dts_copy = 1; + feed_data->tei_errs = 0; + feed_data->last_continuity = -1; + feed_data->continuity_errs = 0; + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + + mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_sum = 0; + mpq_demux->decoder_stat[feed_data->stream_interface]. + out_interval_max = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors = 0; + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors = 0; + + return 0; + +init_failed_free_stream_buffer: + mpq_dmx_release_streambuffer(mpq_feed, feed_data, + feed_data->video_buffer, mpq_demux->ion_client); + mpq_adapter_unregister_stream_if(feed_data->stream_interface); +init_failed_free_priv_data: + feed_data->video_buffer = NULL; + return ret; +} + +/* Register the new stream-buffer interface to MPQ adapter */ +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed) +{ + int ret; + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_streambuffer *stream_buffer; + + switch (mpq_feed->dvb_demux_feed->pes_type) { + case DMX_PES_AUDIO0: + store_mpq_audio_feed[0] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO0_STREAM_IF; + break; + + case DMX_PES_AUDIO1: + store_mpq_audio_feed[1] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO1_STREAM_IF; + break; + + case DMX_PES_AUDIO2: + store_mpq_audio_feed[2] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO2_STREAM_IF; + break; + + case DMX_PES_AUDIO3: + store_mpq_audio_feed[3] = mpq_feed; + feed_data->stream_interface = + MPQ_ADAPTER_AUDIO3_STREAM_IF; + break; + + default: + MPQ_DVB_ERR_PRINT( + "%s: Invalid pes type %d\n", + __func__, + mpq_feed->dvb_demux_feed->pes_type); + ret = -EINVAL; + goto init_failed_free_priv_data; + } + + /* make sure not occupied already */ + stream_buffer = NULL; + mpq_adapter_get_stream_if( + feed_data->stream_interface, + &stream_buffer); + if (stream_buffer != NULL) { + MPQ_DVB_ERR_PRINT( + "%s: Audio interface %d already occupied!\n", + __func__, feed_data->stream_interface); + ret = -EBUSY; + goto init_failed_free_priv_data; + } + + feed_data->audio_buffer = &mpq_dmx_info.decoder_buffers[feed_data->stream_interface]; - ret = mpq_dmx_init_streambuffer( - mpq_feed, feed_data, feed_data->video_buffer); + ret = mpq_dmx_init_audio_streambuffer( + mpq_feed, feed_data, feed_data->audio_buffer); if (ret) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_init_streambuffer failed, err = %d\n", @@ -1493,7 +2111,7 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) ret = mpq_adapter_register_stream_if( feed_data->stream_interface, - feed_data->video_buffer); + feed_data->audio_buffer); if (ret < 0) { MPQ_DVB_ERR_PRINT( @@ -1502,22 +2120,15 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) goto init_failed_free_stream_buffer; } - spin_lock_init(&feed_data->video_buffer_lock); + spin_lock_init(&feed_data->audio_buffer_lock); feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; feed_data->pes_header_offset = 0; mpq_feed->dvb_demux_feed->pusi_seen = 0; mpq_feed->dvb_demux_feed->peslen = 0; feed_data->fullness_wait_cancel = 0; - mpq_streambuffer_get_data_rw_offset(feed_data->video_buffer, NULL, + mpq_streambuffer_get_data_rw_offset(feed_data->audio_buffer, NULL, &feed_data->frame_offset); - feed_data->last_pattern_offset = 0; - feed_data->pending_pattern_len = 0; - feed_data->last_framing_match_type = 0; - feed_data->found_sequence_header_pattern = 0; - memset(&feed_data->prefix_size, 0, - sizeof(struct dvb_dmx_video_prefix_size_masks)); - feed_data->first_prefix_size = 0; feed_data->saved_pts_dts_info.pts_exist = 0; feed_data->saved_pts_dts_info.dts_exist = 0; feed_data->new_pts_dts_info.pts_exist = 0; @@ -1543,11 +2154,11 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed) return 0; init_failed_free_stream_buffer: - mpq_dmx_release_streambuffer(mpq_feed, feed_data, - feed_data->video_buffer, mpq_demux->ion_client); + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + feed_data->audio_buffer, mpq_demux->ion_client); mpq_adapter_unregister_stream_if(feed_data->stream_interface); init_failed_free_priv_data: - feed_data->video_buffer = NULL; + feed_data->audio_buffer = NULL; return ret; } @@ -1563,11 +2174,12 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) { struct mpq_streambuffer *video_buffer; struct mpq_video_feed_info *feed_data; - struct mpq_demux *mpq_demux = mpq_feed->mpq_demux; + struct mpq_demux *mpq_demux; if (mpq_feed == NULL) return -EINVAL; + mpq_demux = mpq_feed->mpq_demux; feed_data = &mpq_feed->video_info; spin_lock(&feed_data->video_buffer_lock); @@ -1582,6 +2194,30 @@ int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed) return 0; } +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer *audio_buffer; + struct mpq_audio_feed_info *feed_data; + struct mpq_demux *mpq_demux; + + if (mpq_feed == NULL) + return -EINVAL; + + mpq_demux = mpq_feed->mpq_demux; + feed_data = &mpq_feed->audio_info; + + spin_lock(&feed_data->audio_buffer_lock); + audio_buffer = feed_data->audio_buffer; + feed_data->audio_buffer = NULL; + wake_up_all(&audio_buffer->raw_data.queue); + spin_unlock(&feed_data->audio_buffer_lock); + + mpq_dmx_release_audio_streambuffer(mpq_feed, feed_data, + audio_buffer, mpq_demux->ion_client); + + return 0; +} + struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed) { struct dvb_demux_feed *tmp; @@ -1814,6 +2450,12 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_terminate_video_feed failed. ret = %d\n", __func__, ret); + } else if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_terminate_audio_feed(mpq_feed); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_terminate_audio_feed failed. ret = %d\n", + __func__, ret); } if (mpq_feed->sdmx_buf_handle) { @@ -1835,22 +2477,28 @@ int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed) int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) { + struct mpq_feed *mpq_feed; + if (dvb_dmx_is_video_feed(feed)) { - struct mpq_feed *mpq_feed; struct mpq_video_feed_info *feed_data; mpq_feed = feed->priv; feed_data = &mpq_feed->video_info; feed_data->fullness_wait_cancel = 0; + return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_audio_feed_info *feed_data; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + feed_data->fullness_wait_cancel = 0; + return 0; } - /* else */ - MPQ_DVB_DBG_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", __func__, + feed->pes_type); return -EINVAL; } @@ -1864,7 +2512,7 @@ int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed) * * Return 1 if required free bytes are available, 0 otherwise. */ -static inline int mpq_dmx_check_decoder_fullness( +static inline int mpq_dmx_check_video_decoder_fullness( struct mpq_streambuffer *sbuff, size_t required_space) { @@ -1888,6 +2536,29 @@ static inline int mpq_dmx_check_decoder_fullness( return (free >= required_space); } +static inline int mpq_dmx_check_audio_decoder_fullness( + struct mpq_streambuffer *sbuff, + size_t required_space) +{ + ssize_t free = mpq_streambuffer_data_free(sbuff); + ssize_t free_meta = mpq_streambuffer_metadata_free(sbuff); + + /* Verify meta-data buffer can contain at least 1 packet */ + if (free_meta < AUDIO_META_DATA_PACKET_SIZE) + return 0; + + /* + * For linear buffers, verify there's enough space for this TSP + * and an additional buffer is free, as framing might required one + * more buffer to be available. + */ + if (sbuff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) + return (free >= required_space && + sbuff->pending_buffers_count < sbuff->buffers_num-1); + else + return (free >= required_space); /* Ring buffer mode */ +} + /** * Checks whether decoder's output buffer has free space * for specific number of bytes, if not, the function waits @@ -1943,7 +2614,8 @@ static int mpq_dmx_decoder_fullness_check( if ((feed_data->video_buffer != NULL) && (!feed_data->fullness_wait_cancel) && - (!mpq_dmx_check_decoder_fullness(sbuff, required_space))) { + (!mpq_dmx_check_video_decoder_fullness(sbuff, + required_space))) { DEFINE_WAIT(__wait); for (;;) { @@ -1952,7 +2624,7 @@ static int mpq_dmx_decoder_fullness_check( TASK_INTERRUPTIBLE); if (!feed_data->video_buffer || feed_data->fullness_wait_cancel || - mpq_dmx_check_decoder_fullness(sbuff, + mpq_dmx_check_video_decoder_fullness(sbuff, required_space)) break; @@ -1987,11 +2659,102 @@ static int mpq_dmx_decoder_fullness_check( return 0; } +static int mpq_dmx_audio_decoder_fullness_check( + struct dvb_demux_feed *feed, + size_t required_space, + int lock_feed) +{ + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_streambuffer *sbuff = NULL; + struct mpq_audio_feed_info *feed_data; + struct mpq_feed *mpq_feed; + int ret = 0; + + if (!dvb_dmx_is_audio_feed(feed)) { + MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n", + __func__, + feed->pes_type); + return -EINVAL; + } + + if (lock_feed) { + mutex_lock(&mpq_demux->mutex); + } else if (!mutex_is_locked(&mpq_demux->mutex)) { + MPQ_DVB_ERR_PRINT( + "%s: Mutex should have been locked\n", + __func__); + return -EINVAL; + } + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + sbuff = feed_data->audio_buffer; + if (sbuff == NULL) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n", + __func__); + return -EINVAL; + } + + if ((feed_data->audio_buffer != NULL) && + (!feed_data->fullness_wait_cancel) && + (!mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space))) { + DEFINE_WAIT(__wait); + + for (;;) { + prepare_to_wait(&sbuff->raw_data.queue, + &__wait, TASK_INTERRUPTIBLE); + if (!feed_data->audio_buffer || + feed_data->fullness_wait_cancel || + mpq_dmx_check_audio_decoder_fullness(sbuff, + required_space)) + break; + + if (!signal_pending(current)) { + mutex_unlock(&mpq_demux->mutex); + schedule(); + mutex_lock(&mpq_demux->mutex); + continue; + } + + ret = -ERESTARTSYS; + break; + } + finish_wait(&sbuff->raw_data.queue, &__wait); + } + + if (ret < 0) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return ret; + } + + if ((feed_data->fullness_wait_cancel) || + (feed_data->audio_buffer == NULL)) { + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + if (lock_feed) + mutex_unlock(&mpq_demux->mutex); + return 0; +} + int mpq_dmx_decoder_fullness_wait( struct dvb_demux_feed *feed, size_t required_space) { - return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + if (dvb_dmx_is_video_feed(feed)) + return mpq_dmx_decoder_fullness_check(feed, required_space, 1); + else if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_audio_decoder_fullness_check(feed, + required_space, 1); + + return 0; } int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) @@ -2006,34 +2769,109 @@ int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed) feed_data->fullness_wait_cancel = 1; - spin_lock(&feed_data->video_buffer_lock); - if (feed_data->video_buffer == NULL) { - MPQ_DVB_DBG_PRINT( - "%s: video_buffer released\n", + spin_lock(&feed_data->video_buffer_lock); + if (feed_data->video_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: video_buffer released\n", __func__); + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + video_buff = &feed_data->video_buffer->raw_data; + wake_up_all(&video_buff->queue); + spin_unlock(&feed_data->video_buffer_lock); + + return 0; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_feed *mpq_feed; + struct mpq_audio_feed_info *feed_data; + struct dvb_ringbuffer *audio_buff; + + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + + feed_data->fullness_wait_cancel = 1; + + spin_lock(&feed_data->audio_buffer_lock); + if (feed_data->audio_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } + + audio_buff = &feed_data->audio_buffer->raw_data; + wake_up_all(&audio_buff->queue); + spin_unlock(&feed_data->audio_buffer_lock); + + return 0; + } + + MPQ_DVB_ERR_PRINT( + "%s: Invalid feed type %d\n", __func__, feed->pes_type); + + return -EINVAL; +} + +int mpq_dmx_parse_mandatory_pes_header( + struct dvb_demux_feed *feed, + struct mpq_video_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + if (feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN) { + left_size = + PES_MANDATORY_FIELDS_LEN - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have beginning of PES header */ + *bytes_avail -= left_size; + *ts_payload_offset += left_size; + + /* Make sure the PES packet is valid */ + if (mpq_dmx_is_valid_video_pes(pes_header) < 0) { + /* + * Since the new PES header parsing + * failed, reset pusi_seen to drop all + * data until next PUSI + */ + feed->pusi_seen = 0; + feed_data->pes_header_offset = 0; + + MPQ_DVB_ERR_PRINT( + "%s: invalid packet\n", __func__); - spin_unlock(&feed_data->video_buffer_lock); - return 0; - } - video_buff = &feed_data->video_buffer->raw_data; - wake_up_all(&video_buff->queue); - spin_unlock(&feed_data->video_buffer_lock); + return -EINVAL; + } - return 0; + feed_data->pes_header_left_bytes = + pes_header->pes_header_data_length; } - /* else */ - MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); - - return -EINVAL; + return 0; } -int mpq_dmx_parse_mandatory_pes_header( +int mpq_dmx_parse_mandatory_audio_pes_header( struct dvb_demux_feed *feed, - struct mpq_video_feed_info *feed_data, + struct mpq_audio_feed_info *feed_data, struct pes_packet_header *pes_header, const u8 *buf, u32 *ts_payload_offset, @@ -2064,7 +2902,7 @@ int mpq_dmx_parse_mandatory_pes_header( *ts_payload_offset += left_size; /* Make sure the PES packet is valid */ - if (mpq_dmx_is_valid_video_pes(pes_header) < 0) { + if (mpq_dmx_is_valid_audio_pes(pes_header) < 0) { /* * Since the new PES header parsing * failed, reset pusi_seen to drop all @@ -2126,6 +2964,46 @@ static inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data, feed_data->new_info_exists = 1; } +static inline void mpq_dmx_get_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header) +{ + struct dmx_pts_dts_info *info = &(feed_data->new_pts_dts_info); + + /* Get PTS/DTS information from PES header */ + + if ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3)) { + info->pts_exist = 1; + + info->pts = + ((u64)pes_header->pts_1 << 30) | + ((u64)pes_header->pts_2 << 22) | + ((u64)pes_header->pts_3 << 15) | + ((u64)pes_header->pts_4 << 7) | + (u64)pes_header->pts_5; + } else { + info->pts_exist = 0; + info->pts = 0; + } + + if (pes_header->pts_dts_flag == 3) { + info->dts_exist = 1; + + info->dts = + ((u64)pes_header->dts_1 << 30) | + ((u64)pes_header->dts_2 << 22) | + ((u64)pes_header->dts_3 << 15) | + ((u64)pes_header->dts_4 << 7) | + (u64)pes_header->dts_5; + } else { + info->dts_exist = 0; + info->dts = 0; + } + + feed_data->new_info_exists = 1; +} + int mpq_dmx_parse_remaining_pes_header( struct dvb_demux_feed *feed, struct mpq_video_feed_info *feed_data, @@ -2218,6 +3096,96 @@ int mpq_dmx_parse_remaining_pes_header( return 0; } +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail) +{ + int left_size, copy_len; + + /* Remaining header bytes that need to be processed? */ + if (!feed_data->pes_header_left_bytes) + return 0; + + /* Did we capture the PTS value (if exists)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+5)) && + ((pes_header->pts_dts_flag == 2) || + (pes_header->pts_dts_flag == 3))) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 5 - + feed_data->pes_header_offset; + + copy_len = + (left_size > *bytes_avail) ? *bytes_avail : left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the PTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Did we capture the DTS value (if exist)? */ + if ((*bytes_avail != 0) && + (feed_data->pes_header_offset < + (PES_MANDATORY_FIELDS_LEN+10)) && + (pes_header->pts_dts_flag == 3)) { + + /* 5 more bytes should be there */ + left_size = + PES_MANDATORY_FIELDS_LEN + 10 - + feed_data->pes_header_offset; + + copy_len = (left_size > *bytes_avail) ? + *bytes_avail : + left_size; + + memcpy((u8 *)((u8 *)pes_header + feed_data->pes_header_offset), + (buf + *ts_payload_offset), + copy_len); + + feed_data->pes_header_offset += copy_len; + feed_data->pes_header_left_bytes -= copy_len; + + if (left_size > *bytes_avail) + return -EINVAL; + + /* else - we have the DTS */ + *bytes_avail -= copy_len; + *ts_payload_offset += copy_len; + } + + /* Any more header bytes?! */ + if (feed_data->pes_header_left_bytes >= *bytes_avail) { + feed_data->pes_header_left_bytes -= *bytes_avail; + return -EINVAL; + } + + /* get PTS/DTS information from PES header to be written later */ + mpq_dmx_get_audio_pts_dts(feed_data, pes_header); + + /* Got PES header, process payload */ + *bytes_avail -= feed_data->pes_header_left_bytes; + *ts_payload_offset += feed_data->pes_header_left_bytes; + feed_data->pes_header_left_bytes = 0; + + return 0; +} + static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, int current_continuity, int discontinuity_indicator) @@ -2249,6 +3217,37 @@ static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data, feed_data->last_continuity = current_continuity; } +static void mpq_dmx_check_audio_continuity( + struct mpq_audio_feed_info *feed_data, + int current_continuity, + int discontinuity_indicator) +{ + const int max_continuity = 0x0F; /* 4 bits in the TS packet header */ + + /* sanity check */ + if (unlikely((current_continuity < 0) || + (current_continuity > max_continuity))) { + MPQ_DVB_DBG_PRINT( + "%s: received invalid continuity counter value %d\n", + __func__, current_continuity); + return; + } + + /* reset last continuity */ + if ((feed_data->last_continuity == -1) || (discontinuity_indicator)) { + feed_data->last_continuity = current_continuity; + return; + } + + /* check for continuity errors */ + if (current_continuity != + ((feed_data->last_continuity + 1) & max_continuity)) + feed_data->continuity_errs++; + + /* save for next time */ + feed_data->last_continuity = current_continuity; +} + static inline void mpq_dmx_prepare_es_event_data( struct mpq_streambuffer_packet_header *packet, struct mpq_adapter_video_meta_data *meta_data, @@ -2295,6 +3294,43 @@ static inline void mpq_dmx_prepare_es_event_data( feed_data->continuity_errs = 0; } +static inline void mpq_dmx_prepare_audio_es_event_data( + struct mpq_streambuffer_packet_header *packet, + struct mpq_adapter_audio_meta_data *meta_data, + struct mpq_audio_feed_info *feed_data, + struct mpq_streambuffer *stream_buffer, + struct dmx_data_ready *data, + int cookie) +{ + struct dmx_pts_dts_info *pts_dts; + + pts_dts = &meta_data->info.pes.pts_dts_info; + data->buf.stc = meta_data->info.pes.stc; + + data->data_length = 0; + data->buf.handle = packet->raw_data_handle; + data->buf.cookie = cookie; + data->buf.offset = packet->raw_data_offset; + data->buf.len = packet->raw_data_len; + data->buf.pts_exists = pts_dts->pts_exist; + data->buf.pts = pts_dts->pts; + data->buf.dts_exists = pts_dts->dts_exist; + data->buf.dts = pts_dts->dts; + data->buf.tei_counter = feed_data->tei_errs; + data->buf.cont_err_counter = feed_data->continuity_errs; + data->buf.ts_packets_num = feed_data->ts_packets_num; + data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes; + data->status = DMX_OK_DECODER_BUF; + + MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie); + + /* reset counters */ + feed_data->ts_packets_num = 0; + feed_data->ts_dropped_bytes = 0; + feed_data->tei_errs = 0; + feed_data->continuity_errs = 0; +} + static int mpq_sdmx_dvr_buffer_desc(struct mpq_demux *mpq_demux, struct sdmx_buff_descr *buf_desc) { @@ -2393,10 +3429,84 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux, mpq_dmx_update_decoder_stat(mpq_feed); - /* Writing meta-data that includes the framing information */ + /* Writing meta-data that includes the framing information */ + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie >= 0) { + mpq_dmx_prepare_es_event_data(&packet, &meta_data, + feed_data, stream_buffer, &data, cookie); + feed->data_ready_cb.ts(&feed->feed.ts, &data); + } else { + MPQ_DVB_ERR_PRINT( + "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + __func__, cookie); + } + } + + spin_unlock(&feed_data->video_buffer_lock); +} + +/** + * mpq_dmx_decoder_pes_closure - Helper function to handle closing current PES + * upon reaching EOS. + * + * @mpq_demux - mpq demux instance + * @mpq_feed - mpq feed object + */ +static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, + struct mpq_feed *mpq_feed) +{ + struct mpq_streambuffer_packet_header packet; + struct mpq_streambuffer *stream_buffer; + struct mpq_adapter_video_meta_data meta_data; + struct mpq_video_feed_info *feed_data; + struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; + struct dmx_data_ready data; + int cookie; + + feed_data = &mpq_feed->video_info; + + /* + * spin-lock is taken to protect against manipulation of video + * output buffer by the API (terminate video feed, re-use of video + * buffers). + */ + spin_lock(&feed_data->video_buffer_lock); + stream_buffer = feed_data->video_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__); + spin_unlock(&feed_data->video_buffer_lock); + return; + } + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + */ + if ((feed->pusi_seen) && (feed_data->pes_header_left_bytes == 0)) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle(stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = feed_data->frame_offset; + packet.user_data_len = + sizeof(struct mpq_adapter_video_meta_data); + + mpq_dmx_write_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); + cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet, (u8 *)&meta_data); if (cookie >= 0) { + /* Save write offset where new PES will begin */ + mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, + &feed_data->frame_offset); mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data, stream_buffer, &data, cookie); feed->data_ready_cb.ts(&feed->feed.ts, &data); @@ -2406,41 +3516,42 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux, __func__, cookie); } } + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; spin_unlock(&feed_data->video_buffer_lock); } -/** - * mpq_dmx_decoder_pes_closure - Helper function to handle closing current PES - * upon reaching EOS. - * - * @mpq_demux - mpq demux instance - * @mpq_feed - mpq feed object +/* + * in audio handling although ES frames are send to decoder, close the + * pes packet */ -static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, +static void mpq_dmx_decoder_audio_pes_closure(struct mpq_demux *mpq_demux, struct mpq_feed *mpq_feed) { struct mpq_streambuffer_packet_header packet; struct mpq_streambuffer *stream_buffer; - struct mpq_adapter_video_meta_data meta_data; - struct mpq_video_feed_info *feed_data; + struct mpq_adapter_audio_meta_data meta_data; + struct mpq_audio_feed_info *feed_data; struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed; struct dmx_data_ready data; int cookie; - feed_data = &mpq_feed->video_info; + feed_data = &mpq_feed->audio_info; /* - * spin-lock is taken to protect against manipulation of video - * output buffer by the API (terminate video feed, re-use of video + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio * buffers). */ - spin_lock(&feed_data->video_buffer_lock); - stream_buffer = feed_data->video_buffer; + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; if (stream_buffer == NULL) { - MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__); - spin_unlock(&feed_data->video_buffer_lock); + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", __func__); + spin_unlock(&feed_data->audio_buffer_lock); return; } @@ -2455,9 +3566,9 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, &packet.raw_data_handle); packet.raw_data_offset = feed_data->frame_offset; packet.user_data_len = - sizeof(struct mpq_adapter_video_meta_data); + sizeof(struct mpq_adapter_audio_meta_data); - mpq_dmx_write_pts_dts(feed_data, + mpq_dmx_write_audio_pts_dts(feed_data, &(meta_data.info.pes.pts_dts_info)); meta_data.packet_type = DMX_PES_PACKET; @@ -2471,12 +3582,12 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, /* Save write offset where new PES will begin */ mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL, &feed_data->frame_offset); - mpq_dmx_prepare_es_event_data(&packet, &meta_data, + mpq_dmx_prepare_audio_es_event_data(&packet, &meta_data, feed_data, stream_buffer, &data, cookie); feed->data_ready_cb.ts(&feed->feed.ts, &data); } else { MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", + "%s: mpq_sb_pkt_write failed, ret=%d\n", __func__, cookie); } } @@ -2485,7 +3596,7 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux, feed_data->pes_header_offset = 0; feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN; - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); } static int mpq_dmx_process_video_packet_framing( @@ -2824,6 +3935,7 @@ static int mpq_dmx_process_video_packet_framing( pending_data_len -= bytes_to_write; feed_data->pending_pattern_len += bytes_to_write; } + non_predicted_video_frame = 0; is_video_frame = mpq_dmx_is_video_frame( feed->video_codec, @@ -2863,9 +3975,9 @@ static int mpq_dmx_process_video_packet_framing( ret = mpq_streambuffer_pkt_write(stream_buffer, &packet, (u8 *)&meta_data); if (ret < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", - __func__, ret); + MPQ_DVB_ERR_PRINT + ("%s: mpq_sb_pkt_write failed ret=%d\n", + __func__, ret); if (ret == -ENOSPC) mpq_dmx_notify_overflow(feed); } else { @@ -2873,7 +3985,15 @@ static int mpq_dmx_process_video_packet_framing( &packet, &meta_data, feed_data, stream_buffer, &data, ret); - feed->data_ready_cb.ts(&feed->feed.ts, &data); + /* Trigger ES Data Event for VPTS */ + if (video_b_frame_events == 1) { + if (non_predicted_video_frame == 1) + feed->data_ready_cb.ts + (&feed->feed.ts, &data); + } else { + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } if (feed_data->video_buffer->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) @@ -2918,76 +4038,300 @@ static int mpq_dmx_process_video_packet_framing( u32 buff_size = feed_data->video_buffer->buffers[0].size; - offset -= framing_res.info[i].used_prefix_size; - offset += (offset < 0) ? buff_size : 0; - feed_data->pending_pattern_len = - framing_res.info[i].used_prefix_size; + offset -= framing_res.info[i].used_prefix_size; + offset += (offset < 0) ? buff_size : 0; + feed_data->pending_pattern_len = + framing_res.info[i].used_prefix_size; + + if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == + feed_data->video_buffer->mode) { + feed_data->frame_offset = (u32)offset; + } + } + } + + /* save the last match for next time */ + feed_data->last_framing_match_type = + framing_res.info[i].type; + feed_data->last_pattern_offset = + framing_res.info[i].offset; + if (framing_res.info[i].used_prefix_size) + feed_data->last_framing_match_stc = feed_data->prev_stc; + else + feed_data->last_framing_match_stc = curr_stc; + } + + feed_data->prev_stc = curr_stc; + feed_data->first_prefix_size = 0; + + /* + * Save the trailing of the TS packet as we might have a pattern + * split that we need to re-use when closing the next + * video linear buffer. + */ + if (MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == + feed_data->video_buffer->mode) + memcpy(feed_data->prev_pattern, + buf + TS_PACKET_SIZE - DVB_DMX_MAX_PATTERN_LEN, + DVB_DMX_MAX_PATTERN_LEN); + + if (pending_data_len) { + ret = mpq_streambuffer_data_write( + stream_buffer, + (buf + ts_payload_offset + bytes_written), + pending_data_len); + + if (ret < 0) { + mpq_demux->decoder_stat + [feed_data->stream_interface].drop_count += + pending_data_len; + feed_data->ts_dropped_bytes += pending_data_len; + MPQ_DVB_DBG_PRINT( + "%s: Couldn't write %d pending bytes to data buffer, ret=%d\n", + __func__, pending_data_len, ret); + if (ret == -ENOSPC) + mpq_dmx_notify_overflow(feed); + } else { + feed_data->pending_pattern_len += pending_data_len; + } + } + + spin_unlock(&feed_data->video_buffer_lock); + return 0; +} + +static int mpq_dmx_process_video_packet_no_framing( + struct dvb_demux_feed *feed, + const u8 *buf, + u64 curr_stc) +{ + int bytes_avail; + u32 ts_payload_offset; + struct mpq_video_feed_info *feed_data; + const struct ts_packet_header *ts_header; + struct mpq_streambuffer *stream_buffer; + struct pes_packet_header *pes_header; + struct mpq_demux *mpq_demux; + struct mpq_feed *mpq_feed; + int discontinuity_indicator = 0; + struct dmx_data_ready data; + int cookie; + int ret; + + mpq_demux = feed->demux->priv; + mpq_feed = feed->priv; + feed_data = &mpq_feed->video_info; + + /* + * spin-lock is taken to protect against manipulation of video + * output buffer by the API (terminate video feed, re-use of video + * buffers). Mutex on the video-feed cannot be held here + * since SW demux holds a spin-lock while calling write_to_decoder + */ + spin_lock(&feed_data->video_buffer_lock); + stream_buffer = feed_data->video_buffer; + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT( + "%s: video_buffer released\n", + __func__); + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + ts_header = (const struct ts_packet_header *)buf; + + pes_header = &feed_data->pes_header; + + /* Make sure this TS packet has a payload and not scrambled */ + if ((ts_header->sync_byte != 0x47) || + (ts_header->adaptation_field_control == 0) || + (ts_header->adaptation_field_control == 2) || + (ts_header->transport_scrambling_control)) { + /* continue to next packet */ + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + if (ts_header->payload_unit_start_indicator) { /* PUSI? */ + if (feed->pusi_seen) { /* Did we see PUSI before? */ + struct mpq_streambuffer_packet_header packet; + struct mpq_adapter_video_meta_data meta_data; + + /* + * Close previous PES. + * Push new packet to the meta-data buffer. + * Double check that we are not in middle of + * previous PES header parsing. + */ + + if (feed_data->pes_header_left_bytes == 0) { + packet.raw_data_len = feed->peslen; + mpq_streambuffer_get_buffer_handle( + stream_buffer, + 0, /* current write buffer handle */ + &packet.raw_data_handle); + packet.raw_data_offset = + feed_data->frame_offset; + packet.user_data_len = + sizeof(struct + mpq_adapter_video_meta_data); + + mpq_dmx_write_pts_dts(feed_data, + &(meta_data.info.pes.pts_dts_info)); + + /* Mark that we detected start of new PES */ + feed_data->first_pts_dts_copy = 1; + + meta_data.packet_type = DMX_PES_PACKET; + meta_data.info.pes.stc = feed_data->prev_stc; + + mpq_dmx_update_decoder_stat(mpq_feed); + + cookie = mpq_streambuffer_pkt_write( + stream_buffer, &packet, + (u8 *)&meta_data); + if (cookie < 0) { + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", + __func__, cookie); + } else { + /* + * Save write offset where new PES + * will begin + */ + mpq_streambuffer_get_data_rw_offset( + stream_buffer, + NULL, + &feed_data->frame_offset); + + mpq_dmx_prepare_es_event_data( + &packet, &meta_data, + feed_data, + stream_buffer, &data, cookie); + + feed->data_ready_cb.ts(&feed->feed.ts, + &data); + } + } else { + MPQ_DVB_ERR_PRINT( + "%s: received PUSI while handling PES header of previous PES\n", + __func__); + } + + /* Reset PES info */ + feed->peslen = 0; + feed_data->pes_header_offset = 0; + feed_data->pes_header_left_bytes = + PES_MANDATORY_FIELDS_LEN; + } else { + feed->pusi_seen = 1; + } + + feed_data->prev_stc = curr_stc; + } + + /* + * Parse PES data only if PUSI was encountered, + * otherwise the data is dropped + */ + if (!feed->pusi_seen) { + spin_unlock(&feed_data->video_buffer_lock); + return 0; /* drop and wait for next packets */ + } + + ts_payload_offset = sizeof(struct ts_packet_header); - if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == - feed_data->video_buffer->mode) { - feed_data->frame_offset = (u32)offset; - } - } - } + /* + * Skip adaptation field if exists. + * Save discontinuity indicator if exists. + */ + if (ts_header->adaptation_field_control == 3) { + const struct ts_adaptation_field *adaptation_field = + (const struct ts_adaptation_field *)(buf + + ts_payload_offset); - /* save the last match for next time */ - feed_data->last_framing_match_type = - framing_res.info[i].type; - feed_data->last_pattern_offset = - framing_res.info[i].offset; - if (framing_res.info[i].used_prefix_size) - feed_data->last_framing_match_stc = feed_data->prev_stc; - else - feed_data->last_framing_match_stc = curr_stc; + discontinuity_indicator = + adaptation_field->discontinuity_indicator; + ts_payload_offset += buf[ts_payload_offset] + 1; } - feed_data->prev_stc = curr_stc; - feed_data->first_prefix_size = 0; + bytes_avail = TS_PACKET_SIZE - ts_payload_offset; + + /* Get the mandatory fields of the video PES header */ + if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + if (mpq_dmx_parse_remaining_pes_header(feed, feed_data, + pes_header, buf, + &ts_payload_offset, + &bytes_avail)) { + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } /* - * Save the trailing of the TS packet as we might have a pattern - * split that we need to re-use when closing the next - * video linear buffer. + * If we reached here, + * then we are now at the PES payload data */ - if (MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == - feed_data->video_buffer->mode) - memcpy(feed_data->prev_pattern, - buf + TS_PACKET_SIZE - DVB_DMX_MAX_PATTERN_LEN, - DVB_DMX_MAX_PATTERN_LEN); + if (bytes_avail == 0) { + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } - if (pending_data_len) { - ret = mpq_streambuffer_data_write( - stream_buffer, - (buf + ts_payload_offset + bytes_written), - pending_data_len); + /* + * Need to back-up the PTS information + * of the start of new PES + */ + if (feed_data->first_pts_dts_copy) { + mpq_dmx_save_pts_dts(feed_data); + feed_data->first_pts_dts_copy = 0; + } - if (ret < 0) { - mpq_demux->decoder_stat - [feed_data->stream_interface].drop_count += - pending_data_len; - feed_data->ts_dropped_bytes += pending_data_len; - MPQ_DVB_DBG_PRINT( - "%s: Couldn't write %d pending bytes to data buffer, ret=%d\n", - __func__, pending_data_len, ret); - if (ret == -ENOSPC) - mpq_dmx_notify_overflow(feed); - } else { - feed_data->pending_pattern_len += pending_data_len; - } + /* Update error counters based on TS header */ + feed_data->ts_packets_num++; + feed_data->tei_errs += ts_header->transport_error_indicator; + mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors += + ts_header->transport_error_indicator; + mpq_dmx_check_continuity(feed_data, + ts_header->continuity_counter, + discontinuity_indicator); + mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors += + feed_data->continuity_errs; + + ret = mpq_streambuffer_data_write(stream_buffer, buf+ts_payload_offset, + bytes_avail); + if (ret < 0) { + mpq_demux->decoder_stat + [feed_data->stream_interface].drop_count += bytes_avail; + feed_data->ts_dropped_bytes += bytes_avail; + if (ret == -ENOSPC) + mpq_dmx_notify_overflow(feed); + } else { + feed->peslen += bytes_avail; } spin_unlock(&feed_data->video_buffer_lock); + return 0; } -static int mpq_dmx_process_video_packet_no_framing( +/* + * parse PES headers and send down ES packets to decoder + * Trigger a new ES Data Event with APTS and QTimer in 1st PES + */ +static int mpq_dmx_process_audio_packet_no_framing( struct dvb_demux_feed *feed, const u8 *buf, u64 curr_stc) { int bytes_avail; u32 ts_payload_offset; - struct mpq_video_feed_info *feed_data; + struct mpq_audio_feed_info *feed_data; const struct ts_packet_header *ts_header; struct mpq_streambuffer *stream_buffer; struct pes_packet_header *pes_header; @@ -3000,21 +4344,21 @@ static int mpq_dmx_process_video_packet_no_framing( mpq_demux = feed->demux->priv; mpq_feed = feed->priv; - feed_data = &mpq_feed->video_info; + feed_data = &mpq_feed->audio_info; /* - * spin-lock is taken to protect against manipulation of video - * output buffer by the API (terminate video feed, re-use of video - * buffers). Mutex on the video-feed cannot be held here + * spin-lock is taken to protect against manipulation of audio + * output buffer by the API (terminate audio feed, re-use of audio + * buffers). Mutex on the audio-feed cannot be held here * since SW demux holds a spin-lock while calling write_to_decoder */ - spin_lock(&feed_data->video_buffer_lock); - stream_buffer = feed_data->video_buffer; + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; if (stream_buffer == NULL) { MPQ_DVB_DBG_PRINT( - "%s: video_buffer released\n", + "%s: audio_buffer released\n", __func__); - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } @@ -3028,14 +4372,14 @@ static int mpq_dmx_process_video_packet_no_framing( (ts_header->adaptation_field_control == 2) || (ts_header->transport_scrambling_control)) { /* continue to next packet */ - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } if (ts_header->payload_unit_start_indicator) { /* PUSI? */ if (feed->pusi_seen) { /* Did we see PUSI before? */ struct mpq_streambuffer_packet_header packet; - struct mpq_adapter_video_meta_data meta_data; + struct mpq_adapter_audio_meta_data meta_data; /* * Close previous PES. @@ -3054,9 +4398,9 @@ static int mpq_dmx_process_video_packet_no_framing( feed_data->frame_offset; packet.user_data_len = sizeof(struct - mpq_adapter_video_meta_data); + mpq_adapter_audio_meta_data); - mpq_dmx_write_pts_dts(feed_data, + mpq_dmx_write_audio_pts_dts(feed_data, &(meta_data.info.pes.pts_dts_info)); /* Mark that we detected start of new PES */ @@ -3067,13 +4411,14 @@ static int mpq_dmx_process_video_packet_no_framing( mpq_dmx_update_decoder_stat(mpq_feed); + /* actual writing of stream audio headers */ cookie = mpq_streambuffer_pkt_write( stream_buffer, &packet, (u8 *)&meta_data); if (cookie < 0) { - MPQ_DVB_ERR_PRINT( - "%s: mpq_streambuffer_pkt_write failed, ret=%d\n", - __func__, cookie); + MPQ_DVB_ERR_PRINT + ("%s: write failed, ret=%d\n", + __func__, cookie); } else { /* * Save write offset where new PES @@ -3084,18 +4429,22 @@ static int mpq_dmx_process_video_packet_no_framing( NULL, &feed_data->frame_offset); - mpq_dmx_prepare_es_event_data( + mpq_dmx_prepare_audio_es_event_data( &packet, &meta_data, feed_data, stream_buffer, &data, cookie); + /* + * Trigger ES data event for APTS + * and AFRAME + */ feed->data_ready_cb.ts(&feed->feed.ts, - &data); + &data); } } else { MPQ_DVB_ERR_PRINT( "%s: received PUSI while handling PES header of previous PES\n", - __func__); + __func__); } /* Reset PES info */ @@ -3115,7 +4464,7 @@ static int mpq_dmx_process_video_packet_no_framing( * otherwise the data is dropped */ if (!feed->pusi_seen) { - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; /* drop and wait for next packets */ } @@ -3137,20 +4486,22 @@ static int mpq_dmx_process_video_packet_no_framing( bytes_avail = TS_PACKET_SIZE - ts_payload_offset; - /* Get the mandatory fields of the video PES header */ - if (mpq_dmx_parse_mandatory_pes_header(feed, feed_data, + /* The audio decoder requires ES packets ! */ + + /* Get the mandatory fields of the audio PES header */ + if (mpq_dmx_parse_mandatory_audio_pes_header(feed, feed_data, pes_header, buf, &ts_payload_offset, &bytes_avail)) { - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } - if (mpq_dmx_parse_remaining_pes_header(feed, feed_data, + if (mpq_dmx_parse_remaining_audio_pes_header(feed, feed_data, pes_header, buf, &ts_payload_offset, &bytes_avail)) { - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } @@ -3159,7 +4510,7 @@ static int mpq_dmx_process_video_packet_no_framing( * then we are now at the PES payload data */ if (bytes_avail == 0) { - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } @@ -3168,7 +4519,7 @@ static int mpq_dmx_process_video_packet_no_framing( * of the start of new PES */ if (feed_data->first_pts_dts_copy) { - mpq_dmx_save_pts_dts(feed_data); + mpq_dmx_save_audio_pts_dts(feed_data); feed_data->first_pts_dts_copy = 0; } @@ -3177,12 +4528,13 @@ static int mpq_dmx_process_video_packet_no_framing( feed_data->tei_errs += ts_header->transport_error_indicator; mpq_demux->decoder_stat[feed_data->stream_interface].ts_errors += ts_header->transport_error_indicator; - mpq_dmx_check_continuity(feed_data, + mpq_dmx_check_audio_continuity(feed_data, ts_header->continuity_counter, discontinuity_indicator); mpq_demux->decoder_stat[feed_data->stream_interface].cc_errors += feed_data->continuity_errs; + /* actual writing of audio data for a stream */ ret = mpq_streambuffer_data_write(stream_buffer, buf+ts_payload_offset, bytes_avail); if (ret < 0) { @@ -3195,65 +4547,108 @@ static int mpq_dmx_process_video_packet_no_framing( feed->peslen += bytes_avail; } - spin_unlock(&feed_data->video_buffer_lock); + spin_unlock(&feed_data->audio_buffer_lock); return 0; } +/* function ptr used in several places, handle differently */ int mpq_dmx_decoder_buffer_status(struct dvb_demux_feed *feed, struct dmx_buffer_status *dmx_buffer_status) { - struct mpq_demux *mpq_demux = feed->demux->priv; - struct mpq_video_feed_info *feed_data; - struct mpq_streambuffer *video_buff; - struct mpq_feed *mpq_feed; - if (!dvb_dmx_is_video_feed(feed)) { - MPQ_DVB_ERR_PRINT( - "%s: Invalid feed type %d\n", - __func__, - feed->pes_type); - return -EINVAL; - } + if (dvb_dmx_is_video_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_video_feed_info *feed_data; + struct mpq_streambuffer *video_buff; + struct mpq_feed *mpq_feed; - mutex_lock(&mpq_demux->mutex); + mutex_lock(&mpq_demux->mutex); + + mpq_feed = feed->priv; + feed_data = &mpq_feed->video_info; + video_buff = feed_data->video_buffer; + if (!video_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } + + dmx_buffer_status->error = video_buff->raw_data.error; + + if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + video_buff->buffers[0].size * + video_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + video_buff->buffers[0].size * + (video_buff->buffers_num - + video_buff->pending_buffers_count); + dmx_buffer_status->size = + video_buff->buffers[0].size * + video_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(video_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(video_buff); + dmx_buffer_status->size = video_buff->buffers[0].size; + } + + mpq_streambuffer_get_data_rw_offset( + video_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); - mpq_feed = feed->priv; - feed_data = &mpq_feed->video_info; - video_buff = feed_data->video_buffer; - if (!video_buff) { mutex_unlock(&mpq_demux->mutex); - return -EINVAL; - } - dmx_buffer_status->error = video_buff->raw_data.error; + } else if (dvb_dmx_is_audio_feed(feed)) { + struct mpq_demux *mpq_demux = feed->demux->priv; + struct mpq_audio_feed_info *feed_data; + struct mpq_streambuffer *audio_buff; + struct mpq_feed *mpq_feed; + + mutex_lock(&mpq_demux->mutex); - if (video_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { - dmx_buffer_status->fullness = - video_buff->buffers[0].size * - video_buff->pending_buffers_count; - dmx_buffer_status->free_bytes = - video_buff->buffers[0].size * - (video_buff->buffers_num - - video_buff->pending_buffers_count); - dmx_buffer_status->size = - video_buff->buffers[0].size * - video_buff->buffers_num; - } else { - dmx_buffer_status->fullness = - mpq_streambuffer_data_avail(video_buff); - dmx_buffer_status->free_bytes = - mpq_streambuffer_data_free(video_buff); - dmx_buffer_status->size = video_buff->buffers[0].size; - } + mpq_feed = feed->priv; + feed_data = &mpq_feed->audio_info; + audio_buff = feed_data->audio_buffer; + if (!audio_buff) { + mutex_unlock(&mpq_demux->mutex); + return -EINVAL; + } - mpq_streambuffer_get_data_rw_offset( - video_buff, - &dmx_buffer_status->read_offset, - &dmx_buffer_status->write_offset); + dmx_buffer_status->error = audio_buff->raw_data.error; + + if (audio_buff->mode == MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR) { + dmx_buffer_status->fullness = + audio_buff->buffers[0].size * + audio_buff->pending_buffers_count; + dmx_buffer_status->free_bytes = + audio_buff->buffers[0].size * + (audio_buff->buffers_num - + audio_buff->pending_buffers_count); + dmx_buffer_status->size = + audio_buff->buffers[0].size * + audio_buff->buffers_num; + } else { + dmx_buffer_status->fullness = + mpq_streambuffer_data_avail(audio_buff); + dmx_buffer_status->free_bytes = + mpq_streambuffer_data_free(audio_buff); + dmx_buffer_status->size = audio_buff->buffers[0].size; + } - mutex_unlock(&mpq_demux->mutex); + mpq_streambuffer_get_data_rw_offset( + audio_buff, + &dmx_buffer_status->read_offset, + &dmx_buffer_status->write_offset); + mutex_unlock(&mpq_demux->mutex); + } else { + MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n", + __func__, feed->pes_type); + return -EINVAL; + } return 0; } @@ -3268,10 +4663,18 @@ int mpq_dmx_process_video_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { curr_stc = 0; } else { - curr_stc = buf[STC_LOCATION_IDX + 2] << 16; - curr_stc += buf[STC_LOCATION_IDX + 1] << 8; - curr_stc += buf[STC_LOCATION_IDX]; - curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } } if (!video_framing) @@ -3282,6 +4685,34 @@ int mpq_dmx_process_video_packet( curr_stc); } +int mpq_dmx_process_audio_packet( + struct dvb_demux_feed *feed, + const u8 *buf) +{ + u64 curr_stc; + struct mpq_demux *mpq_demux = feed->demux->priv; + + if ((mpq_demux->source >= DMX_SOURCE_DVR0) && + (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { + curr_stc = 0; + } else { + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + curr_stc = buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + curr_stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + curr_stc = buf[STC_LOCATION_IDX + 3] << 24; + curr_stc += buf[STC_LOCATION_IDX + 2] << 16; + curr_stc += buf[STC_LOCATION_IDX + 1] << 8; + curr_stc += buf[STC_LOCATION_IDX]; + } + } + + return mpq_dmx_process_audio_packet_no_framing(feed, buf, curr_stc); +} + int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci) { const struct ts_packet_header *ts_header; @@ -3342,10 +4773,18 @@ int mpq_dmx_process_pcr_packet( (mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) { stc = 0; } else { - stc = buf[STC_LOCATION_IDX + 2] << 16; - stc += buf[STC_LOCATION_IDX + 1] << 8; - stc += buf[STC_LOCATION_IDX]; - stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != + TSIF_TTS_LPASS_TIMER) { + stc = buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + stc *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + stc = buf[STC_LOCATION_IDX + 3] << 24; + stc += buf[STC_LOCATION_IDX + 2] << 16; + stc += buf[STC_LOCATION_IDX + 1] << 8; + stc += buf[STC_LOCATION_IDX]; + } } data.data_length = 0; @@ -3356,45 +4795,86 @@ int mpq_dmx_process_pcr_packet( return 0; } -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed) +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type) { - struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; - struct mpq_streambuffer *stream_buffer; - struct mpq_streambuffer_packet_header oob_packet; - struct mpq_adapter_video_meta_data oob_meta_data; - int ret; + if (feed_type == 1) { /* video feed */ + struct mpq_video_feed_info *feed_data = &mpq_feed->video_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_video_meta_data oob_meta_data; + int ret; - spin_lock(&feed_data->video_buffer_lock); - stream_buffer = feed_data->video_buffer; + spin_lock(&feed_data->video_buffer_lock); + stream_buffer = feed_data->video_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", + __func__); + spin_unlock(&feed_data->video_buffer_lock); + return 0; + } + + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; + + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); - if (stream_buffer == NULL) { - MPQ_DVB_DBG_PRINT("%s: video_buffer released\n", __func__); spin_unlock(&feed_data->video_buffer_lock); - return 0; - } + return (ret < 0) ? ret : 0; + + } else if (feed_type == 2) { /* audio feed */ + struct mpq_audio_feed_info *feed_data = &mpq_feed->audio_info; + struct mpq_streambuffer *stream_buffer; + struct mpq_streambuffer_packet_header oob_packet; + struct mpq_adapter_audio_meta_data oob_meta_data; + int ret; + + spin_lock(&feed_data->audio_buffer_lock); + stream_buffer = feed_data->audio_buffer; + + if (stream_buffer == NULL) { + MPQ_DVB_DBG_PRINT("%s: audio_buffer released\n", + __func__); + spin_unlock(&feed_data->audio_buffer_lock); + return 0; + } - memset(&oob_packet, 0, sizeof(oob_packet)); - oob_packet.user_data_len = sizeof(oob_meta_data); - oob_meta_data.packet_type = DMX_EOS_PACKET; + memset(&oob_packet, 0, sizeof(oob_packet)); + oob_packet.user_data_len = sizeof(oob_meta_data); + oob_meta_data.packet_type = DMX_EOS_PACKET; - ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, - (u8 *)&oob_meta_data); + ret = mpq_streambuffer_pkt_write(stream_buffer, &oob_packet, + (u8 *)&oob_meta_data); - spin_unlock(&feed_data->video_buffer_lock); - return (ret < 0) ? ret : 0; + spin_unlock(&feed_data->audio_buffer_lock); + return (ret < 0) ? ret : 0; + } + + return 0; } void mpq_dmx_convert_tts(struct dvb_demux_feed *feed, const u8 timestamp[TIMESTAMP_LEN], u64 *timestampIn27Mhz) { + struct mpq_demux *mpq_demux = feed->demux->priv; + if (unlikely(!timestampIn27Mhz)) return; - *timestampIn27Mhz = timestamp[2] << 16; - *timestampIn27Mhz += timestamp[1] << 8; - *timestampIn27Mhz += timestamp[0]; - *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + if (mpq_demux->ts_packet_timestamp_source != TSIF_TTS_LPASS_TIMER) { + *timestampIn27Mhz = timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + *timestampIn27Mhz *= 256; /* convert from 105.47 KHZ to 27MHz */ + } else { + *timestampIn27Mhz = timestamp[3] << 24; + *timestampIn27Mhz += timestamp[2] << 16; + *timestampIn27Mhz += timestamp[1] << 8; + *timestampIn27Mhz += timestamp[0]; + } } int mpq_sdmx_open_session(struct mpq_demux *mpq_demux) @@ -3904,6 +5384,16 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) } } + if (dvb_dmx_is_audio_feed(feed)) { + ret = mpq_dmx_init_audio_feed(mpq_feed); + if (ret) { + MPQ_DVB_ERR_PRINT( + "%s: mpq_dmx_init_audio_feed failed, ret=%d\n", + __func__, ret); + goto init_mpq_feed_end; + } + } + /* * sdmx is not relevant for recording filters, which always use * regular filters (non-sdmx) @@ -3924,6 +5414,8 @@ int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed) __func__, ret); if (dvb_dmx_is_video_feed(feed)) mpq_dmx_terminate_video_feed(mpq_feed); + else if (dvb_dmx_is_audio_feed(feed)) + mpq_dmx_terminate_audio_feed(mpq_feed); } init_mpq_feed_end: @@ -4662,7 +6154,7 @@ decoder_filter_check_flags: if (sts->status_indicators & SDMX_FILTER_STATUS_EOS) { /* Notify decoder via the stream buffer */ - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Failed to notify decoder on EOS, ret=%d\n", @@ -5162,11 +6654,19 @@ int mpq_dmx_oob_command(struct dvb_demux_feed *feed, else mpq_dmx_decoder_frame_closure(mpq_demux, mpq_feed); - ret = mpq_dmx_decoder_eos_cmd(mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 1); if (ret) MPQ_DVB_ERR_PRINT( "%s: Couldn't write oob eos packet\n", __func__); + } else if (dvb_dmx_is_audio_feed(feed)) { + mpq_dmx_decoder_audio_pes_closure(mpq_demux, + mpq_feed); + ret = mpq_dmx_decoder_eos_cmd(mpq_feed, 2); + if (ret) + MPQ_DVB_ERR_PRINT( + "%s: Couldn't write oob eos packet\n", + __func__); } ret = feed->data_ready_cb.ts(&feed->feed.ts, &event); } else if (!mpq_demux->sdmx_eos) { diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h index f36e9e7e7a23..ca6372b7d152 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -44,12 +44,20 @@ #define VIDEO_META_DATA_BUFFER_SIZE \ (VIDEO_NUM_OF_PES_PACKETS * VIDEO_META_DATA_PACKET_SIZE) +#define AUDIO_NUM_OF_PES_PACKETS 100 + +#define AUDIO_META_DATA_PACKET_SIZE \ + (DVB_RINGBUFFER_PKTHDRSIZE + \ + sizeof(struct mpq_streambuffer_packet_header) + \ + sizeof(struct mpq_adapter_audio_meta_data)) + +#define AUDIO_META_DATA_BUFFER_SIZE \ + (AUDIO_NUM_OF_PES_PACKETS * AUDIO_META_DATA_PACKET_SIZE) + /* Max number open() request can be done on demux device */ #define MPQ_MAX_DMX_FILES 128 -/** - * TSIF alias name length - */ +/* TSIF alias name length */ #define TSIF_NAME_LENGTH 20 /** @@ -332,6 +340,30 @@ const struct dvb_dmx_video_patterns *patterns[DVB_DMX_MAX_SEARCH_PATTERN_NUM]; u64 prev_stc; }; +/* require a bare minimal mpq_audio_feed_info struct */ +struct mpq_audio_feed_info { + struct mpq_streambuffer *audio_buffer; + spinlock_t audio_buffer_lock; + struct mpq_decoder_buffers_desc buffer_desc; + struct pes_packet_header pes_header; + u32 pes_header_left_bytes; + u32 pes_header_offset; + int fullness_wait_cancel; + enum mpq_adapter_stream_if stream_interface; + u32 frame_offset; /* pes frame offset */ + struct dmx_pts_dts_info saved_pts_dts_info; + struct dmx_pts_dts_info new_pts_dts_info; + int saved_info_used; + int new_info_exists; + int first_pts_dts_copy; + u32 tei_errs; + int last_continuity; + u32 continuity_errs; + u32 ts_packets_num; + u32 ts_dropped_bytes; + u64 prev_stc; +}; + /** * mpq feed object - mpq common plugin feed information * @@ -367,6 +399,7 @@ struct mpq_feed { struct ion_handle *sdmx_buf_handle; struct mpq_video_feed_info video_info; + struct mpq_audio_feed_info audio_info; }; /** @@ -509,6 +542,7 @@ struct mpq_demux { enum sdmx_log_level sdmx_log_level; struct timespec last_notification_time; + int ts_packet_timestamp_source; }; /** @@ -879,11 +913,12 @@ struct dvb_demux_feed *mpq_dmx_peer_rec_feed(struct dvb_demux_feed *feed); /** * mpq_dmx_decoder_eos_cmd() - Report EOS event to the mpq_streambuffer * - * @mpq_feed: Video mpq_feed object for notification + * @mpq_feed: Audio/Video mpq_feed object for notification + * @feed_type: Feed type( Audio or Video ) * * Return error code */ -int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed); +int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed, int feed_type); /** * mpq_dmx_parse_mandatory_pes_header() - Parse non-optional PES header fields @@ -1023,5 +1058,66 @@ int mpq_dmx_get_param_scramble_even(void); /* Return the common module parameter mpq_sdmx_scramble_default_discard */ int mpq_dmx_get_param_scramble_default_discard(void); +/* APIs for Audio stream buffers interface -- Added for broadcase use case */ +/* + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ +struct mpq_streambuffer *consumer_audio_streambuffer(int dmx_ts_pes_audio); +struct mpq_streambuffer *consumer_video_streambuffer(int dmx_ts_pes_video); + +int mpq_dmx_init_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_terminate_audio_feed(struct mpq_feed *mpq_feed); + +int mpq_dmx_parse_remaining_audio_pes_header( + struct dvb_demux_feed *feed, + struct mpq_audio_feed_info *feed_data, + struct pes_packet_header *pes_header, + const u8 *buf, + u32 *ts_payload_offset, + int *bytes_avail); + +static inline void mpq_dmx_save_audio_pts_dts( + struct mpq_audio_feed_info *feed_data) +{ + if (feed_data->new_info_exists) { + feed_data->saved_pts_dts_info.pts_exist = + feed_data->new_pts_dts_info.pts_exist; + feed_data->saved_pts_dts_info.pts = + feed_data->new_pts_dts_info.pts; + feed_data->saved_pts_dts_info.dts_exist = + feed_data->new_pts_dts_info.dts_exist; + feed_data->saved_pts_dts_info.dts = + feed_data->new_pts_dts_info.dts; + + feed_data->new_info_exists = 0; + feed_data->saved_info_used = 0; + } +} + +/* + * mpq_dmx_process_audio_packet - Assemble Audio PES data and output to + * stream buffer connected to decoder. + */ +int mpq_dmx_process_audio_packet(struct dvb_demux_feed *feed, const u8 *buf); + +static inline void mpq_dmx_write_audio_pts_dts( + struct mpq_audio_feed_info *feed_data, + struct dmx_pts_dts_info *info) +{ + if (!feed_data->saved_info_used) { + info->pts_exist = feed_data->saved_pts_dts_info.pts_exist; + info->pts = feed_data->saved_pts_dts_info.pts; + info->dts_exist = feed_data->saved_pts_dts_info.dts_exist; + info->dts = feed_data->saved_pts_dts_info.dts; + + feed_data->saved_info_used = 1; + } else { + info->pts_exist = 0; + info->dts_exist = 0; + } +} #endif /* _MPQ_DMX_PLUGIN_COMMON_H */ diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c index be88bc1bf19f..4f4b9170d639 100644 --- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c +++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1634,6 +1634,9 @@ static int mpq_tspp_dmx_write_to_decoder( if (dvb_dmx_is_video_feed(feed)) return mpq_dmx_process_video_packet(feed, buf); + if (dvb_dmx_is_audio_feed(feed)) + return mpq_dmx_process_audio_packet(feed, buf); + if (dvb_dmx_is_pcr_feed(feed)) return mpq_dmx_process_pcr_packet(feed, buf); @@ -1663,7 +1666,7 @@ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING | - DMX_CAP_AUTO_BUFFER_FLUSH; + DMX_CAP_AUDIO_DECODER_DATA | DMX_CAP_AUTO_BUFFER_FLUSH; caps->recording_max_video_pids_indexed = 0; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; @@ -1753,6 +1756,8 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, { enum tspp_source source; u32 tcr_counter; + u64 avtimer_stc = 0; + int tts_source = 0; if (!demux || !stc || !base) return -EINVAL; @@ -1764,11 +1769,18 @@ static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num, else return -EINVAL; - tspp_get_ref_clk_counter(0, source, &tcr_counter); - - *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ - *base = 300; /* divisor to get 90KHz clock from stc value */ + if (tspp_get_tts_source(0, &tts_source) < 0) + tts_source = TSIF_TTS_TCR; + if (tts_source != TSIF_TTS_LPASS_TIMER) { + tspp_get_ref_clk_counter(0, source, &tcr_counter); + *stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */ + *base = 300; /* divisor to get 90KHz clock from stc value */ + } else { + if (tspp_get_lpass_time_counter(0, source, &avtimer_stc) < 0) + return -EINVAL; + *stc = avtimer_stc; + } return 0; } @@ -1840,6 +1852,10 @@ static int mpq_tspp_dmx_init( /* Extend dvb-demux debugfs with TSPP statistics. */ mpq_dmx_init_debugfs_entries(mpq_demux); + /* Get the TSIF TTS info */ + if (tspp_get_tts_source(0, &mpq_demux->ts_packet_timestamp_source) < 0) + mpq_demux->ts_packet_timestamp_source = TSIF_TTS_TCR; + return 0; init_failed_dmx_release: diff --git a/drivers/media/platform/msm/dvb/include/mpq_adapter.h b/drivers/media/platform/msm/dvb/include/mpq_adapter.h index 54e671c3b38d..c55a5aa1ae32 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_adapter.h +++ b/drivers/media/platform/msm/dvb/include/mpq_adapter.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,12 +27,24 @@ enum mpq_adapter_stream_if { /** Interface holding stream-buffer for video1 stream */ MPQ_ADAPTER_VIDEO1_STREAM_IF = 1, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video2 stream */ MPQ_ADAPTER_VIDEO2_STREAM_IF = 2, - /** Interface holding stream-buffer for video1 stream */ + /** Interface holding stream-buffer for video3 stream */ MPQ_ADAPTER_VIDEO3_STREAM_IF = 3, + /** Interface holding stream-buffer for audio0 stream */ + MPQ_ADAPTER_AUDIO0_STREAM_IF = 4, + + /** Interface holding stream-buffer for audio1 stream */ + MPQ_ADAPTER_AUDIO1_STREAM_IF = 5, + + /** Interface holding stream-buffer for audio2 stream */ + MPQ_ADAPTER_AUDIO2_STREAM_IF = 6, + + /** Interface holding stream-buffer for audio3 stream */ + MPQ_ADAPTER_AUDIO3_STREAM_IF = 7, + /** Maximum number of interfaces holding stream-buffers */ MPQ_ADAPTER_MAX_NUM_OF_INTERFACES, }; @@ -113,6 +125,17 @@ struct mpq_adapter_video_meta_data { } info; } __packed; +/** The meta-data used for audio interface */ +struct mpq_adapter_audio_meta_data { + /** meta-data packet type */ + enum dmx_packet_type packet_type; + + /** packet-type specific information */ + union { + struct dmx_pes_packet_info pes; + struct dmx_marker_info marker; + } info; +} __packed; /** Callback function to notify on registrations of specific interfaces */ typedef void (*mpq_adapter_stream_if_callback)( diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h index b24dc1f3b5ff..62404513007a 100644 --- a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h +++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,7 +15,6 @@ #include "dvb_ringbuffer.h" - /** * DOC: MPQ Stream Buffer * @@ -459,4 +458,37 @@ ssize_t mpq_streambuffer_metadata_free(struct mpq_streambuffer *sbuff); */ int mpq_streambuffer_flush(struct mpq_streambuffer *sbuff); +/* + * ------------------------------------------------------ + * Consumer or AV Decoder Stream Interface to Ring Buffer + * ------------------------------------------------------ + * Producer is Demux Driver + * ------------------------ + * + * call from Audio/Video Decoder Driver to find Audio/Video + * streambuffer AV handles, "DMX_PES_AUDIO0 through 3" or + * DMX_PES_VIDEO0 through 3" interfaces corresponding to 4 programs. + */ + +/* call from Audio/Video Decoder Driver via POLLING to consume + * Headers and Compressed data from ring buffer using streambuffer handle. + * hdrdata[] and cdata[] buffers have to be malloc'd by consumer + * + * -------------------------- + * Consumer Calling Sequence + * -------------------------- + * Find the streambuffer corresponding to a DMX TS PES stream instance. + * 1. consumer_audio_streambuffer() or consumer_video_streambuffer() + * Process the packet headers if required. + * 2. mpq_read_new_packet_hdr_data() + * Process the compressed data by forwarding to AV decoder. + * 3. mpq_read_new_packet_compressed_data() + * Dispose the packet. + * 4. mpq_dispose_new_packet_read() + * + * The Audio/Video drivers (or consumers) require the stream_buffer information + * for consuming packet headers and compressed AV data from the + * ring buffer filled by demux driver which is the producer + */ + #endif /* _MPQ_STREAM_BUFFER_H */ diff --git a/include/linux/qcom_tspp.h b/include/linux/qcom_tspp.h index 28e6695fb057..1b34c389d7f0 100644 --- a/include/linux/qcom_tspp.h +++ b/include/linux/qcom_tspp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -70,6 +70,11 @@ struct tspp_select_source { int enable_inverse; }; +enum tsif_tts_source { + TSIF_TTS_TCR = 0, /* Time stamps from TCR counter */ + TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */ +}; + typedef void (tspp_notifier)(int channel_id, void *user); typedef void* (tspp_allocator)(int channel_id, u32 size, phys_addr_t *phys_base, void *user); @@ -96,4 +101,8 @@ int tspp_allocate_buffers(u32 dev, u32 channel_id, u32 count, u32 size, u32 int_freq, tspp_allocator *alloc, tspp_memfree *memfree, void *user); +int tspp_get_tts_source(u32 dev, int *tts_source); +int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source, + u64 *lpass_time_counter); + #endif /* _MSM_TSPP_H_ */ diff --git a/include/uapi/linux/dvb/dmx.h b/include/uapi/linux/dvb/dmx.h index a768696c90f8..175534a26792 100644 --- a/include/uapi/linux/dvb/dmx.h +++ b/include/uapi/linux/dvb/dmx.h @@ -148,6 +148,9 @@ enum dmx_video_codec { #define DMX_IDX_VC1_FRAME_END 0x02000000 #define DMX_IDX_H264_ACCESS_UNIT_DEL 0x04000000 #define DMX_IDX_H264_SEI 0x08000000 +#define DMX_IDX_H264_IDR_ISLICE_START 0x10000000 +#define DMX_IDX_H264_NON_IDR_PSLICE_START 0x20000000 +#define DMX_IDX_H264_NON_IDR_BSLICE_START 0x40000000 struct dmx_pes_filter_params { -- cgit v1.2.3