| // SPDX-License-Identifier: GPL-2.0 |
| #include "dvb_filter.h" |
| #include "av7110_ipack.h" |
| #include <linux/string.h> /* for memcpy() */ |
| #include <linux/vmalloc.h> |
| |
| |
| void av7110_ipack_reset(struct ipack *p) |
| { |
| p->found = 0; |
| p->cid = 0; |
| p->plength = 0; |
| p->flag1 = 0; |
| p->flag2 = 0; |
| p->hlength = 0; |
| p->mpeg = 0; |
| p->check = 0; |
| p->which = 0; |
| p->done = 0; |
| p->count = 0; |
| } |
| |
| |
| int av7110_ipack_init(struct ipack *p, int size, |
| void (*func)(u8 *buf, int size, void *priv)) |
| { |
| if (!(p->buf = vmalloc(size*sizeof(u8)))) { |
| printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); |
| return -ENOMEM; |
| } |
| p->size = size; |
| p->func = func; |
| p->repack_subids = 0; |
| av7110_ipack_reset(p); |
| return 0; |
| } |
| |
| |
| void av7110_ipack_free(struct ipack *p) |
| { |
| vfree(p->buf); |
| } |
| |
| |
| static void send_ipack(struct ipack *p) |
| { |
| int off; |
| struct dvb_audio_info ai; |
| int ac3_off = 0; |
| int streamid = 0; |
| int nframes = 0; |
| int f = 0; |
| |
| switch (p->mpeg) { |
| case 2: |
| if (p->count < 10) |
| return; |
| p->buf[3] = p->cid; |
| p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); |
| p->buf[5] = (u8)((p->count - 6) & 0x00ff); |
| if (p->repack_subids && p->cid == PRIVATE_STREAM1) { |
| off = 9 + p->buf[8]; |
| streamid = p->buf[off]; |
| if ((streamid & 0xf8) == 0x80) { |
| ai.off = 0; |
| ac3_off = ((p->buf[off + 2] << 8)| |
| p->buf[off + 3]); |
| if (ac3_off < p->count) |
| f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, |
| p->count - ac3_off, &ai, 0); |
| if (!f) { |
| nframes = (p->count - off - 3 - ac3_off) / |
| ai.framesize + 1; |
| p->buf[off + 2] = (ac3_off >> 8) & 0xff; |
| p->buf[off + 3] = (ac3_off) & 0xff; |
| p->buf[off + 1] = nframes; |
| ac3_off += nframes * ai.framesize - p->count; |
| } |
| } |
| } |
| p->func(p->buf, p->count, p->data); |
| |
| p->buf[6] = 0x80; |
| p->buf[7] = 0x00; |
| p->buf[8] = 0x00; |
| p->count = 9; |
| if (p->repack_subids && p->cid == PRIVATE_STREAM1 |
| && (streamid & 0xf8) == 0x80) { |
| p->count += 4; |
| p->buf[9] = streamid; |
| p->buf[10] = (ac3_off >> 8) & 0xff; |
| p->buf[11] = (ac3_off) & 0xff; |
| p->buf[12] = 0; |
| } |
| break; |
| |
| case 1: |
| if (p->count < 8) |
| return; |
| p->buf[3] = p->cid; |
| p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); |
| p->buf[5] = (u8)((p->count - 6) & 0x00ff); |
| p->func(p->buf, p->count, p->data); |
| |
| p->buf[6] = 0x0f; |
| p->count = 7; |
| break; |
| } |
| } |
| |
| |
| void av7110_ipack_flush(struct ipack *p) |
| { |
| if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) |
| return; |
| p->plength = p->found - 6; |
| p->found = 0; |
| send_ipack(p); |
| av7110_ipack_reset(p); |
| } |
| |
| |
| static void write_ipack(struct ipack *p, const u8 *data, int count) |
| { |
| u8 headr[3] = { 0x00, 0x00, 0x01 }; |
| |
| if (p->count < 6) { |
| memcpy(p->buf, headr, 3); |
| p->count = 6; |
| } |
| |
| if (p->count + count < p->size){ |
| memcpy(p->buf+p->count, data, count); |
| p->count += count; |
| } else { |
| int rest = p->size - p->count; |
| memcpy(p->buf+p->count, data, rest); |
| p->count += rest; |
| send_ipack(p); |
| if (count - rest > 0) |
| write_ipack(p, data + rest, count - rest); |
| } |
| } |
| |
| |
| int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) |
| { |
| int l; |
| int c = 0; |
| |
| while (c < count && (p->mpeg == 0 || |
| (p->mpeg == 1 && p->found < 7) || |
| (p->mpeg == 2 && p->found < 9)) |
| && (p->found < 5 || !p->done)) { |
| switch (p->found) { |
| case 0: |
| case 1: |
| if (buf[c] == 0x00) |
| p->found++; |
| else |
| p->found = 0; |
| c++; |
| break; |
| case 2: |
| if (buf[c] == 0x01) |
| p->found++; |
| else if (buf[c] == 0) |
| p->found = 2; |
| else |
| p->found = 0; |
| c++; |
| break; |
| case 3: |
| p->cid = 0; |
| switch (buf[c]) { |
| case PROG_STREAM_MAP: |
| case PRIVATE_STREAM2: |
| case PROG_STREAM_DIR: |
| case ECM_STREAM : |
| case EMM_STREAM : |
| case PADDING_STREAM : |
| case DSM_CC_STREAM : |
| case ISO13522_STREAM: |
| p->done = 1; |
| /* fall through */ |
| case PRIVATE_STREAM1: |
| case VIDEO_STREAM_S ... VIDEO_STREAM_E: |
| case AUDIO_STREAM_S ... AUDIO_STREAM_E: |
| p->found++; |
| p->cid = buf[c]; |
| c++; |
| break; |
| default: |
| p->found = 0; |
| break; |
| } |
| break; |
| |
| case 4: |
| if (count-c > 1) { |
| p->plen[0] = buf[c]; |
| c++; |
| p->plen[1] = buf[c]; |
| c++; |
| p->found += 2; |
| p->plength = (p->plen[0] << 8) | p->plen[1]; |
| } else { |
| p->plen[0] = buf[c]; |
| p->found++; |
| return count; |
| } |
| break; |
| case 5: |
| p->plen[1] = buf[c]; |
| c++; |
| p->found++; |
| p->plength = (p->plen[0] << 8) | p->plen[1]; |
| break; |
| case 6: |
| if (!p->done) { |
| p->flag1 = buf[c]; |
| c++; |
| p->found++; |
| if ((p->flag1 & 0xc0) == 0x80) |
| p->mpeg = 2; |
| else { |
| p->hlength = 0; |
| p->which = 0; |
| p->mpeg = 1; |
| p->flag2 = 0; |
| } |
| } |
| break; |
| |
| case 7: |
| if (!p->done && p->mpeg == 2) { |
| p->flag2 = buf[c]; |
| c++; |
| p->found++; |
| } |
| break; |
| |
| case 8: |
| if (!p->done && p->mpeg == 2) { |
| p->hlength = buf[c]; |
| c++; |
| p->found++; |
| } |
| break; |
| } |
| } |
| |
| if (c == count) |
| return count; |
| |
| if (!p->plength) |
| p->plength = MMAX_PLENGTH - 6; |
| |
| if (p->done || ((p->mpeg == 2 && p->found >= 9) || |
| (p->mpeg == 1 && p->found >= 7))) { |
| switch (p->cid) { |
| case AUDIO_STREAM_S ... AUDIO_STREAM_E: |
| case VIDEO_STREAM_S ... VIDEO_STREAM_E: |
| case PRIVATE_STREAM1: |
| if (p->mpeg == 2 && p->found == 9) { |
| write_ipack(p, &p->flag1, 1); |
| write_ipack(p, &p->flag2, 1); |
| write_ipack(p, &p->hlength, 1); |
| } |
| |
| if (p->mpeg == 1 && p->found == 7) |
| write_ipack(p, &p->flag1, 1); |
| |
| if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && |
| p->found < 14) { |
| while (c < count && p->found < 14) { |
| p->pts[p->found - 9] = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| } |
| if (c == count) |
| return count; |
| } |
| |
| if (p->mpeg == 1 && p->which < 2000) { |
| |
| if (p->found == 7) { |
| p->check = p->flag1; |
| p->hlength = 1; |
| } |
| |
| while (!p->which && c < count && |
| p->check == 0xff){ |
| p->check = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->hlength++; |
| } |
| |
| if (c == count) |
| return count; |
| |
| if ((p->check & 0xc0) == 0x40 && !p->which) { |
| p->check = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->hlength++; |
| |
| p->which = 1; |
| if (c == count) |
| return count; |
| p->check = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->hlength++; |
| p->which = 2; |
| if (c == count) |
| return count; |
| } |
| |
| if (p->which == 1) { |
| p->check = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->hlength++; |
| p->which = 2; |
| if (c == count) |
| return count; |
| } |
| |
| if ((p->check & 0x30) && p->check != 0xff) { |
| p->flag2 = (p->check & 0xf0) << 2; |
| p->pts[0] = p->check; |
| p->which = 3; |
| } |
| |
| if (c == count) |
| return count; |
| if (p->which > 2){ |
| if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { |
| while (c < count && p->which < 7) { |
| p->pts[p->which - 2] = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->which++; |
| p->hlength++; |
| } |
| if (c == count) |
| return count; |
| } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { |
| while (c < count && p->which < 12) { |
| if (p->which < 7) |
| p->pts[p->which - 2] = buf[c]; |
| write_ipack(p, buf + c, 1); |
| c++; |
| p->found++; |
| p->which++; |
| p->hlength++; |
| } |
| if (c == count) |
| return count; |
| } |
| p->which = 2000; |
| } |
| |
| } |
| |
| while (c < count && p->found < p->plength + 6) { |
| l = count - c; |
| if (l + p->found > p->plength + 6) |
| l = p->plength + 6 - p->found; |
| write_ipack(p, buf + c, l); |
| p->found += l; |
| c += l; |
| } |
| break; |
| } |
| |
| |
| if (p->done) { |
| if (p->found + count - c < p->plength + 6) { |
| p->found += count - c; |
| c = count; |
| } else { |
| c += p->plength + 6 - p->found; |
| p->found = p->plength + 6; |
| } |
| } |
| |
| if (p->plength && p->found == p->plength + 6) { |
| send_ipack(p); |
| av7110_ipack_reset(p); |
| if (c < count) |
| av7110_ipack_instant_repack(buf + c, count - c, p); |
| } |
| } |
| return count; |
| } |