mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-23 08:35:19 -05:00
media: dvb: update buffer mmaped flags and frame counter
Now that we have support for a buffer counter and for error flags, update them at DMX_DQBUF. Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
parent
9c171cdf22
commit
fdbeb96258
10 changed files with 160 additions and 76 deletions
|
@ -385,7 +385,8 @@ static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
|
||||||
|
|
||||||
static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
|
static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
const u8 *buffer2, size_t buffer2_len,
|
const u8 *buffer2, size_t buffer2_len,
|
||||||
struct dmx_section_filter *filter)
|
struct dmx_section_filter *filter,
|
||||||
|
u32 *buffer_flags)
|
||||||
{
|
{
|
||||||
struct dmxdev_filter *dmxdevfilter = filter->priv;
|
struct dmxdev_filter *dmxdevfilter = filter->priv;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -404,10 +405,12 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
dprintk("section callback %*ph\n", 6, buffer1);
|
dprintk("section callback %*ph\n", 6, buffer1);
|
||||||
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
|
if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) {
|
||||||
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
|
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
|
||||||
buffer1, buffer1_len);
|
buffer1, buffer1_len,
|
||||||
|
buffer_flags);
|
||||||
if (ret == buffer1_len)
|
if (ret == buffer1_len)
|
||||||
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
|
ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx,
|
||||||
buffer2, buffer2_len);
|
buffer2, buffer2_len,
|
||||||
|
buffer_flags);
|
||||||
} else {
|
} else {
|
||||||
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
|
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer,
|
||||||
buffer1, buffer1_len);
|
buffer1, buffer1_len);
|
||||||
|
@ -427,7 +430,8 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
|
|
||||||
static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
|
static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
const u8 *buffer2, size_t buffer2_len,
|
const u8 *buffer2, size_t buffer2_len,
|
||||||
struct dmx_ts_feed *feed)
|
struct dmx_ts_feed *feed,
|
||||||
|
u32 *buffer_flags)
|
||||||
{
|
{
|
||||||
struct dmxdev_filter *dmxdevfilter = feed->priv;
|
struct dmxdev_filter *dmxdevfilter = feed->priv;
|
||||||
struct dvb_ringbuffer *buffer;
|
struct dvb_ringbuffer *buffer;
|
||||||
|
@ -456,9 +460,11 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dvb_vb2_is_streaming(ctx)) {
|
if (dvb_vb2_is_streaming(ctx)) {
|
||||||
ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len);
|
ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len,
|
||||||
|
buffer_flags);
|
||||||
if (ret == buffer1_len)
|
if (ret == buffer1_len)
|
||||||
ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len);
|
ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len,
|
||||||
|
buffer_flags);
|
||||||
} else {
|
} else {
|
||||||
if (buffer->error) {
|
if (buffer->error) {
|
||||||
spin_unlock(&dmxdevfilter->dev->lock);
|
spin_unlock(&dmxdevfilter->dev->lock);
|
||||||
|
@ -1218,7 +1224,7 @@ static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dmxdev->may_do_mmap)
|
if (!dmxdev->may_do_mmap)
|
||||||
return -EOPNOTSUPP;
|
return -ENOTTY;
|
||||||
|
|
||||||
if (mutex_lock_interruptible(&dmxdev->mutex))
|
if (mutex_lock_interruptible(&dmxdev->mutex))
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
|
@ -1318,7 +1324,7 @@ static int dvb_dvr_do_ioctl(struct file *file,
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -ENOTTY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mutex_unlock(&dmxdev->mutex);
|
mutex_unlock(&dmxdev->mutex);
|
||||||
|
@ -1367,7 +1373,7 @@ static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dmxdev->may_do_mmap)
|
if (!dmxdev->may_do_mmap)
|
||||||
return -EOPNOTSUPP;
|
return -ENOTTY;
|
||||||
|
|
||||||
if (dmxdev->exit)
|
if (dmxdev->exit)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -55,6 +55,17 @@ MODULE_PARM_DESC(dvb_demux_feed_err_pkts,
|
||||||
dprintk(x); \
|
dprintk(x); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
||||||
|
# define dprintk_sect_loss(x...) dprintk(x)
|
||||||
|
#else
|
||||||
|
# define dprintk_sect_loss(x...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define set_buf_flags(__feed, __flag) \
|
||||||
|
do { \
|
||||||
|
(__feed)->buffer_flags |= (__flag); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* static inlined helper functions
|
* static inlined helper functions
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -104,31 +115,30 @@ static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
|
||||||
{
|
{
|
||||||
int count = payload(buf);
|
int count = payload(buf);
|
||||||
int p;
|
int p;
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
|
||||||
int ccok;
|
int ccok;
|
||||||
u8 cc;
|
u8 cc;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
p = 188 - count;
|
p = 188 - count;
|
||||||
|
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
|
||||||
cc = buf[3] & 0x0f;
|
cc = buf[3] & 0x0f;
|
||||||
ccok = ((feed->cc + 1) & 0x0f) == cc;
|
ccok = ((feed->cc + 1) & 0x0f) == cc;
|
||||||
feed->cc = cc;
|
feed->cc = cc;
|
||||||
if (!ccok)
|
if (!ccok) {
|
||||||
dprintk("missed packet: %d instead of %d!\n",
|
set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
cc, (feed->cc + 1) & 0x0f);
|
dprintk_sect_loss("missed packet: %d instead of %d!\n",
|
||||||
#endif
|
cc, (feed->cc + 1) & 0x0f);
|
||||||
|
}
|
||||||
|
|
||||||
if (buf[1] & 0x40) // PUSI ?
|
if (buf[1] & 0x40) // PUSI ?
|
||||||
feed->peslen = 0xfffa;
|
feed->peslen = 0xfffa;
|
||||||
|
|
||||||
feed->peslen += count;
|
feed->peslen += count;
|
||||||
|
|
||||||
return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts);
|
return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts,
|
||||||
|
&feed->buffer_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
|
static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
|
||||||
|
@ -150,7 +160,7 @@ static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
|
return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
|
||||||
NULL, 0, &f->filter);
|
NULL, 0, &f->filter, &feed->buffer_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
|
static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
|
||||||
|
@ -169,8 +179,10 @@ static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
|
||||||
if (sec->check_crc) {
|
if (sec->check_crc) {
|
||||||
section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
|
section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
|
||||||
if (section_syntax_indicator &&
|
if (section_syntax_indicator &&
|
||||||
demux->check_crc32(feed, sec->secbuf, sec->seclen))
|
demux->check_crc32(feed, sec->secbuf, sec->seclen)) {
|
||||||
|
set_buf_flags(feed, DMX_BUFFER_FLAG_HAD_CRC32_DISCARD);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -187,7 +199,6 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
|
||||||
{
|
{
|
||||||
struct dmx_section_feed *sec = &feed->feed.sec;
|
struct dmx_section_feed *sec = &feed->feed.sec;
|
||||||
|
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
|
||||||
if (sec->secbufp < sec->tsfeedp) {
|
if (sec->secbufp < sec->tsfeedp) {
|
||||||
int n = sec->tsfeedp - sec->secbufp;
|
int n = sec->tsfeedp - sec->secbufp;
|
||||||
|
|
||||||
|
@ -197,12 +208,13 @@ static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
|
||||||
* but just first and last.
|
* but just first and last.
|
||||||
*/
|
*/
|
||||||
if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {
|
if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {
|
||||||
dprintk("section ts padding loss: %d/%d\n",
|
set_buf_flags(feed,
|
||||||
n, sec->tsfeedp);
|
DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
dprintk("pad data: %*ph\n", n, sec->secbuf);
|
dprintk_sect_loss("section ts padding loss: %d/%d\n",
|
||||||
|
n, sec->tsfeedp);
|
||||||
|
dprintk_sect_loss("pad data: %*ph\n", n, sec->secbuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
sec->tsfeedp = sec->secbufp = sec->seclen = 0;
|
sec->tsfeedp = sec->secbufp = sec->seclen = 0;
|
||||||
sec->secbuf = sec->secbuf_base;
|
sec->secbuf = sec->secbuf_base;
|
||||||
|
@ -237,11 +249,10 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
|
if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
set_buf_flags(feed, DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
dprintk("section buffer full loss: %d/%d\n",
|
dprintk_sect_loss("section buffer full loss: %d/%d\n",
|
||||||
sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
|
sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
|
||||||
DMX_MAX_SECFEED_SIZE);
|
DMX_MAX_SECFEED_SIZE);
|
||||||
#endif
|
|
||||||
len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
|
len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,12 +280,13 @@ static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
|
||||||
sec->seclen = seclen;
|
sec->seclen = seclen;
|
||||||
sec->crc_val = ~0;
|
sec->crc_val = ~0;
|
||||||
/* dump [secbuf .. secbuf+seclen) */
|
/* dump [secbuf .. secbuf+seclen) */
|
||||||
if (feed->pusi_seen)
|
if (feed->pusi_seen) {
|
||||||
dvb_dmx_swfilter_section_feed(feed);
|
dvb_dmx_swfilter_section_feed(feed);
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
} else {
|
||||||
else
|
set_buf_flags(feed,
|
||||||
dprintk("pusi not seen, discarding section data\n");
|
DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
#endif
|
dprintk_sect_loss("pusi not seen, discarding section data\n");
|
||||||
|
}
|
||||||
sec->secbufp += seclen; /* secbufp and secbuf moving together is */
|
sec->secbufp += seclen; /* secbufp and secbuf moving together is */
|
||||||
sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
|
sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
|
||||||
}
|
}
|
||||||
|
@ -307,18 +319,22 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ccok || dc_i) {
|
if (!ccok || dc_i) {
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
if (dc_i) {
|
||||||
if (dc_i)
|
set_buf_flags(feed,
|
||||||
dprintk("%d frame with disconnect indicator\n",
|
DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR);
|
||||||
|
dprintk_sect_loss("%d frame with disconnect indicator\n",
|
||||||
cc);
|
cc);
|
||||||
else
|
} else {
|
||||||
dprintk("discontinuity: %d instead of %d. %d bytes lost\n",
|
set_buf_flags(feed,
|
||||||
|
DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
|
dprintk_sect_loss("discontinuity: %d instead of %d. %d bytes lost\n",
|
||||||
cc, (feed->cc + 1) & 0x0f, count + 4);
|
cc, (feed->cc + 1) & 0x0f, count + 4);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* those bytes under sume circumstances will again be reported
|
* those bytes under some circumstances will again be reported
|
||||||
* in the following dvb_dmx_swfilter_section_new
|
* in the following dvb_dmx_swfilter_section_new
|
||||||
*/
|
*/
|
||||||
#endif
|
|
||||||
/*
|
/*
|
||||||
* Discontinuity detected. Reset pusi_seen to
|
* Discontinuity detected. Reset pusi_seen to
|
||||||
* stop feeding of suspicious data until next PUSI=1 arrives
|
* stop feeding of suspicious data until next PUSI=1 arrives
|
||||||
|
@ -326,6 +342,7 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
|
||||||
* FIXME: does it make sense if the MPEG-TS is the one
|
* FIXME: does it make sense if the MPEG-TS is the one
|
||||||
* reporting discontinuity?
|
* reporting discontinuity?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
feed->pusi_seen = false;
|
feed->pusi_seen = false;
|
||||||
dvb_dmx_swfilter_section_new(feed);
|
dvb_dmx_swfilter_section_new(feed);
|
||||||
}
|
}
|
||||||
|
@ -345,11 +362,11 @@ static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
|
||||||
dvb_dmx_swfilter_section_new(feed);
|
dvb_dmx_swfilter_section_new(feed);
|
||||||
dvb_dmx_swfilter_section_copy_dump(feed, after,
|
dvb_dmx_swfilter_section_copy_dump(feed, after,
|
||||||
after_len);
|
after_len);
|
||||||
|
} else if (count > 0) {
|
||||||
|
set_buf_flags(feed,
|
||||||
|
DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED);
|
||||||
|
dprintk_sect_loss("PUSI=1 but %d bytes lost\n", count);
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_DVB_DEMUX_SECTION_LOSS_LOG
|
|
||||||
else if (count > 0)
|
|
||||||
dprintk("PUSI=1 but %d bytes lost\n", count);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
/* PUSI=0 (is not set), no section boundary */
|
/* PUSI=0 (is not set), no section boundary */
|
||||||
dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
|
dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
|
||||||
|
@ -369,7 +386,8 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
|
||||||
if (feed->ts_type & TS_PAYLOAD_ONLY)
|
if (feed->ts_type & TS_PAYLOAD_ONLY)
|
||||||
dvb_dmx_swfilter_payload(feed, buf);
|
dvb_dmx_swfilter_payload(feed, buf);
|
||||||
else
|
else
|
||||||
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
|
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
|
||||||
|
&feed->buffer_flags);
|
||||||
}
|
}
|
||||||
/* Used only on full-featured devices */
|
/* Used only on full-featured devices */
|
||||||
if (feed->ts_type & TS_DECODER)
|
if (feed->ts_type & TS_DECODER)
|
||||||
|
@ -430,6 +448,11 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[1] & 0x80) {
|
if (buf[1] & 0x80) {
|
||||||
|
list_for_each_entry(feed, &demux->feed_list, list_head) {
|
||||||
|
if ((feed->pid != pid) && (feed->pid != 0x2000))
|
||||||
|
continue;
|
||||||
|
set_buf_flags(feed, DMX_BUFFER_FLAG_TEI);
|
||||||
|
}
|
||||||
dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n",
|
dprintk_tscheck("TEI detected. PID=0x%x data1=0x%x\n",
|
||||||
pid, buf[1]);
|
pid, buf[1]);
|
||||||
/* data in this packet can't be trusted - drop it unless
|
/* data in this packet can't be trusted - drop it unless
|
||||||
|
@ -445,6 +468,13 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
|
||||||
(demux->cnt_storage[pid] + 1) & 0xf;
|
(demux->cnt_storage[pid] + 1) & 0xf;
|
||||||
|
|
||||||
if ((buf[3] & 0xf) != demux->cnt_storage[pid]) {
|
if ((buf[3] & 0xf) != demux->cnt_storage[pid]) {
|
||||||
|
list_for_each_entry(feed, &demux->feed_list, list_head) {
|
||||||
|
if ((feed->pid != pid) && (feed->pid != 0x2000))
|
||||||
|
continue;
|
||||||
|
set_buf_flags(feed,
|
||||||
|
DMX_BUFFER_PKT_COUNTER_MISMATCH);
|
||||||
|
}
|
||||||
|
|
||||||
dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
|
dprintk_tscheck("TS packet counter mismatch. PID=0x%x expected 0x%x got 0x%x\n",
|
||||||
pid, demux->cnt_storage[pid],
|
pid, demux->cnt_storage[pid],
|
||||||
buf[3] & 0xf);
|
buf[3] & 0xf);
|
||||||
|
@ -466,7 +496,8 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
|
||||||
if (feed->pid == pid)
|
if (feed->pid == pid)
|
||||||
dvb_dmx_swfilter_packet_type(feed, buf);
|
dvb_dmx_swfilter_packet_type(feed, buf);
|
||||||
else if (feed->pid == 0x2000)
|
else if (feed->pid == 0x2000)
|
||||||
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts);
|
feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
|
||||||
|
&feed->buffer_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +616,8 @@ void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
|
||||||
|
|
||||||
spin_lock_irqsave(&demux->lock, flags);
|
spin_lock_irqsave(&demux->lock, flags);
|
||||||
|
|
||||||
demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts);
|
demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts,
|
||||||
|
&demux->feed->buffer_flags);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&demux->lock, flags);
|
spin_unlock_irqrestore(&demux->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -785,6 +817,7 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
|
||||||
feed->demux = demux;
|
feed->demux = demux;
|
||||||
feed->pid = 0xffff;
|
feed->pid = 0xffff;
|
||||||
feed->peslen = 0xfffa;
|
feed->peslen = 0xfffa;
|
||||||
|
feed->buffer_flags = 0;
|
||||||
|
|
||||||
(*ts_feed) = &feed->feed.ts;
|
(*ts_feed) = &feed->feed.ts;
|
||||||
(*ts_feed)->parent = dmx;
|
(*ts_feed)->parent = dmx;
|
||||||
|
@ -1042,6 +1075,7 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
|
||||||
dvbdmxfeed->cb.sec = callback;
|
dvbdmxfeed->cb.sec = callback;
|
||||||
dvbdmxfeed->demux = dvbdmx;
|
dvbdmxfeed->demux = dvbdmx;
|
||||||
dvbdmxfeed->pid = 0xffff;
|
dvbdmxfeed->pid = 0xffff;
|
||||||
|
dvbdmxfeed->buffer_flags = 0;
|
||||||
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
|
dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
|
||||||
dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
|
dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
|
||||||
dvbdmxfeed->feed.sec.tsfeedp = 0;
|
dvbdmxfeed->feed.sec.tsfeedp = 0;
|
||||||
|
|
|
@ -883,7 +883,8 @@ static void dvb_net_ule(struct net_device *dev, const u8 *buf, size_t buf_len)
|
||||||
|
|
||||||
static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
|
static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
const u8 *buffer2, size_t buffer2_len,
|
const u8 *buffer2, size_t buffer2_len,
|
||||||
struct dmx_ts_feed *feed)
|
struct dmx_ts_feed *feed,
|
||||||
|
u32 *buffer_flags)
|
||||||
{
|
{
|
||||||
struct net_device *dev = feed->priv;
|
struct net_device *dev = feed->priv;
|
||||||
|
|
||||||
|
@ -992,7 +993,7 @@ static void dvb_net_sec(struct net_device *dev,
|
||||||
|
|
||||||
static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
|
static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
|
||||||
const u8 *buffer2, size_t buffer2_len,
|
const u8 *buffer2, size_t buffer2_len,
|
||||||
struct dmx_section_filter *filter)
|
struct dmx_section_filter *filter, u32 *buffer_flags)
|
||||||
{
|
{
|
||||||
struct net_device *dev = filter->priv;
|
struct net_device *dev = filter->priv;
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,8 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
|
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
|
||||||
const unsigned char *src, int len)
|
const unsigned char *src, int len,
|
||||||
|
enum dmx_buffer_flags *buffer_flags)
|
||||||
{
|
{
|
||||||
unsigned long flags = 0;
|
unsigned long flags = 0;
|
||||||
void *vbuf = NULL;
|
void *vbuf = NULL;
|
||||||
|
@ -264,15 +265,17 @@ int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
|
||||||
unsigned char *psrc = (unsigned char *)src;
|
unsigned char *psrc = (unsigned char *)src;
|
||||||
int ll = 0;
|
int ll = 0;
|
||||||
|
|
||||||
dprintk(3, "[%s] %d bytes are rcvd\n", ctx->name, len);
|
/*
|
||||||
if (!src) {
|
* normal case: This func is called twice from demux driver
|
||||||
dprintk(3, "[%s]:NULL pointer src\n", ctx->name);
|
* one with valid src pointer, second time with NULL pointer
|
||||||
/**normal case: This func is called twice from demux driver
|
*/
|
||||||
* once with valid src pointer, second time with NULL pointer
|
if (!src || !len)
|
||||||
*/
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
spin_lock_irqsave(&ctx->slock, flags);
|
spin_lock_irqsave(&ctx->slock, flags);
|
||||||
|
if (buffer_flags && *buffer_flags) {
|
||||||
|
ctx->flags |= *buffer_flags;
|
||||||
|
*buffer_flags = 0;
|
||||||
|
}
|
||||||
while (todo) {
|
while (todo) {
|
||||||
if (!ctx->buf) {
|
if (!ctx->buf) {
|
||||||
if (list_empty(&ctx->dvb_q)) {
|
if (list_empty(&ctx->dvb_q)) {
|
||||||
|
@ -395,6 +398,7 @@ int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
|
||||||
|
|
||||||
int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
|
int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking);
|
ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking);
|
||||||
|
@ -402,7 +406,16 @@ int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
|
||||||
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
|
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
dprintk(5, "[%s] index=%d\n", ctx->name, b->index);
|
|
||||||
|
spin_lock_irqsave(&ctx->slock, flags);
|
||||||
|
b->count = ctx->count++;
|
||||||
|
b->flags = ctx->flags;
|
||||||
|
ctx->flags = 0;
|
||||||
|
spin_unlock_irqrestore(&ctx->slock, flags);
|
||||||
|
|
||||||
|
dprintk(5, "[%s] index=%d, count=%d, flags=%d\n",
|
||||||
|
ctx->name, b->index, ctx->count, b->flags);
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,14 +324,15 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len,
|
||||||
}
|
}
|
||||||
return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len,
|
return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len,
|
||||||
buffer2, buffer2_len,
|
buffer2, buffer2_len,
|
||||||
&dvbdmxfilter->filter);
|
&dvbdmxfilter->filter, NULL);
|
||||||
case DMX_TYPE_TS:
|
case DMX_TYPE_TS:
|
||||||
if (!(dvbdmxfilter->feed->ts_type & TS_PACKET))
|
if (!(dvbdmxfilter->feed->ts_type & TS_PACKET))
|
||||||
return 0;
|
return 0;
|
||||||
if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY)
|
if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY)
|
||||||
return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len,
|
return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len,
|
||||||
buffer2, buffer2_len,
|
buffer2, buffer2_len,
|
||||||
&dvbdmxfilter->feed->feed.ts);
|
&dvbdmxfilter->feed->feed.ts,
|
||||||
|
NULL);
|
||||||
else
|
else
|
||||||
av7110_p2t_write(buffer1, buffer1_len,
|
av7110_p2t_write(buffer1, buffer1_len,
|
||||||
dvbdmxfilter->feed->pid,
|
dvbdmxfilter->feed->pid,
|
||||||
|
|
|
@ -99,7 +99,7 @@ int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len)
|
||||||
buf[4] = buf[5] = 0;
|
buf[4] = buf[5] = 0;
|
||||||
if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
|
if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
|
||||||
return dvbdmxfeed->cb.ts(buf, len, NULL, 0,
|
return dvbdmxfeed->cb.ts(buf, len, NULL, 0,
|
||||||
&dvbdmxfeed->feed.ts);
|
&dvbdmxfeed->feed.ts, NULL);
|
||||||
else
|
else
|
||||||
return dvb_filter_pes2ts(p2t, buf, len, 1);
|
return dvb_filter_pes2ts(p2t, buf, len, 1);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data)
|
||||||
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv;
|
struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv;
|
||||||
|
|
||||||
dvbdmxfeed->cb.ts(data, 188, NULL, 0,
|
dvbdmxfeed->cb.ts(data, 188, NULL, 0,
|
||||||
&dvbdmxfeed->feed.ts);
|
&dvbdmxfeed->feed.ts, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,7 +814,7 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter,
|
||||||
memcpy(obuf + l, buf + c, TS_SIZE - l);
|
memcpy(obuf + l, buf + c, TS_SIZE - l);
|
||||||
c = length;
|
c = length;
|
||||||
}
|
}
|
||||||
feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts);
|
feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, NULL);
|
||||||
pes_start = 0;
|
pes_start = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,7 +428,7 @@ static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data)
|
||||||
struct ttusb_dec *dec = priv;
|
struct ttusb_dec *dec = priv;
|
||||||
|
|
||||||
dec->audio_filter->feed->cb.ts(data, 188, NULL, 0,
|
dec->audio_filter->feed->cb.ts(data, 188, NULL, 0,
|
||||||
&dec->audio_filter->feed->feed.ts);
|
&dec->audio_filter->feed->feed.ts, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@ static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data)
|
||||||
struct ttusb_dec *dec = priv;
|
struct ttusb_dec *dec = priv;
|
||||||
|
|
||||||
dec->video_filter->feed->cb.ts(data, 188, NULL, 0,
|
dec->video_filter->feed->cb.ts(data, 188, NULL, 0,
|
||||||
&dec->video_filter->feed->feed.ts);
|
&dec->video_filter->feed->feed.ts, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -490,7 +490,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length)
|
||||||
|
|
||||||
if (output_pva) {
|
if (output_pva) {
|
||||||
dec->video_filter->feed->cb.ts(pva, length, NULL, 0,
|
dec->video_filter->feed->cb.ts(pva, length, NULL, 0,
|
||||||
&dec->video_filter->feed->feed.ts);
|
&dec->video_filter->feed->feed.ts, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,7 +551,7 @@ static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length)
|
||||||
case 0x02: /* MainAudioStream */
|
case 0x02: /* MainAudioStream */
|
||||||
if (output_pva) {
|
if (output_pva) {
|
||||||
dec->audio_filter->feed->cb.ts(pva, length, NULL, 0,
|
dec->audio_filter->feed->cb.ts(pva, length, NULL, 0,
|
||||||
&dec->audio_filter->feed->feed.ts);
|
&dec->audio_filter->feed->feed.ts, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,7 +589,7 @@ static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet,
|
||||||
|
|
||||||
if (filter)
|
if (filter)
|
||||||
filter->feed->cb.sec(&packet[2], length - 2, NULL, 0,
|
filter->feed->cb.sec(&packet[2], length - 2, NULL, 0,
|
||||||
&filter->filter);
|
&filter->filter, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ttusb_dec_process_packet(struct ttusb_dec *dec)
|
static void ttusb_dec_process_packet(struct ttusb_dec *dec)
|
||||||
|
|
|
@ -117,7 +117,7 @@ struct dmx_ts_feed {
|
||||||
* specified by @filter_value that will be used on the filter
|
* specified by @filter_value that will be used on the filter
|
||||||
* match logic.
|
* match logic.
|
||||||
* @filter_mode: Contains a 16 bytes (128 bits) filter mode.
|
* @filter_mode: Contains a 16 bytes (128 bits) filter mode.
|
||||||
* @parent: Pointer to struct dmx_section_feed.
|
* @parent: Back-pointer to struct dmx_section_feed.
|
||||||
* @priv: Pointer to private data of the API client.
|
* @priv: Pointer to private data of the API client.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -130,8 +130,9 @@ struct dmx_section_filter {
|
||||||
u8 filter_value[DMX_MAX_FILTER_SIZE];
|
u8 filter_value[DMX_MAX_FILTER_SIZE];
|
||||||
u8 filter_mask[DMX_MAX_FILTER_SIZE];
|
u8 filter_mask[DMX_MAX_FILTER_SIZE];
|
||||||
u8 filter_mode[DMX_MAX_FILTER_SIZE];
|
u8 filter_mode[DMX_MAX_FILTER_SIZE];
|
||||||
struct dmx_section_feed *parent; /* Back-pointer */
|
struct dmx_section_feed *parent;
|
||||||
void *priv; /* Pointer to private data of the API client */
|
|
||||||
|
void *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,6 +194,10 @@ struct dmx_section_feed {
|
||||||
* @buffer2: Pointer to the tail of the filtered TS packets, or NULL.
|
* @buffer2: Pointer to the tail of the filtered TS packets, or NULL.
|
||||||
* @buffer2_length: Length of the TS data in buffer2.
|
* @buffer2_length: Length of the TS data in buffer2.
|
||||||
* @source: Indicates which TS feed is the source of the callback.
|
* @source: Indicates which TS feed is the source of the callback.
|
||||||
|
* @buffer_flags: Address where buffer flags are stored. Those are
|
||||||
|
* used to report discontinuity users via DVB
|
||||||
|
* memory mapped API, as defined by
|
||||||
|
* &enum dmx_buffer_flags.
|
||||||
*
|
*
|
||||||
* This function callback prototype, provided by the client of the demux API,
|
* This function callback prototype, provided by the client of the demux API,
|
||||||
* is called from the demux code. The function is only called when filtering
|
* is called from the demux code. The function is only called when filtering
|
||||||
|
@ -245,7 +250,8 @@ typedef int (*dmx_ts_cb)(const u8 *buffer1,
|
||||||
size_t buffer1_length,
|
size_t buffer1_length,
|
||||||
const u8 *buffer2,
|
const u8 *buffer2,
|
||||||
size_t buffer2_length,
|
size_t buffer2_length,
|
||||||
struct dmx_ts_feed *source);
|
struct dmx_ts_feed *source,
|
||||||
|
u32 *buffer_flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* typedef dmx_section_cb - DVB demux TS filter callback function prototype
|
* typedef dmx_section_cb - DVB demux TS filter callback function prototype
|
||||||
|
@ -261,6 +267,10 @@ typedef int (*dmx_ts_cb)(const u8 *buffer1,
|
||||||
* including headers and CRC.
|
* including headers and CRC.
|
||||||
* @source: Indicates which section feed is the source of the
|
* @source: Indicates which section feed is the source of the
|
||||||
* callback.
|
* callback.
|
||||||
|
* @buffer_flags: Address where buffer flags are stored. Those are
|
||||||
|
* used to report discontinuity users via DVB
|
||||||
|
* memory mapped API, as defined by
|
||||||
|
* &enum dmx_buffer_flags.
|
||||||
*
|
*
|
||||||
* This function callback prototype, provided by the client of the demux API,
|
* This function callback prototype, provided by the client of the demux API,
|
||||||
* is called from the demux code. The function is only called when
|
* is called from the demux code. The function is only called when
|
||||||
|
@ -286,7 +296,8 @@ typedef int (*dmx_section_cb)(const u8 *buffer1,
|
||||||
size_t buffer1_len,
|
size_t buffer1_len,
|
||||||
const u8 *buffer2,
|
const u8 *buffer2,
|
||||||
size_t buffer2_len,
|
size_t buffer2_len,
|
||||||
struct dmx_section_filter *source);
|
struct dmx_section_filter *source,
|
||||||
|
u32 *buffer_flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DVB Front-End
|
* DVB Front-End
|
||||||
|
|
|
@ -115,6 +115,8 @@ struct dvb_demux_filter {
|
||||||
* @pid: PID to be filtered.
|
* @pid: PID to be filtered.
|
||||||
* @timeout: feed timeout.
|
* @timeout: feed timeout.
|
||||||
* @filter: pointer to &struct dvb_demux_filter.
|
* @filter: pointer to &struct dvb_demux_filter.
|
||||||
|
* @buffer_flags: Buffer flags used to report discontinuity users via DVB
|
||||||
|
* memory mapped API, as defined by &enum dmx_buffer_flags.
|
||||||
* @ts_type: type of TS, as defined by &enum ts_filter_type.
|
* @ts_type: type of TS, as defined by &enum ts_filter_type.
|
||||||
* @pes_type: type of PES, as defined by &enum dmx_ts_pes.
|
* @pes_type: type of PES, as defined by &enum dmx_ts_pes.
|
||||||
* @cc: MPEG-TS packet continuity counter
|
* @cc: MPEG-TS packet continuity counter
|
||||||
|
@ -145,6 +147,8 @@ struct dvb_demux_feed {
|
||||||
ktime_t timeout;
|
ktime_t timeout;
|
||||||
struct dvb_demux_filter *filter;
|
struct dvb_demux_filter *filter;
|
||||||
|
|
||||||
|
u32 buffer_flags;
|
||||||
|
|
||||||
enum ts_filter_type ts_type;
|
enum ts_filter_type ts_type;
|
||||||
enum dmx_ts_pes pes_type;
|
enum dmx_ts_pes pes_type;
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,12 @@ struct dvb_buffer {
|
||||||
* @nonblocking:
|
* @nonblocking:
|
||||||
* If different than zero, device is operating on non-blocking
|
* If different than zero, device is operating on non-blocking
|
||||||
* mode.
|
* mode.
|
||||||
|
* @flags: buffer flags as defined by &enum dmx_buffer_flags.
|
||||||
|
* Filled only at &DMX_DQBUF. &DMX_QBUF should zero this field.
|
||||||
|
* @count: monotonic counter for filled buffers. Helps to identify
|
||||||
|
* data stream loses. Filled only at &DMX_DQBUF. &DMX_QBUF should
|
||||||
|
* zero this field.
|
||||||
|
*
|
||||||
* @name: name of the device type. Currently, it can either be
|
* @name: name of the device type. Currently, it can either be
|
||||||
* "dvr" or "demux_filter".
|
* "dvr" or "demux_filter".
|
||||||
*/
|
*/
|
||||||
|
@ -100,6 +106,10 @@ struct dvb_vb2_ctx {
|
||||||
int buf_siz;
|
int buf_siz;
|
||||||
int buf_cnt;
|
int buf_cnt;
|
||||||
int nonblocking;
|
int nonblocking;
|
||||||
|
|
||||||
|
enum dmx_buffer_flags flags;
|
||||||
|
u32 count;
|
||||||
|
|
||||||
char name[DVB_VB2_NAME_MAX + 1];
|
char name[DVB_VB2_NAME_MAX + 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +124,7 @@ static inline int dvb_vb2_release(struct dvb_vb2_ctx *ctx)
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
#define dvb_vb2_is_streaming(ctx) (0)
|
#define dvb_vb2_is_streaming(ctx) (0)
|
||||||
#define dvb_vb2_fill_buffer(ctx, file, wait) (0)
|
#define dvb_vb2_fill_buffer(ctx, file, wait, flags) (0)
|
||||||
|
|
||||||
static inline __poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx,
|
static inline __poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx,
|
||||||
struct file *file,
|
struct file *file,
|
||||||
|
@ -153,9 +163,13 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx);
|
||||||
* @ctx: control struct for VB2 handler
|
* @ctx: control struct for VB2 handler
|
||||||
* @src: place where the data is stored
|
* @src: place where the data is stored
|
||||||
* @len: number of bytes to be copied from @src
|
* @len: number of bytes to be copied from @src
|
||||||
|
* @buffer_flags:
|
||||||
|
* pointer to buffer flags as defined by &enum dmx_buffer_flags.
|
||||||
|
* can be NULL.
|
||||||
*/
|
*/
|
||||||
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
|
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
|
||||||
const unsigned char *src, int len);
|
const unsigned char *src, int len,
|
||||||
|
enum dmx_buffer_flags *buffer_flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dvb_vb2_poll - Wrapper to vb2_core_streamon() for Digital TV
|
* dvb_vb2_poll - Wrapper to vb2_core_streamon() for Digital TV
|
||||||
|
|
Loading…
Reference in a new issue