blob: 91e93c28f99777b952ec14fc7ade70624d09a39e [file] [log] [blame]
/* Copyright (c) 2012, Code Aurora Forum. 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/file.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
/* Length of mandatory fields that must exist in header of video PES */
#define PES_MANDATORY_FIELDS_LEN 9
/*
* 500 PES header packets in the meta-data buffer,
* should be more than enough
*/
#define VIDEO_NUM_OF_PES_PACKETS 500
#define VIDEO_META_DATA_BUFFER_SIZE \
(VIDEO_NUM_OF_PES_PACKETS * \
(DVB_RINGBUFFER_PKTHDRSIZE + \
sizeof(struct mpq_streambuffer_packet_header) + \
sizeof(struct mpq_adapter_video_meta_data)))
/* Number of demux devices, has default of linux configuration */
static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
module_param(mpq_demux_device_num, int, S_IRUGO);
/**
* Maximum allowed framing pattern size
*/
#define MPQ_MAX_PATTERN_SIZE 6
/**
* Number of patterns to look for when doing framing, per video standard
*/
#define MPQ_MPEG2_PATTERN_NUM 5
#define MPQ_H264_PATTERN_NUM 5
#define MPQ_VC1_PATTERN_NUM 3
/*
* mpq_framing_pattern_lookup_params - framing pattern lookup parameters.
*
* @pattern: the byte pattern to look for.
* @mask: the byte mask to use (same length as pattern).
* @size: the length of the pattern, in bytes.
* @type: the type of the pattern.
*/
struct mpq_framing_pattern_lookup_params {
u8 pattern[MPQ_MAX_PATTERN_SIZE];
u8 mask[MPQ_MAX_PATTERN_SIZE];
size_t size;
enum dmx_framing_pattern_type type;
};
/*
* Pre-defined video framing lookup pattern information.
* Note: the first pattern in each patterns database must
* be the Sequence Header (or equivalent SPS in H.264).
* The code assumes this is the case when prepending
* Sequence Header data in case it is required.
*/
static const struct mpq_framing_pattern_lookup_params
mpeg2_patterns[MPQ_MPEG2_PATTERN_NUM] = {
{{0x00, 0x00, 0x01, 0xB3}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
DMX_FRM_MPEG2_SEQUENCE_HEADER},
{{0x00, 0x00, 0x01, 0xB8}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
DMX_FRM_MPEG2_GOP_HEADER},
{{0x00, 0x00, 0x01, 0x00, 0x00, 0x08},
{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
DMX_FRM_MPEG2_I_PIC},
{{0x00, 0x00, 0x01, 0x00, 0x00, 0x10},
{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
DMX_FRM_MPEG2_P_PIC},
{{0x00, 0x00, 0x01, 0x00, 0x00, 0x18},
{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x38}, 6,
DMX_FRM_MPEG2_B_PIC}
};
static const struct mpq_framing_pattern_lookup_params
h264_patterns[MPQ_H264_PATTERN_NUM] = {
{{0x00, 0x00, 0x01, 0x07}, {0xFF, 0xFF, 0xFF, 0x1F}, 4,
DMX_FRM_H264_SPS},
{{0x00, 0x00, 0x01, 0x08}, {0xFF, 0xFF, 0xFF, 0x1F}, 4,
DMX_FRM_H264_PPS},
{{0x00, 0x00, 0x01, 0x05, 0x80}, {0xFF, 0xFF, 0xFF, 0x1F, 0x80}, 5,
DMX_FRM_H264_IDR_PIC},
{{0x00, 0x00, 0x01, 0x01, 0x80}, {0xFF, 0xFF, 0xFF, 0x1F, 0x80}, 5,
DMX_FRM_H264_NON_IDR_PIC}
};
static const struct mpq_framing_pattern_lookup_params
vc1_patterns[MPQ_VC1_PATTERN_NUM] = {
{{0x00, 0x00, 0x01, 0x0F}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
DMX_FRM_VC1_SEQUENCE_HEADER},
{{0x00, 0x00, 0x01, 0x0E}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
DMX_FRM_VC1_ENTRY_POINT_HEADER},
{{0x00, 0x00, 0x01, 0x0D}, {0xFF, 0xFF, 0xFF, 0xFF}, 4,
DMX_FRM_VC1_FRAME_START_CODE}
};
/* Global data-structure for managing demux devices */
static struct
{
/* ION demux client used for memory allocation */
struct ion_client *ion_client;
/* demux devices array */
struct mpq_demux *devices;
/* Stream buffers objects used for tunneling to decoders */
struct mpq_streambuffer
decoder_buffers[MPQ_ADAPTER_MAX_NUM_OF_INTERFACES];
/*
* Indicates whether the video decoder handles framing
* or we are required to provide framing information
* in the meta-data passed to the decoder.
*/
int decoder_framing;
} mpq_dmx_info;
/* 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)
{
/* 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;
/* stream_id is video? */
if ((pes_header->stream_id & 0xF0) != 0xE0)
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_indexing_video_standard standard,
enum dmx_framing_pattern_type pattern_type)
{
switch (standard) {
case DMX_INDEXING_MPEG2:
if ((pattern_type == DMX_FRM_MPEG2_I_PIC) ||
(pattern_type == DMX_FRM_MPEG2_P_PIC) ||
(pattern_type == DMX_FRM_MPEG2_B_PIC))
return 1;
return 0;
case DMX_INDEXING_H264:
if ((pattern_type == DMX_FRM_H264_IDR_PIC) ||
(pattern_type == DMX_FRM_H264_NON_IDR_PIC))
return 1;
return 0;
case DMX_INDEXING_VC1:
if (pattern_type == DMX_FRM_VC1_FRAME_START_CODE)
return 1;
return 0;
default:
return -EINVAL;
}
}
/*
* mpq_framing_pattern_lookup_results - framing lookup results
*
* @offset: The offset in the buffer where the pattern was found.
* If a pattern is found using a prefix (i.e. started on the
* previous buffer), offset is zero.
* @type: the type of the pattern found.
* @used_prefix_size: the prefix size that was used to find this pattern
*/
struct mpq_framing_pattern_lookup_results {
struct {
u32 offset;
enum dmx_framing_pattern_type type;
u32 used_prefix_size;
} info[MPQ_MAX_FOUND_PATTERNS];
};
/*
* Check if two patterns are identical, taking mask into consideration.
* @pattern1: the first byte pattern to compare.
* @pattern2: the second byte pattern to compare.
* @mask: the bit mask to use.
* @pattern_size: the length of both patterns and the mask, in bytes.
*
* Return: 1 if patterns match, 0 otherwise.
*/
static inline int mpq_dmx_patterns_match(const u8 *pattern1, const u8 *pattern2,
const u8 *mask, size_t pattern_size)
{
int i;
/*
* Assumption: it is OK to access pattern1, pattern2 and mask.
* This function performs no sanity checks to keep things fast.
*/
for (i = 0; i < pattern_size; i++)
if ((pattern1[i] & mask[i]) != (pattern2[i] & mask[i]))
return 0;
return 1;
}
/*
* mpq_dmx_framing_pattern_search -
* search for framing patterns in a given buffer.
*
* Optimized version: first search for a common substring, e.g. 0x00 0x00 0x01.
* If this string is found, go over all the given patterns (all must start
* with this string) and search for their ending in the buffer.
*
* Assumption: the patterns we look for do not spread over more than two
* buffers.
*
* @paterns: the full patterns information to look for.
* @patterns_num: the number of patterns to look for.
* @buf: the buffer to search.
* @buf_size: the size of the buffer to search. we search the entire buffer.
* @prefix_size_masks: a bit mask (per pattern) of possible prefix sizes to use
* when searching for a pattern that started at the last buffer.
* Updated in this function for use in the next lookup.
* @results: lookup results (offset, type, used_prefix_size) per found pattern,
* up to MPQ_MAX_FOUND_PATTERNS.
*
* Return:
* Number of patterns found (up to MPQ_MAX_FOUND_PATTERNS).
* 0 if pattern was not found.
* Negative error value on failure.
*/
static int mpq_dmx_framing_pattern_search(
const struct mpq_framing_pattern_lookup_params *patterns,
int patterns_num,
const u8 *buf,
size_t buf_size,
struct mpq_framing_prefix_size_masks *prefix_size_masks,
struct mpq_framing_pattern_lookup_results *results)
{
int i, j;
unsigned int current_size;
u32 prefix;
int found = 0;
int start_offset = 0;
/* the starting common substring to look for */
u8 string[] = {0x00, 0x00, 0x01};
/* the mask for the starting string */
u8 string_mask[] = {0xFF, 0xFF, 0xFF};
/* the size of the starting string (in bytes) */
size_t string_size = 3;
/* sanity checks - can be commented out for optimization purposes */
if ((patterns == NULL) || (patterns_num <= 0) || (buf == NULL)) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
memset(results, 0, sizeof(struct mpq_framing_pattern_lookup_results));
/*
* handle prefix - disregard string, simply check all patterns,
* looking for a matching suffix at the very beginning of the buffer.
*/
for (j = 0; (j < patterns_num) && !found; j++) {
prefix = prefix_size_masks->size_mask[j];
current_size = 32;
while (prefix) {
if (prefix & (0x1 << (current_size - 1))) {
/*
* check that we don't look further
* than buf_size boundary
*/
if ((int)(patterns[j].size - current_size) >
buf_size)
break;
if (mpq_dmx_patterns_match(
(patterns[j].pattern + current_size),
buf, (patterns[j].mask + current_size),
(patterns[j].size - current_size))) {
MPQ_DVB_DBG_PRINT(
"%s: Found matching pattern"
"using prefix of size %d\n",
__func__, current_size);
/*
* pattern found using prefix at the
* very beginning of the buffer, so
* offset is 0, but we already zeroed
* everything in the beginning of the
* function. that's why the next line
* is commented.
*/
/* results->info[found].offset = 0; */
results->info[found].type =
patterns[j].type;
results->info[found].used_prefix_size =
current_size;
found++;
/*
* save offset to start looking from
* in the buffer, to avoid reusing the
* data of a pattern we already found.
*/
start_offset = (patterns[j].size -
current_size);
if (found >= MPQ_MAX_FOUND_PATTERNS)
goto next_prefix_lookup;
/*
* we don't want to search for the same
* pattern with several possible prefix
* sizes if we have already found it,
* so we break from the inner loop.
* since we incremented 'found', we
* will not search for additional
* patterns using a prefix - that would
* imply ambiguous patterns where one
* pattern can be included in another.
* the for loop will exit.
*/
break;
}
}
current_size--;
prefix &= ~(0x1 << (current_size - 1));
}
}
/*
* Search buffer for entire pattern, starting with the string.
* Note the external for loop does not execute if buf_size is
* smaller than string_size (the cast to int is required, since
* size_t is unsigned).
*/
for (i = start_offset; i < (int)(buf_size - string_size + 1); i++) {
if (mpq_dmx_patterns_match(string, (buf + i), string_mask,
string_size)) {
/* now search for patterns: */
for (j = 0; j < patterns_num; j++) {
/* avoid overflow to next buffer */
if ((i + patterns[j].size) > buf_size)
continue;
if (mpq_dmx_patterns_match(
(patterns[j].pattern + string_size),
(buf + i + string_size),
(patterns[j].mask + string_size),
(patterns[j].size - string_size))) {
results->info[found].offset = i;
results->info[found].type =
patterns[j].type;
/*
* save offset to start next prefix
* lookup, to avoid reusing the data
* of any pattern we already found.
*/
if ((i + patterns[j].size) >
start_offset)
start_offset = (i +
patterns[j].size);
/*
* did not use a prefix to find this
* pattern, but we zeroed everything
* in the beginning of the function.
* So no need to zero used_prefix_size
* for results->info[found]
*/
found++;
if (found >= MPQ_MAX_FOUND_PATTERNS)
goto next_prefix_lookup;
/*
* theoretically we don't have to break
* here, but we don't want to search
* for the other matching patterns on
* the very same same place in the
* buffer. That would mean the
* (pattern & mask) combinations are
* not unique. So we break from inner
* loop and move on to the next place
* in the buffer.
*/
break;
}
}
}
}
next_prefix_lookup:
/* check for possible prefix sizes for the next buffer */
for (j = 0; j < patterns_num; j++) {
prefix_size_masks->size_mask[j] = 0;
for (i = 1; i < patterns[j].size; i++) {
/*
* avoid looking outside of the buffer
* or reusing previously used data.
*/
if (i > (buf_size - start_offset))
break;
if (mpq_dmx_patterns_match(patterns[j].pattern,
(buf + buf_size - i),
patterns[j].mask, i)) {
prefix_size_masks->size_mask[j] |=
(1 << (i - 1));
}
}
}
return found;
}
/*
* mpq_dmx_get_pattern_params -
* get a pointer to the relevant pattern parameters structure,
* based on the video parameters.
*
* @video_params: the video parameters (e.g. video standard).
* @patterns: a pointer to a pointer to the pattern parameters,
* updated by this function.
* @patterns_num: number of patterns, updated by this function.
*/
static inline int mpq_dmx_get_pattern_params(
struct dmx_indexing_video_params *video_params,
const struct mpq_framing_pattern_lookup_params **patterns,
int *patterns_num)
{
switch (video_params->standard) {
case DMX_INDEXING_MPEG2:
*patterns = mpeg2_patterns;
*patterns_num = MPQ_MPEG2_PATTERN_NUM;
break;
case DMX_INDEXING_H264:
*patterns = h264_patterns;
*patterns_num = MPQ_H264_PATTERN_NUM;
break;
case DMX_INDEXING_VC1:
*patterns = vc1_patterns;
*patterns_num = MPQ_VC1_PATTERN_NUM;
break;
default:
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
*patterns = NULL;
*patterns_num = 0;
return -EINVAL;
}
return 0;
}
/* Extend dvb-demux debugfs with HW statistics */
void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
{
/*
* Extend dvb-demux debugfs with HW statistics.
* Note that destruction of debugfs directory is done
* when dvb-demux is terminated.
*/
mpq_demux->hw_notification_count = 0;
mpq_demux->hw_notification_interval = 0;
mpq_demux->hw_notification_size = 0;
mpq_demux->decoder_tsp_drop_count = 0;
mpq_demux->hw_notification_min_size = 0xFFFFFFFF;
if (mpq_demux->demux.dmx.debugfs_demux_dir != NULL) {
debugfs_create_u32(
"hw_notification_interval",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_interval);
debugfs_create_u32(
"hw_notification_min_interval",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_min_interval);
debugfs_create_u32(
"hw_notification_count",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_count);
debugfs_create_u32(
"hw_notification_size",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_size);
debugfs_create_u32(
"hw_notification_min_size",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->hw_notification_min_size);
debugfs_create_u32(
"decoder_tsp_drop_count",
S_IRUGO|S_IWUGO,
mpq_demux->demux.dmx.debugfs_demux_dir,
&mpq_demux->decoder_tsp_drop_count);
}
}
EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
/* Update dvb-demux debugfs with HW notification statistics */
void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
{
struct timespec curr_time, delta_time;
u64 delta_time_ms;
curr_time = current_kernel_time();
if (likely(mpq_demux->hw_notification_count)) {
/* calculate time-delta between notifications */
delta_time =
timespec_sub(
curr_time,
mpq_demux->last_notification_time);
delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
delta_time.tv_nsec / NSEC_PER_MSEC;
mpq_demux->hw_notification_interval = delta_time_ms;
if ((mpq_demux->hw_notification_count == 1) ||
(mpq_demux->hw_notification_interval &&
mpq_demux->hw_notification_interval <
mpq_demux->hw_notification_min_interval))
mpq_demux->hw_notification_min_interval =
mpq_demux->hw_notification_interval;
}
mpq_demux->hw_notification_count++;
mpq_demux->last_notification_time = curr_time;
}
EXPORT_SYMBOL(mpq_dmx_update_hw_statistics);
int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
{
int i;
int result;
struct mpq_demux *mpq_demux;
struct dvb_adapter *mpq_adapter;
MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
__func__,
mpq_demux_device_num);
mpq_adapter = mpq_adapter_get();
if (mpq_adapter == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_adapter is not valid\n",
__func__);
result = -EPERM;
goto init_failed;
}
if (mpq_demux_device_num == 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_demux_device_num set to 0\n",
__func__);
result = -EPERM;
goto init_failed;
}
mpq_dmx_info.devices = NULL;
mpq_dmx_info.ion_client = NULL;
/*
* TODO: the following should be set based on the decoder:
* 0 means the decoder doesn't handle framing, so framing
* is done by demux. 1 means the decoder handles framing.
*/
mpq_dmx_info.decoder_framing = 0;
/* Allocate memory for all MPQ devices */
mpq_dmx_info.devices =
vmalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
if (!mpq_dmx_info.devices) {
MPQ_DVB_ERR_PRINT(
"%s: failed to allocate devices memory\n",
__func__);
result = -ENOMEM;
goto init_failed;
}
/* Zero allocated memory */
memset(mpq_dmx_info.devices,
0,
mpq_demux_device_num*sizeof(struct mpq_demux));
/*
* Create a new ION client used by demux to allocate memory
* for decoder's buffers.
*/
mpq_dmx_info.ion_client =
msm_ion_client_create(UINT_MAX, "demux_client");
if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
MPQ_DVB_ERR_PRINT(
"%s: msm_ion_client_create\n",
__func__);
result = PTR_ERR(mpq_dmx_info.ion_client);
if (!result)
result = -ENOMEM;
mpq_dmx_info.ion_client = NULL;
goto init_failed_free_demux_devices;
}
/* Initialize and register all demux devices to the system */
for (i = 0; i < mpq_demux_device_num; i++) {
mpq_demux = mpq_dmx_info.devices+i;
/* initialize demux source to memory by default */
mpq_demux->source = DMX_SOURCE_DVR0 + i;
/*
* Give the plugin pointer to the ion client so
* that it can allocate memory from ION if it requires so
*/
mpq_demux->ion_client = mpq_dmx_info.ion_client;
spin_lock_init(&mpq_demux->feed_lock);
/*
* mpq_demux_plugin_hw_init should be implemented
* by the specific plugin
*/
result = dmx_init_func(mpq_adapter, mpq_demux);
if (result < 0) {
MPQ_DVB_ERR_PRINT(
"%s: dmx_init_func (errno=%d)\n",
__func__,
result);
goto init_failed_free_demux_devices;
}
mpq_demux->is_initialized = 1;
/*
* Add capability of receiving input from memory.
* Every demux in our system may be connected to memory input,
* or any live input.
*/
mpq_demux->fe_memory.source = DMX_MEMORY_FE;
result =
mpq_demux->demux.dmx.add_frontend(
&mpq_demux->demux.dmx,
&mpq_demux->fe_memory);
if (result < 0) {
MPQ_DVB_ERR_PRINT(
"%s: add_frontend (mem) failed (errno=%d)\n",
__func__,
result);
goto init_failed_free_demux_devices;
}
}
return 0;
init_failed_free_demux_devices:
mpq_dmx_plugin_exit();
init_failed:
return result;
}
EXPORT_SYMBOL(mpq_dmx_plugin_init);
void mpq_dmx_plugin_exit(void)
{
int i;
struct mpq_demux *mpq_demux;
MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
if (mpq_dmx_info.ion_client != NULL) {
ion_client_destroy(mpq_dmx_info.ion_client);
mpq_dmx_info.ion_client = NULL;
}
if (mpq_dmx_info.devices != NULL) {
for (i = 0; i < mpq_demux_device_num; i++) {
mpq_demux = mpq_dmx_info.devices + i;
if (mpq_demux->is_initialized) {
mpq_demux->demux.dmx.remove_frontend(
&mpq_demux->demux.dmx,
&mpq_demux->fe_memory);
dvb_dmxdev_release(&mpq_demux->dmxdev);
dvb_dmx_release(&mpq_demux->demux);
}
}
vfree(mpq_dmx_info.devices);
mpq_dmx_info.devices = NULL;
}
}
EXPORT_SYMBOL(mpq_dmx_plugin_exit);
int mpq_dmx_set_source(
struct dmx_demux *demux,
const dmx_source_t *src)
{
int i;
int dvr_index;
int dmx_index;
struct dvb_demux *dvb_demux = demux->priv;
struct mpq_demux *mpq_demux;
if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL)) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
mpq_demux = dvb_demux->priv;
if (mpq_demux == NULL) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
/*
* For dvr sources,
* verify that this source is connected to the respective demux
*/
dmx_index = mpq_demux - mpq_dmx_info.devices;
if (*src >= DMX_SOURCE_DVR0) {
dvr_index = *src - DMX_SOURCE_DVR0;
if (dvr_index != dmx_index) {
MPQ_DVB_ERR_PRINT(
"%s: can't connect demux%d to dvr%d\n",
__func__,
dmx_index,
dvr_index);
return -EINVAL;
}
}
/*
* For front-end sources,
* verify that this source is not already set to different demux
*/
for (i = 0; i < mpq_demux_device_num; i++) {
if ((&mpq_dmx_info.devices[i] != mpq_demux) &&
(mpq_dmx_info.devices[i].source == *src)) {
MPQ_DVB_ERR_PRINT(
"%s: demux%d source can't be set,\n"
"demux%d occupies this source already\n",
__func__,
dmx_index,
i);
return -EBUSY;
}
}
mpq_demux->source = *src;
return 0;
}
EXPORT_SYMBOL(mpq_dmx_set_source);
int mpq_dmx_map_buffer(struct dmx_demux *demux, struct dmx_buffer *dmx_buffer,
void **priv_handle, void **kernel_mem)
{
struct dvb_demux *dvb_demux = demux->priv;
struct mpq_demux *mpq_demux;
struct ion_handle *ion_handle;
unsigned long ionflag = 0;
int ret;
if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
(priv_handle == NULL) || (kernel_mem == NULL)) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
mpq_demux = dvb_demux->priv;
if (mpq_demux == NULL) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
ion_handle = ion_import_dma_buf(mpq_demux->ion_client,
dmx_buffer->handle);
if (IS_ERR_OR_NULL(ion_handle)) {
ret = PTR_ERR(ion_handle);
if (!ret)
ret = -ENOMEM;
MPQ_DVB_ERR_PRINT("%s: ion_import_dma_buf failed %d\n",
__func__, ret);
goto map_buffer_failed;
}
ret = ion_handle_get_flags(mpq_demux->ion_client, ion_handle, &ionflag);
if (ret) {
MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
__func__, ret);
goto map_buffer_failed_free_buff;
}
if (ionflag & ION_SECURE) {
MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
/* TBD: Set buffer as secured */
*kernel_mem = NULL;
} else {
*kernel_mem = ion_map_kernel(mpq_demux->ion_client,
ion_handle);
if (*kernel_mem == NULL) {
MPQ_DVB_ERR_PRINT("%s: ion_map_kernel failed\n",
__func__);
ret = -ENOMEM;
goto map_buffer_failed_free_buff;
}
}
*priv_handle = (void *)ion_handle;
return 0;
map_buffer_failed_free_buff:
ion_free(mpq_demux->ion_client, ion_handle);
map_buffer_failed:
return ret;
}
EXPORT_SYMBOL(mpq_dmx_map_buffer);
int mpq_dmx_unmap_buffer(struct dmx_demux *demux,
void *priv_handle)
{
struct dvb_demux *dvb_demux = demux->priv;
struct ion_handle *ion_handle = priv_handle;
struct mpq_demux *mpq_demux;
unsigned long ionflag = 0;
int ret;
if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
(priv_handle == NULL)) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
mpq_demux = dvb_demux->priv;
if (mpq_demux == NULL) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
ret = ion_handle_get_flags(mpq_demux->ion_client, ion_handle, &ionflag);
if (ret) {
MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
__func__, ret);
return -EINVAL;
}
if (!(ionflag & ION_SECURE))
ion_unmap_kernel(mpq_demux->ion_client, ion_handle);
ion_free(mpq_demux->ion_client, ion_handle);
return 0;
}
EXPORT_SYMBOL(mpq_dmx_unmap_buffer);
int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
{
int ret;
void *packet_buffer;
void *payload_buffer;
struct mpq_video_feed_info *feed_data;
struct mpq_demux *mpq_demux = feed->demux->priv;
struct mpq_streambuffer *stream_buffer;
int actual_buffer_size;
/* Allocate memory for private feed data */
feed_data = vmalloc(sizeof(struct mpq_video_feed_info));
if (feed_data == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to allocate private video feed data\n",
__func__);
ret = -ENOMEM;
goto init_failed;
}
/* get and store framing information if required */
if (!mpq_dmx_info.decoder_framing) {
mpq_dmx_get_pattern_params(&feed->indexing_params,
&feed_data->patterns, &feed_data->patterns_num);
if (feed_data->patterns == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to get framing pattern parameters\n",
__func__);
ret = -EINVAL;
goto init_failed_free_priv_data;
}
}
/* Allocate packet buffer holding the meta-data */
packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
if (packet_buffer == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to allocate packets buffer\n",
__func__);
ret = -ENOMEM;
goto init_failed_free_priv_data;
}
/*
* Allocate payload buffer through ION.
* TODO: for scrambling support, need to check if the
* stream is scrambled and allocate the buffer with secure
* flag set.
*/
actual_buffer_size = feed->buffer_size;
actual_buffer_size += (SZ_4K - 1);
actual_buffer_size &= ~(SZ_4K - 1);
feed_data->payload_buff_handle =
ion_alloc(mpq_demux->ion_client,
actual_buffer_size,
SZ_4K,
ION_HEAP(ION_CP_MM_HEAP_ID),
ION_FLAG_CACHED);
if (IS_ERR_OR_NULL(feed_data->payload_buff_handle)) {
ret = PTR_ERR(feed_data->payload_buff_handle);
MPQ_DVB_ERR_PRINT(
"%s: FAILED to allocate payload buffer %d\n",
__func__, ret);
if (!ret)
ret = -ENOMEM;
goto init_failed_free_packet_buffer;
}
payload_buffer =
ion_map_kernel(mpq_demux->ion_client,
feed_data->payload_buff_handle);
if (IS_ERR_OR_NULL(payload_buffer)) {
ret = PTR_ERR(payload_buffer);
MPQ_DVB_ERR_PRINT(
"%s: FAILED to map payload buffer %d\n",
__func__, ret);
if (!ret)
ret = -ENOMEM;
goto init_failed_free_payload_buffer;
}
feed_data->buffer_desc.read_ptr = 0;
feed_data->buffer_desc.write_ptr = 0;
feed_data->buffer_desc.base = payload_buffer;
feed_data->buffer_desc.size = actual_buffer_size;
feed_data->buffer_desc.handle =
ion_share_dma_buf(
mpq_demux->ion_client,
feed_data->payload_buff_handle);
if (feed_data->buffer_desc.handle < 0) {
ret = -EFAULT;
goto init_failed_unmap_payload_buffer;
}
/* Register the new stream-buffer interface to MPQ adapter */
switch (feed->pes_type) {
case DMX_TS_PES_VIDEO0:
feed_data->stream_interface =
MPQ_ADAPTER_VIDEO0_STREAM_IF;
break;
case DMX_TS_PES_VIDEO1:
feed_data->stream_interface =
MPQ_ADAPTER_VIDEO1_STREAM_IF;
break;
case DMX_TS_PES_VIDEO2:
feed_data->stream_interface =
MPQ_ADAPTER_VIDEO2_STREAM_IF;
break;
case DMX_TS_PES_VIDEO3:
feed_data->stream_interface =
MPQ_ADAPTER_VIDEO3_STREAM_IF;
break;
default:
MPQ_DVB_ERR_PRINT(
"%s: Invalid pes type %d\n",
__func__,
feed->pes_type);
ret = -EINVAL;
goto init_failed_unshare_payload_buffer;
}
/* 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_unshare_payload_buffer;
}
feed_data->video_buffer =
&mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
ret = mpq_streambuffer_init(
feed_data->video_buffer,
MPQ_STREAMBUFFER_BUFFER_MODE_RING,
&feed_data->buffer_desc,
1,
packet_buffer,
VIDEO_META_DATA_BUFFER_SIZE);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_streambuffer_init failed, err = %d\n",
__func__, ret);
goto init_failed_unshare_payload_buffer;
}
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_unshare_payload_buffer;
}
feed->buffer_size = actual_buffer_size;
feed_data->pes_payload_address =
(u32)feed_data->video_buffer->raw_data.data;
feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
feed_data->pes_header_offset = 0;
feed->pusi_seen = 0;
feed->peslen = 0;
feed_data->fullness_wait_cancel = 0;
feed_data->last_framing_match_address = 0;
feed_data->last_framing_match_type = DMX_FRM_UNKNOWN;
feed_data->found_sequence_header_pattern = 0;
memset(&feed_data->prefix_size, 0,
sizeof(struct mpq_framing_prefix_size_masks));
feed_data->first_pattern_offset = 0;
feed_data->first_prefix_size = 0;
feed_data->write_pts_dts = 0;
spin_lock(&mpq_demux->feed_lock);
feed->priv = (void *)feed_data;
spin_unlock(&mpq_demux->feed_lock);
return 0;
init_failed_unshare_payload_buffer:
put_unused_fd(feed_data->buffer_desc.handle);
init_failed_unmap_payload_buffer:
ion_unmap_kernel(mpq_demux->ion_client,
feed_data->payload_buff_handle);
init_failed_free_payload_buffer:
ion_free(mpq_demux->ion_client,
feed_data->payload_buff_handle);
init_failed_free_packet_buffer:
vfree(packet_buffer);
init_failed_free_priv_data:
vfree(feed_data);
feed->priv = NULL;
init_failed:
return ret;
}
EXPORT_SYMBOL(mpq_dmx_init_video_feed);
int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
{
struct mpq_video_feed_info *feed_data;
struct mpq_demux *mpq_demux;
if (feed->priv == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid feed, feed->priv is NULL\n",
__func__);
return -EINVAL;
}
mpq_demux = feed->demux->priv;
feed_data = feed->priv;
spin_lock(&mpq_demux->feed_lock);
feed->priv = NULL;
spin_unlock(&mpq_demux->feed_lock);
wake_up_all(&feed_data->video_buffer->raw_data.queue);
mpq_adapter_unregister_stream_if(feed_data->stream_interface);
vfree(feed_data->video_buffer->packet_data.data);
put_unused_fd(feed_data->buffer_desc.handle);
ion_unmap_kernel(mpq_demux->ion_client,
feed_data->payload_buff_handle);
ion_free(mpq_demux->ion_client,
feed_data->payload_buff_handle);
vfree(feed_data);
return 0;
}
EXPORT_SYMBOL(mpq_dmx_terminate_video_feed);
int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
{
struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
spin_lock(&mpq_demux->feed_lock);
if (feed->priv == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid feed, feed->priv is NULL\n",
__func__);
spin_unlock(&mpq_demux->feed_lock);
return -EINVAL;
}
feed_data = feed->priv;
feed_data->fullness_wait_cancel = 0;
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/* else */
MPQ_DVB_DBG_PRINT(
"%s: Invalid feed type %d\n",
__func__,
feed->pes_type);
return -EINVAL;
}
EXPORT_SYMBOL(mpq_dmx_decoder_fullness_init);
int mpq_dmx_decoder_fullness_wait(
struct dvb_demux_feed *feed,
size_t required_space)
{
struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
int ret;
struct mpq_video_feed_info *feed_data;
struct dvb_ringbuffer *video_buff;
spin_lock(&mpq_demux->feed_lock);
if (feed->priv == NULL) {
spin_unlock(&mpq_demux->feed_lock);
return -EINVAL;
}
feed_data = feed->priv;
video_buff = &feed_data->video_buffer->raw_data;
ret = 0;
if ((feed_data != NULL) &&
(!feed_data->fullness_wait_cancel) &&
(dvb_ringbuffer_free(video_buff) < required_space)) {
DEFINE_WAIT(__wait);
for (;;) {
prepare_to_wait(
&video_buff->queue,
&__wait,
TASK_INTERRUPTIBLE);
if ((feed->priv == NULL) ||
(feed_data->fullness_wait_cancel) ||
(dvb_ringbuffer_free(video_buff) >=
required_space))
break;
if (!signal_pending(current)) {
spin_unlock(&mpq_demux->feed_lock);
schedule();
spin_lock(&mpq_demux->feed_lock);
continue;
}
ret = -ERESTARTSYS;
break;
}
finish_wait(&video_buff->queue, &__wait);
}
if (ret < 0) {
spin_unlock(&mpq_demux->feed_lock);
return ret;
}
if ((feed->priv == NULL) ||
(feed_data->fullness_wait_cancel)) {
spin_unlock(&mpq_demux->feed_lock);
return -EINVAL;
}
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/* else */
MPQ_DVB_DBG_PRINT(
"%s: Invalid feed type %d\n",
__func__,
feed->pes_type);
return -EINVAL;
}
EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
{
struct mpq_demux *mpq_demux = feed->demux->priv;
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
struct dvb_ringbuffer *video_buff;
spin_lock(&mpq_demux->feed_lock);
if (feed->priv == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid feed, feed->priv is NULL\n",
__func__);
spin_unlock(&mpq_demux->feed_lock);
return -EINVAL;
}
feed_data = feed->priv;
video_buff = &feed_data->video_buffer->raw_data;
feed_data->fullness_wait_cancel = 1;
spin_unlock(&mpq_demux->feed_lock);
wake_up_all(&video_buff->queue);
return 0;
}
/* else */
MPQ_DVB_ERR_PRINT(
"%s: Invalid feed type %d\n",
__func__,
feed->pes_type);
return -EINVAL;
}
EXPORT_SYMBOL(mpq_dmx_decoder_fullness_abort);
static inline 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__);
return -EINVAL;
}
feed_data->pes_header_left_bytes =
pes_header->pes_header_data_length;
}
return 0;
}
static inline int mpq_dmx_parse_remaining_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;
/* Remainning 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;
feed_data->write_pts_dts = 1;
}
/* 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;
feed_data->write_pts_dts = 1;
}
/* Any more header bytes?! */
if (feed_data->pes_header_left_bytes >= *bytes_avail) {
feed_data->pes_header_left_bytes -= *bytes_avail;
return -EINVAL;
}
/* 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 inline void mpq_dmx_get_pts_dts(struct mpq_video_feed_info *feed_data,
struct pes_packet_header *pes_header,
struct mpq_adapter_video_meta_data *meta_data,
enum dmx_packet_type packet_type)
{
struct dmx_pts_dts_info *info;
if (packet_type == DMX_PES_PACKET)
info = &(meta_data->info.pes.pts_dts_info);
else
info = &(meta_data->info.framing.pts_dts_info);
if (feed_data->write_pts_dts) {
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;
}
} else {
info->pts_exist = 0;
info->dts_exist = 0;
}
}
static int mpq_dmx_process_video_packet_framing(
struct dvb_demux_feed *feed,
const u8 *buf)
{
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_framing_pattern_lookup_results framing_res;
int found_patterns = 0;
int first_pattern = 0;
int i;
u32 pattern_addr = 0;
int is_video_frame = 0;
mpq_demux = feed->demux->priv;
spin_lock(&mpq_demux->feed_lock);
feed_data = feed->priv;
if (unlikely(feed_data == NULL)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
ts_header = (const struct ts_packet_header *)buf;
stream_buffer = feed_data->video_buffer;
pes_header = &feed_data->pes_header;
/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
ts_header->sync_byte,
ts_header->transport_error_indicator,
ts_header->payload_unit_start_indicator,
ts_header->transport_priority,
ts_header->pid_msb,
ts_header->pid_lsb,
ts_header->transport_scrambling_control,
ts_header->adaptation_field_control,
ts_header->continuity_counter); */
/* 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(&mpq_demux->feed_lock);
return 0;
}
if (ts_header->payload_unit_start_indicator) { /* PUSI? */
if (feed->pusi_seen) { /* Did we see PUSI before? */
/*
* Double check that we are not in middle of
* previous PES header parsing.
*/
if (feed_data->pes_header_left_bytes != 0) {
MPQ_DVB_ERR_PRINT(
"%s: received PUSI"
"while handling PES header"
"of previous PES\n",
__func__);
}
feed->peslen = 0;
feed_data->pes_header_offset = 0;
feed_data->pes_header_left_bytes =
PES_MANDATORY_FIELDS_LEN;
feed_data->write_pts_dts = 0;
} else {
feed->pusi_seen = 1;
}
}
/*
* Parse PES data only if PUSI was encountered,
* otherwise the data is dropped
*/
if (!feed->pusi_seen) {
spin_unlock(&mpq_demux->feed_lock);
return 0; /* drop and wait for next packets */
}
ts_payload_offset = sizeof(struct ts_packet_header);
/* Skip adaptation field if exists */
if (ts_header->adaptation_field_control == 3)
ts_payload_offset += buf[ts_payload_offset] + 1;
/* 188 bytes: the size of a TS packet including the TS packet header */
bytes_avail = 188 - 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(&mpq_demux->feed_lock);
return 0;
}
if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/*
* If we reached here,
* then we are now at the PES payload data
*/
if (bytes_avail == 0) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/*
* the decoder requires demux to do framing,
* so search for the patterns now.
*/
found_patterns = mpq_dmx_framing_pattern_search(
feed_data->patterns,
feed_data->patterns_num,
(buf + ts_payload_offset),
bytes_avail,
&feed_data->prefix_size,
&framing_res);
if (!(feed_data->found_sequence_header_pattern)) {
for (i = 0; i < found_patterns; i++) {
if ((framing_res.info[i].type ==
DMX_FRM_MPEG2_SEQUENCE_HEADER) ||
(framing_res.info[i].type ==
DMX_FRM_H264_SPS) ||
(framing_res.info[i].type ==
DMX_FRM_VC1_SEQUENCE_HEADER)) {
MPQ_DVB_DBG_PRINT(
"%s: Found Sequence Pattern, buf %p, "
"i = %d, offset = %d, type = %d\n",
__func__, buf, i,
framing_res.info[i].offset,
framing_res.info[i].type);
first_pattern = i;
feed_data->found_sequence_header_pattern = 1;
ts_payload_offset +=
framing_res.info[i].offset;
bytes_avail -= framing_res.info[i].offset;
if (framing_res.info[i].used_prefix_size) {
feed_data->first_prefix_size =
framing_res.info[i].
used_prefix_size;
}
/*
* if this is the first pattern we write,
* no need to take offset into account since we
* dropped all data before it (so effectively
* offset is 0).
* we save the first pattern offset and take
* it into consideration for the rest of the
* patterns found in this buffer.
*/
feed_data->first_pattern_offset =
framing_res.info[i].offset;
break;
}
}
}
/*
* If decoder requires demux to do framing,
* pass data to decoder only after sequence header
* or equivalent is found. Otherwise the data is dropped.
*/
if (!(feed_data->found_sequence_header_pattern)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/*
* write prefix used to find first Sequence pattern, if needed.
* feed_data->patterns[0].pattern always contains the Sequence
* pattern.
*/
if (feed_data->first_prefix_size) {
if (mpq_streambuffer_data_write(stream_buffer,
(feed_data->patterns[0].pattern),
feed_data->first_prefix_size) < 0) {
mpq_demux->decoder_tsp_drop_count++;
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
feed_data->first_prefix_size = 0;
}
/* write data to payload buffer */
if (mpq_streambuffer_data_write(stream_buffer,
(buf + ts_payload_offset),
bytes_avail) < 0) {
mpq_demux->decoder_tsp_drop_count++;
} else {
struct mpq_streambuffer_packet_header packet;
struct mpq_adapter_video_meta_data meta_data;
feed->peslen += bytes_avail;
meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
packet.raw_data_handle = feed_data->buffer_desc.handle;
packet.raw_data_offset = 0;
packet.user_data_len =
sizeof(struct mpq_adapter_video_meta_data);
for (i = first_pattern; i < found_patterns; i++) {
if (feed_data->last_framing_match_address) {
is_video_frame = mpq_dmx_is_video_frame(
feed->indexing_params.standard,
feed_data->last_framing_match_type);
if (is_video_frame == 1) {
mpq_dmx_get_pts_dts(feed_data,
pes_header,
&meta_data,
DMX_FRAMING_INFO_PACKET);
} else {
meta_data.info.framing.
pts_dts_info.pts_exist = 0;
meta_data.info.framing.
pts_dts_info.dts_exist = 0;
}
/*
* writing meta-data that includes
* framing information
*/
meta_data.info.framing.pattern_type =
feed_data->last_framing_match_type;
pattern_addr = feed_data->pes_payload_address +
framing_res.info[i].offset -
framing_res.info[i].used_prefix_size;
if ((pattern_addr -
feed_data->first_pattern_offset) <
feed_data->last_framing_match_address) {
/* wraparound case */
packet.raw_data_len =
(pattern_addr -
feed_data->
last_framing_match_address +
stream_buffer->raw_data.size) -
feed_data->first_pattern_offset;
} else {
packet.raw_data_len =
pattern_addr -
feed_data->
last_framing_match_address -
feed_data->first_pattern_offset;
}
MPQ_DVB_DBG_PRINT(
"Writing Packet: len = %d, type = %d, isPts = %d, isDts = %d\n",
packet.raw_data_len,
meta_data.info.framing.pattern_type,
meta_data.info.framing.
pts_dts_info.pts_exist,
meta_data.info.framing.
pts_dts_info.dts_exist);
if (mpq_streambuffer_pkt_write(stream_buffer,
&packet,
(u8 *)&meta_data) < 0) {
MPQ_DVB_ERR_PRINT(
"%s: Couldn't write packet. Should never happen\n",
__func__);
} else {
if (is_video_frame == 1)
feed_data->write_pts_dts = 0;
}
}
/* save the last match for next time */
feed_data->last_framing_match_type =
framing_res.info[i].type;
feed_data->last_framing_match_address =
(feed_data->pes_payload_address +
framing_res.info[i].offset -
framing_res.info[i].used_prefix_size -
feed_data->first_pattern_offset);
}
/*
* the first pattern offset is needed only for the group of
* patterns that are found and written with the first pattern.
*/
feed_data->first_pattern_offset = 0;
feed_data->pes_payload_address =
(u32)stream_buffer->raw_data.data +
stream_buffer->raw_data.pwrite;
}
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
static int mpq_dmx_process_video_packet_no_framing(
struct dvb_demux_feed *feed,
const u8 *buf)
{
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;
mpq_demux = feed->demux->priv;
spin_lock(&mpq_demux->feed_lock);
feed_data = feed->priv;
if (unlikely(feed_data == NULL)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
ts_header = (const struct ts_packet_header *)buf;
stream_buffer = feed_data->video_buffer;
pes_header = &feed_data->pes_header;
/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
ts_header->sync_byte,
ts_header->transport_error_indicator,
ts_header->payload_unit_start_indicator,
ts_header->transport_priority,
ts_header->pid_msb,
ts_header->pid_lsb,
ts_header->transport_scrambling_control,
ts_header->adaptation_field_control,
ts_header->continuity_counter); */
/* 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(&mpq_demux->feed_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 (0 == feed_data->pes_header_left_bytes) {
packet.raw_data_len = feed->peslen;
packet.raw_data_handle =
feed_data->buffer_desc.handle;
packet.raw_data_offset = 0;
packet.user_data_len =
sizeof(struct
mpq_adapter_video_meta_data);
mpq_dmx_get_pts_dts(feed_data, pes_header,
&meta_data,
DMX_PES_PACKET);
meta_data.packet_type = DMX_PES_PACKET;
if (mpq_streambuffer_pkt_write(
stream_buffer,
&packet,
(u8 *)&meta_data) < 0)
MPQ_DVB_ERR_PRINT(
"%s: "
"Couldn't write packet. "
"Should never happen\n",
__func__);
else
feed_data->write_pts_dts = 0;
} else {
MPQ_DVB_ERR_PRINT(
"%s: received PUSI"
"while handling PES header"
"of previous PES\n",
__func__);
}
/* Reset PES info */
feed_data->pes_payload_address =
(u32)stream_buffer->raw_data.data +
stream_buffer->raw_data.pwrite;
feed->peslen = 0;
feed_data->pes_header_offset = 0;
feed_data->pes_header_left_bytes =
PES_MANDATORY_FIELDS_LEN;
} else {
feed->pusi_seen = 1;
}
}
/*
* Parse PES data only if PUSI was encountered,
* otherwise the data is dropped
*/
if (!feed->pusi_seen) {
spin_unlock(&mpq_demux->feed_lock);
return 0; /* drop and wait for next packets */
}
ts_payload_offset = sizeof(struct ts_packet_header);
/* Skip adaptation field if exists */
if (ts_header->adaptation_field_control == 3)
ts_payload_offset += buf[ts_payload_offset] + 1;
/* 188 bytes: size of a TS packet including the TS packet header */
bytes_avail = 188 - 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(&mpq_demux->feed_lock);
return 0;
}
if (mpq_dmx_parse_remaining_pes_header(feed, feed_data,
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/*
* If we reached here,
* then we are now at the PES payload data
*/
if (bytes_avail == 0) {
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
if (mpq_streambuffer_data_write(
stream_buffer,
buf+ts_payload_offset,
bytes_avail) < 0)
mpq_demux->decoder_tsp_drop_count++;
else
feed->peslen += bytes_avail;
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
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;
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
struct dvb_ringbuffer *video_buff;
spin_lock(&mpq_demux->feed_lock);
if (feed->priv == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid feed, feed->priv is NULL\n",
__func__);
spin_unlock(&mpq_demux->feed_lock);
return -EINVAL;
}
feed_data = feed->priv;
video_buff = &feed_data->video_buffer->raw_data;
dmx_buffer_status->error = video_buff->error;
dmx_buffer_status->fullness = dvb_ringbuffer_avail(video_buff);
dmx_buffer_status->free_bytes = dvb_ringbuffer_free(video_buff);
dmx_buffer_status->read_offset = video_buff->pread;
dmx_buffer_status->write_offset = video_buff->pwrite;
dmx_buffer_status->size = video_buff->size;
spin_unlock(&mpq_demux->feed_lock);
return 0;
}
/* else */
MPQ_DVB_ERR_PRINT(
"%s: Invalid feed type %d\n",
__func__,
feed->pes_type);
return -EINVAL;
}
EXPORT_SYMBOL(mpq_dmx_decoder_buffer_status);
int mpq_dmx_process_video_packet(
struct dvb_demux_feed *feed,
const u8 *buf)
{
if (mpq_dmx_info.decoder_framing)
return mpq_dmx_process_video_packet_no_framing(feed, buf);
else
return mpq_dmx_process_video_packet_framing(feed, buf);
}
EXPORT_SYMBOL(mpq_dmx_process_video_packet);
int mpq_dmx_process_pcr_packet(
struct dvb_demux_feed *feed,
const u8 *buf)
{
u64 pcr;
u64 stc;
struct dmx_data_ready data;
struct mpq_demux *mpq_demux = feed->demux->priv;
const struct ts_packet_header *ts_header;
const struct ts_adaptation_field *adaptation_field;
/*
* When we play from front-end, we configure HW
* to output the extra timestamp, if we are playing
* from DVR, make sure the format is 192 packet.
*/
if ((mpq_demux->source >= DMX_SOURCE_DVR0) &&
(mpq_demux->demux.tsp_format != DMX_TSP_FORMAT_192_TAIL)) {
MPQ_DVB_ERR_PRINT(
"%s: invalid packet format %d for PCR extraction\n",
__func__,
mpq_demux->demux.tsp_format);
return -EINVAL;
}
ts_header = (const struct ts_packet_header *)buf;
/* Make sure this TS packet has a adaptation field */
if ((ts_header->sync_byte != 0x47) ||
(ts_header->adaptation_field_control == 0) ||
(ts_header->adaptation_field_control == 1)) {
return 0;
}
adaptation_field = (const struct ts_adaptation_field *)
(buf + sizeof(struct ts_packet_header));
if ((!adaptation_field->adaptation_field_length) ||
(!adaptation_field->PCR_flag))
return 0; /* 0 adaptation field or no PCR */
pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
pcr += adaptation_field->program_clock_reference_base_5;
pcr *= 300;
pcr +=
(((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
adaptation_field->program_clock_reference_ext_2;
stc = buf[190] << 16;
stc += buf[189] << 8;
stc += buf[188];
stc *= 256; /* convert from 105.47 KHZ to 27MHz */
data.data_length = 0;
data.pcr.pcr = pcr;
data.pcr.stc = stc;
data.pcr.disc_indicator_set = adaptation_field->discontinuity_indicator;
data.status = DMX_OK_PCR;
feed->data_ready_cb.ts(&feed->feed.ts, &data);
return 0;
}
EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);