Files
2026-08-24 10:03:24 +08:00

1053 lines
34 KiB
C

/*
* Copyright (C) 2014-2025 Amlogic, Inc. All rights reserved.
*
* All information contained herein is Amlogic confidential.
*
* This software is provided to you pursuant to Software License Agreement
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
* only in accordance with the terms of this agreement.
*
* Redistribution and use in source and binary forms, with or without
* modification is strictly prohibited without prior written permission from
* Amlogic.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "Userdata"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "CcSourceManager.h"
#include "MediaSyncInterface.h"
#include "StreamUtils.h"
#include "SubtitleLog.h"
#include "UserdataDriver.h"
#define MAX_CC_NUM 16
#define MAX_CC_DATA_LEN (1024 * 5 + 4)
#define MAX_DUMP_DATA_LENGTH 1024
#define CC_DEFAULT_PLAYER_ID 0
#define PTS_DIFF_DURATION_S (60 * 90000) // 60s
#define IS_H264(p) ((p[0] == 0xb5 && p[3] == 0x47 && p[4] == 0x41 && p[5] == 0x39 && p[6] == 0x34))
#define IS_H265(p) ((p[0] == 0xb5 && p[3] == 0x47 && p[4] == 0x41 && p[5] == 0x39 && p[6] == 0x34))
#define IS_DIRECTV(p) ((p[0] == 0xb5 && p[1] == 0x00 && p[2] == 0x2f))
#define IS_AVS(p) ((p[0] == 0x47) && (p[1] == 0x41) && (p[2] == 0x39) && (p[3] == 0x34))
#define IS_ATSC(p) ((p[0] == 0x47) && (p[1] == 0x41) && (p[2] == 0x39) && (p[3] == 0x34))
#define IS_SCTE(p) ((p[0] == 0x3) && ((p[1] & 0x7f) == 1))
#define IS_AFD(p) ((p[0] == 0x44) && (p[1] == 0x54) && (p[2] == 0x47) && (p[3] == 0x31))
#define IS_H264_AFD(p) \
((p[0] == 0xb5) && (p[3] == 0x44) && (p[4] == 0x54) && (p[5] == 0x47) && (p[6] == 0x31))
/****************************************************************************
* Type definitions
***************************************************************************/
typedef enum {
INVALID_TYPE = 0,
CC_TYPE_FOR_JUDGE = 10,
MPEG_CC_TYPE,
H264_CC_TYPE,
DIRECTV_CC_TYPE,
AVS_CC_TYPE,
SCTE_CC_TYPE,
AFD_TYPE_FOR_JUDGE = 100,
MPEG_AFD_TYPE,
H264_AFD_TYPE,
USERDATA_TYPE_MAX,
} userdata_type;
enum VideoDecoderType {
VIDEO_DECODER_TYPE_INVALID = -1,
VIDEO_DECODER_TYPE_AMSTREAM_USERDATA = 0,
VIDEO_DECODER_TYPE_V4L = 1,
};
typedef enum {
ORDER_TYPE_PTS = 0,
ORDER_TYPE_POC = 1,
} FrameOrderType;
#define IS_AFD_TYPE(p) (p > AFD_TYPE_FOR_JUDGE && p < USERDATA_TYPE_MAX)
#define IS_CC_TYPE(p) (p > CC_TYPE_FOR_JUDGE && p < AFD_TYPE_FOR_JUDGE)
#define GET_ATSC_FLAG(hdr_ptr, type) \
((type == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA) \
? ((aml_ud_header_t *)(hdr_ptr))->atsc_flag \
: ((aml_v4l_ud_header_t *)(hdr_ptr))->atsc_flag)
#define GET_CC_DATA_START(hdr_ptr, type) \
((type == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA) \
? ((aml_ud_header_t *)(hdr_ptr))->cc_data_start \
: ((aml_v4l_ud_header_t *)(hdr_ptr))->cc_data_start)
typedef enum {
/* 0 forbidden */
I_TYPE = 1,
P_TYPE = 2,
B_TYPE = 3,
D_TYPE = 4,
/* 5 ... 7 reserved */
} picture_coding_type;
typedef struct AM_CCData_s AM_CCData;
struct AM_CCData_s {
AM_CCData *next;
uint8_t *buf;
int64_t pts;
int64_t duration;
int pts_valid;
int size;
int cap;
int poc;
};
typedef struct {
uint32_t picture_structure : 16;
uint32_t temporal_reference : 10;
uint32_t picture_coding_type : 3;
uint32_t reserved : 3;
uint32_t index : 16;
uint32_t offset : 16;
uint8_t atsc_flag[4];
uint8_t cc_data_start[4];
} aml_ud_header_t;
typedef struct {
uint8_t atsc_flag[4];
uint8_t cc_data_start[4];
} aml_v4l_ud_header_t;
typedef struct {
pthread_t th;
int fd;
int vfmt;
int playerId;
int mediasync_id;
int videoDecoderType;
bool running;
AM_CCData *cc_list;
AM_CCData *free_list;
int cc_num;
userdata_type format;
int curr_poc;
int64_t curr_pts;
int64_t curr_duration;
int scte_enable;
int mode;
void *mediaSync;
} AM_UDDrvData;
#define MOD_ON(__mod, __mask) (__mod & __mask)
#define MOD_ON_CC(__mod) MOD_ON(__mod, USERDATA_MODE_CC)
#define MOD_ON_AFD(__mod) MOD_ON(__mod, USERDATA_MODE_AFD)
/****************************************************************************
* Static data definitions
***************************************************************************/
static int aml_open(UserdataDeviceType *dev, const UserdataOpenParaType *para);
static int aml_close(UserdataDeviceType *dev);
static int aml_set_mode(UserdataDeviceType *dev, int mode);
static int aml_get_mode(UserdataDeviceType *dev, int *mode);
static int aml_set_param(UserdataDeviceType *dev, int para);
const UserdataDriverType aml_ud_drv = {
.open = aml_open,
.close = aml_close,
.set_mode = aml_set_mode,
.get_mode = aml_get_mode,
.set_param = aml_set_param,
};
int64_t am_get_video_pts(void *media_sync) {
int64_t value = 0;
if (media_sync != NULL) {
MediaSync_getTrackMediaTime(media_sync, &value);
return us2Pts(value);
}
return 0;
}
static void dump_cc_data(char *who, uint8_t *buff, int size) {
int i = 0;
char buf[4096] = {'\0'};
if (size > 1024) size = 1024;
for (i = 0; i < size; i++) {
sprintf(buf + i * 3, "%02x ", buff[i]);
}
SUBTITLE_LOGI("CC DUMP:who:%s :%s", who, buf);
}
static void aml_free_cc_data(AM_CCData *cc) {
if (cc->buf) free(cc->buf);
free(cc);
}
static uint8_t scte20_char_map[256];
static uint8_t scte20_get_char(uint8_t c) {
// Builds a lookup table (`scte20_char_map`)
// that maps each 8-bit value to its bit-reversed counterpart
if (scte20_char_map[1] == 0) {
int i;
for (i = 0; i < 256; i++) {
uint8_t v1, v2;
int b;
v1 = i;
v2 = 0;
for (b = 0; b < 8; b++) {
if (v1 & (1 << b)) v2 |= (1 << (7 - b));
}
scte20_char_map[i] = v2;
}
}
return scte20_char_map[c];
}
static userdata_type aml_check_userdata_format(uint8_t *buf, int vfmt, int len,
int videoDecoderType) {
if (len < 8) return INVALID_TYPE;
// vfmt:
// 0 for MPEG
// 2 for H264
// 7 for AVS
if (vfmt == 2) {
if (IS_H264(buf) || IS_H265(buf)) {
SUBTITLE_LOGI("CC format is h264_or_h265_cc_type");
return H264_CC_TYPE;
} else if (IS_DIRECTV(buf)) {
SUBTITLE_LOGI("CC format is directv_cc_type");
return DIRECTV_CC_TYPE;
} else if (IS_H264_AFD(buf)) {
return H264_AFD_TYPE;
}
} else if (vfmt == 7) {
if (IS_AVS(buf)) {
SUBTITLE_LOGI("CC format is avs_cc_type");
return AVS_CC_TYPE;
}
} else if (vfmt == 0) {
int size = videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA
? (int)sizeof(aml_ud_header_t)
: (int)sizeof(aml_v4l_ud_header_t);
if (len >= size) {
void *hdr = NULL;
if (videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA) {
hdr = (aml_ud_header_t *)buf;
} else {
hdr = (aml_v4l_ud_header_t *)buf;
}
if (IS_ATSC(GET_ATSC_FLAG(hdr, videoDecoderType))) {
SUBTITLE_LOGI("CC format is mpeg_cc_type");
return MPEG_CC_TYPE;
} else if (IS_SCTE(GET_ATSC_FLAG(hdr, videoDecoderType))) {
SUBTITLE_LOGI("CC format is scte_cc_type");
return SCTE_CC_TYPE;
} else if (IS_AFD(GET_ATSC_FLAG(hdr, videoDecoderType))) {
return MPEG_AFD_TYPE;
}
}
} // else {
// SUBTITLE_LOGE("vfmt not handled");
// }
return INVALID_TYPE;
}
static void aml_write_userdata(UserdataDeviceType *dev, uint8_t *buffer, int buffer_len,
int64_t pts, int pts_valid, int64_t duration, int poc) {
uint8_t userdata_with_pts[MAX_CC_DATA_LEN];
AM_UDDrvData *ud = dev->drv_data;
if (pts_valid == 0) {
pts = ud->curr_pts + ud->curr_duration;
}
memcpy(userdata_with_pts, &pts, sizeof(pts));
memcpy(userdata_with_pts + sizeof(pts), buffer, buffer_len);
if (isSubtitleDumpAvailable()) {
char display_buffer[MAX_DUMP_DATA_LENGTH * 4] = {0};
int size = buffer_len;
if (size > 1024) size = 1024;
for (int i = 0; i < size && i < MAX_DUMP_DATA_LENGTH; i++) {
sprintf(&display_buffer[i * 3], " %02x", buffer[i]);
}
SUBTITLE_LOGI("after reorder: poc:%d, pts:%" PRId64 ", dataSize:%d data:%s", poc, pts, size,
display_buffer);
}
dev->write_package(dev, userdata_with_pts, buffer_len + sizeof(pts));
ud->curr_pts = pts;
ud->curr_duration = duration;
}
static void aml_flush_cc_data(UserdataDeviceType *dev) {
AM_UDDrvData *ud = dev->drv_data;
AM_CCData *cc, *ncc;
char buf[256];
char *pp = buf;
int left = sizeof(buf), i, pr;
for (cc = ud->cc_list; cc; cc = ncc) {
ncc = cc->next;
for (i = 0; i < cc->size; i++) {
pr = snprintf(pp, left, "%02x ", cc->buf[i]);
if (pr < left) {
pp += pr;
left -= pr;
}
}
SUBTITLE_LOGD("cc_write_package decode:%s", buf);
aml_write_userdata(dev, cc->buf, cc->size, cc->pts, cc->pts_valid, cc->duration, cc->poc);
cc->next = ud->free_list;
ud->free_list = cc;
}
ud->cc_list = NULL;
ud->cc_num = 0;
ud->curr_poc = -1;
}
static void aml_add_cc_data(UserdataDeviceType *dev, int poc, int type, uint8_t *p, int len,
int64_t pts, int pts_valid, int64_t duration, FrameOrderType oderType) {
if (!dev) {
SUBTITLE_LOGE("dev is NULL!");
return;
}
AM_UDDrvData *ud = dev->drv_data;
if (!ud) {
SUBTITLE_LOGE("dev->drv_data is NULL!");
return;
}
if (!p) {
SUBTITLE_LOGE("Input data pointer p is NULL!");
return;
}
AM_CCData **pcc, *cc;
// When CC data count reaches maximum limit, remove the oldest entry
if (ud->cc_num >= MAX_CC_NUM) {
#if 0
aml_flush_cc_data(dev);
#else
cc = ud->cc_list;
if (cc) {
// Write user data and move node to free list
aml_write_userdata(dev, cc->buf, cc->size, cc->pts, cc->pts_valid, cc->duration,
cc->poc);
ud->cc_list = cc->next;
cc->next = ud->free_list;
ud->free_list = cc;
ud->cc_num--;
}
#endif
}
if (isSubtitleDumpAvailable()) {
char buf[256] = {0};
char *pp = buf;
int left = sizeof(buf), i = 0, pr = 0;
for (i = 0; i < len; i++) {
pr = snprintf(pp, left, "%02x ", p[i]);
if (pr < left) {
pp += pr;
left -= pr;
}
}
SUBTITLE_LOGI("CC oder type:%d, poc:%d ptype:%d data:%s", oderType, poc, type, buf);
}
pcc = &ud->cc_list;
if (*pcc && poc < ((*pcc)->poc - 30)) {
aml_flush_cc_data(dev);
}
// Traverse list to find insertion position for current CC data (sorted by pts and poc)
while ((cc = *pcc)) {
if (oderType == ORDER_TYPE_POC) {
if (cc->poc > poc) break;
} else {
if (cc->pts == pts) {
if (cc->poc > poc) break;
} else if ((cc->pts > pts) || (cc->poc > poc)) {
if (cc->poc > poc) {
SUBTITLE_LOGI("cc->pts:%" PRId64 ", pts:%" PRId64 ", cc->poc:%d, poc:%d",
cc->pts, pts, cc->poc, poc);
}
break;
}
}
pcc = &cc->next;
}
// Get node from free list; allocate new node if no free nodes available
if (ud->free_list) {
cc = ud->free_list;
ud->free_list = cc->next;
} else {
cc = malloc(sizeof(AM_CCData));
if (!cc) {
SUBTITLE_LOGI("Error! CC data node memory allocation failed!");
return;
}
// Initialize new node members
cc->buf = NULL;
cc->size = 0;
cc->cap = 0;
cc->poc = 0;
cc->next = NULL;
}
// Reallocate memory if current node buffer capacity is insufficient
if (cc->cap < len) {
void *new_buf = realloc(cc->buf, len);
if (!new_buf) {
SUBTITLE_LOGE("Error! CC buffer reallocation failed!");
// Recycle node to free list (prevent memory leak)
cc->next = ud->free_list;
ud->free_list = cc;
return;
}
cc->buf = new_buf;
cc->cap = len;
}
if (!p) {
SUBTITLE_LOGE("Error! Input data pointer p is NULL!");
// Recycle node to free list
cc->next = ud->free_list;
ud->free_list = cc;
return;
}
// Copy data to node buffer
memcpy(cc->buf, p, len);
// Set node properties and insert into list
cc->size = len;
cc->poc = poc;
cc->pts = pts;
cc->pts_valid = pts_valid;
cc->duration = duration;
cc->next = *pcc;
*pcc = cc;
ud->cc_num++;
}
static void aml_mpeg_userdata_package(UserdataDeviceType *dev, int poc, int type, uint8_t *p,
int len, int64_t pts, int pts_valid, int64_t duration) {
AM_UDDrvData *ud = dev->drv_data;
if (isSubtitleDumpAvailable()) {
char display_buffer[MAX_DUMP_DATA_LENGTH * 4];
for (int i = 0; i < len && i < MAX_DUMP_DATA_LENGTH; i++) {
sprintf(&display_buffer[i * 3], " %02x", p[i]);
}
SUBTITLE_LOGI("mpeg_process type:%d, pts:%" PRId64 ", ud->curr_pts:%" PRId64
", pts_valid:%d,"
"len:%d, poc:%d, curr_poc:%d, data:%s",
type, pts, ud->curr_pts, pts_valid, len, poc, ud->curr_poc, display_buffer);
}
if (len < 5) return;
if (p[4] != 3) return;
if (type == I_TYPE) aml_flush_cc_data(dev);
// now we judge stream from header flag based on pts diff,
// then flush cc data.
if ((ud->curr_pts - pts) > PTS_DIFF_DURATION_S) {
SUBTITLE_LOGI("Pts diff(%" PRId64 ") is more than %d, currt_pts:%" PRId64 ", pts:%" PRId64
", flush cc data!",
ud->curr_pts - pts, PTS_DIFF_DURATION_S, ud->curr_pts, pts);
aml_flush_cc_data(dev);
}
if (poc == ud->curr_poc + 1 || poc == ud->curr_poc) {
AM_CCData *cc, **pcc;
aml_write_userdata(dev, p, len, pts, pts_valid, duration, poc);
ud->curr_poc = poc;
pcc = &ud->cc_list;
while ((cc = *pcc)) {
if (ud->curr_poc + 1 != cc->poc) break;
aml_write_userdata(dev, cc->buf, cc->size, cc->pts, cc->pts_valid, cc->duration,
cc->poc);
*pcc = cc->next;
ud->curr_poc++;
cc->next = ud->free_list;
ud->free_list = cc;
}
return;
}
aml_add_cc_data(dev, poc, type, p, len, pts, pts_valid, duration, ORDER_TYPE_PTS);
}
static int aml_process_scte_userdata(UserdataDeviceType *dev, uint8_t *data, int len,
uint32_t ptsVersion, struct userdata_meta_info_t *meta_info) {
int cc_count = 0, cnt;
uint8_t cc_data[64] = {0};
uint8_t *scte_head;
uint8_t *scte_head_search_position;
int head_posi = 0;
int prio, field, line, cc1, cc2, mark, size, ptype, ref;
int bits = 0, array_position = 0;
uint8_t *p = NULL;
int left_bits;
uint32_t v;
int flags;
int64_t pts;
int top_bit_value, top_bit_valid;
AM_UDDrvData *ud = dev->drv_data;
flags = meta_info->flags;
pts = 0; // meta_info->vpts;
if (ptsVersion == 2) {
pts = meta_info->vpts_64;
} else {
SUBTITLE_LOGE(
"[subtitle_unsupported_flag] doesn't support 32bit PTS any more,"
"please update the latest decoder driver, ptsVersion = %d",
ptsVersion);
return -1;
}
if (ud->scte_enable != 1) return len;
v = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
top_bit_valid = flags & (1 << 10);
top_bit_value = flags & (1 << 11);
if (top_bit_valid == 0) top_bit_value = 1;
ref = meta_info->poc_number;
ptype = (meta_info->flags >> 7) & 0x7;
if (isSubtitleDumpAvailable()) {
char display_buffer[MAX_DUMP_DATA_LENGTH] = {0};
int maxDumpBytes = (MAX_DUMP_DATA_LENGTH - 1) / 3;
for (int i = 0; i < len && i < maxDumpBytes; i++) {
snprintf(&display_buffer[i * 3], MAX_DUMP_DATA_LENGTH - i * 3, " %02x", data[i]);
}
SUBTITLE_LOGI("mpeg_process type:%d, pts:%" PRId64 ", ud->curr_pts:%" PRId64
", pts_valid:%d"
", top_bit_value:%d"
", len:%d,ref:%d poc:%d, curr_poc:%d, data:%s",
ptype, pts, ud->curr_pts, meta_info->vpts_valid, top_bit_value, len, ref,
meta_info->poc_number, ud->curr_poc, display_buffer);
}
if (ptype == I_TYPE) aml_flush_cc_data(dev);
scte_head = data;
while (head_posi < len) {
scte_head_search_position = &scte_head[head_posi];
if (IS_SCTE(scte_head_search_position)) break;
head_posi += 8;
}
if ((len - head_posi) < 8) return len;
p = &data[head_posi + 2];
cc_data[0] = 0x47;
cc_data[1] = 0x41;
cc_data[2] = 0x39;
cc_data[3] = 0x34;
cc_data[4] = 0x3;
left_bits = (len - head_posi) << 3;
#define NST_BITS(v, b, l) (((v) >> (b)) & (0xff >> (8 - (l))))
#define GET(n, b) \
do { \
int off, bs; \
if (bits + b > left_bits) goto error; \
off = bits >> 3; \
bs = bits & 7; \
if (8 - bs >= b) { \
n = NST_BITS(p[off], 8 - bs - b, b); \
} else { \
int n1, n2, b1 = 8 - bs, b2 = b - b1; \
n1 = NST_BITS(p[off], 0, b1); \
n2 = NST_BITS(p[off + 1], 8 - b + b1, b - b1); \
n = (n1 << b2) | n2; \
} \
bits += b; \
} while (0)
GET(cnt, 5);
array_position = 7;
for (int i = 0; i < cnt; i++) {
GET(prio, 2);
GET(field, 2);
GET(line, 5);
GET(cc1, 8);
GET(cc2, 8);
GET(mark, 1);
if (field == 3) field = 1;
SUBTITLE_LOGD("loop %d field %d line %d cc1 %x cc2 %x", i, field, line, cc1, cc2);
if (field == 1)
line = (top_bit_value) ? line + 10 : line + 273;
else if (field == 2)
line = (top_bit_value) ? line + 273 : line + 10;
else
continue;
uint8_t cc_data1 = 0;
uint8_t cc_data2 = 0;
if (line == 21) {
cc_data[array_position] = 4;
cc_data1 = scte20_get_char(cc1);
cc_data2 = scte20_get_char(cc2);
cc_data[array_position + 1] = cc_data1;
cc_data[array_position + 2] = cc_data2;
array_position += 3;
cc_count++;
} else if (line == 284) {
cc_data[array_position] = 4 | 1;
cc_data1 = scte20_get_char(cc1);
cc_data2 = scte20_get_char(cc2);
cc_data[array_position + 1] = cc_data1;
cc_data[array_position + 2] = cc_data2;
array_position += 3;
cc_count++;
}
SUBTITLE_LOGD("cc char info: field %d line %d cc_data1 %x cc_data2 %x, cc_count:%d", field,
line, cc_data1, cc_data2, cc_count);
}
cc_data[5] = 0x40 | cc_count;
size = 7 + cc_count * 3;
if (cc_count > 0)
aml_add_cc_data(dev, ref, ptype, cc_data, size, pts, meta_info->vpts_valid,
meta_info->duration, ORDER_TYPE_POC);
error:
return len;
}
static int aml_process_mpeg_userdata(UserdataDeviceType *dev, uint8_t *data, int len,
uint32_t ptsVersion, struct userdata_meta_info_t *meta_info) {
AM_UDDrvData *ud = dev->drv_data;
uint8_t *pd = data;
int left = len;
int r = 0;
int userdata_length = 0;
int flag = 0;
int poc = -1;
int ptype = -1;
int64_t pts = -1;
flag = meta_info->flags;
if (ptsVersion == 2) {
pts = meta_info->vpts_64;
} else {
SUBTITLE_LOGE(
"[subtitle_unsupported_flag] doesn't support 32bit PTS any more,"
"please update the latest decoder driver, ptsVersion = %d",
ptsVersion);
return -1;
}
int size = ud->videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA
? (int)sizeof(aml_ud_header_t)
: (int)sizeof(aml_v4l_ud_header_t);
while (left >= size) {
void *hdr = NULL;
if (ud->videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA) {
hdr = (aml_ud_header_t *)pd;
} else {
hdr = (aml_v4l_ud_header_t *)pd;
}
if (MOD_ON_CC(ud->mode) && IS_ATSC(GET_ATSC_FLAG(hdr, ud->videoDecoderType))) {
uint8_t *pp;
if (ud->videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA) {
pp = pd + 8; // For coverity, in case p[4] atsc flag overrun
userdata_length = len - r - 8;
} else if (ud->videoDecoderType == VIDEO_DECODER_TYPE_V4L) {
// New v4l2 architecture, no 8 bytes header
pp = pd;
userdata_length = len - r;
}
poc = meta_info->poc_number;
ptype = (meta_info->flags >> 7) & 0x7;
/* We read one packet in one time, so treat entire buffer */
if (!(flag & (1 << 2))) return len;
aml_mpeg_userdata_package(dev, poc, ptype, pp, userdata_length, pts,
meta_info->vpts_valid, meta_info->duration);
r = len;
break;
} else if (MOD_ON_AFD(ud->mode) && IS_AFD(GET_ATSC_FLAG(hdr, ud->videoDecoderType))) {
uint8_t *pafd_hdr = (uint8_t *)GET_CC_DATA_START(hdr, ud->videoDecoderType);
UserdataAFDType afd = *((UserdataAFDType *)(pafd_hdr));
// afd.reserved = afd.pts = 0;
AmlogicEventSignal(dev->dev_no, USERDATA_EVENT_AFD, (void *)&afd);
pd += 8;
left -= 8;
r += 8;
break;
} else {
pd += 8;
left -= 8;
r += 8;
}
}
return r;
}
static void aml_h264_userdata_package(UserdataDeviceType *dev, int poc, int type, uint8_t *p,
int len, int64_t pts, int pts_valid, int64_t duration,
void *media_sync) {
AM_UDDrvData *ud = dev->drv_data;
AM_CCData *cc;
if (isSubtitleDumpAvailable()) {
char display_buffer[MAX_DUMP_DATA_LENGTH * 4] = {0};
for (int i = 0; i < len && i < MAX_DUMP_DATA_LENGTH; i++) {
sprintf(&display_buffer[i * 3], " %02x", p[i]);
}
SUBTITLE_LOGI("h264_process type:%d, pts:%" PRId64 ", ud->curr_pts:%" PRId64
","
"pts_valid:%d, len:%d, poc:%d, curr_poc:%d, data:%s",
type, pts, ud->curr_pts, pts_valid, len, poc, ud->curr_poc, display_buffer);
}
// if (poc == 0) aml_flush_cc_data(dev);
aml_add_cc_data(dev, poc, I_TYPE, p, len, pts, pts_valid, duration, ORDER_TYPE_PTS);
cc = ud->cc_list;
while (cc) {
if (am_get_video_pts(media_sync) > cc->pts) {
aml_write_userdata(dev, cc->buf, cc->size, cc->pts, cc->pts_valid, cc->duration,
cc->poc);
ud->curr_poc = cc->poc;
// If node is chain head, reset chain head
if (cc == ud->cc_list) {
ud->cc_list = cc->next;
} else {
// Delete node from chain
AM_CCData *cc_tmp = ud->cc_list;
while (cc_tmp) {
if (cc_tmp->next == cc) {
cc_tmp->next = cc->next;
break;
} else {
cc_tmp = cc_tmp->next;
}
}
}
cc->next = ud->free_list;
ud->free_list = cc;
ud->cc_num--;
cc = ud->cc_list;
} else {
cc = cc->next;
}
}
}
static int aml_process_h264_userdata(UserdataDeviceType *dev, uint8_t *data, int len,
uint32_t ptsVersion, struct userdata_meta_info_t *meta_info,
void *media_sync) {
AM_UDDrvData *ud = dev->drv_data;
uint8_t *pd = data;
int left = len;
int r = 0;
int poc;
int64_t pts = 0;
poc = meta_info->poc_number;
if (ptsVersion == 2) {
pts = meta_info->vpts_64;
} else {
SUBTITLE_LOGE(
"[subtitle_unsupported_flag] doesn't support 32bit PTS any more,"
"please update the latest decoder driver, ptsVersion = %d",
ptsVersion);
return -1;
}
while (left >= 8) {
if (MOD_ON_CC(ud->mode) && ((IS_H264(pd) || IS_H265(pd) || IS_DIRECTV(pd) || IS_AVS(pd)))) {
int hdr = (ud->format == H264_CC_TYPE) ? 3 : 0;
int pl;
pd += hdr;
pl = 8 + (pd[5] & 0x1f) * 3;
if (pl + hdr > left) {
break;
}
// SUBTITLE_LOGI("CC poc_number:%x hdr:%d pl:%d", poc, hdr, pl);
if (poc == 0) {
aml_flush_cc_data(dev);
}
// aml_add_cc_data(dev, poc, I_TYPE, pd, pl, pts, meta_info->vpts_valid,
// meta_info->duration);
// aml_add_cc_data(dev, poc, I_TYPE, userdata_with_pts, pl+4);
aml_h264_userdata_package(dev, poc, I_TYPE, pd, pl, pts, meta_info->vpts_valid,
meta_info->duration, media_sync);
pd += pl;
left -= pl + hdr;
r += pl + hdr;
} else if (MOD_ON_AFD(ud->mode) && IS_H264_AFD(pd)) {
UserdataAFDType afd = *((UserdataAFDType *)(pd + 7));
// afd.reserved = afd.pts = 0;
AmlogicEventSignal(dev->dev_no, USERDATA_EVENT_AFD, (void *)&afd);
pd += 8;
left -= 8;
r += 8;
break;
} else {
pd += 8;
left -= 8;
r += 8;
}
}
return r;
}
static void userdata_callback(CcSourceMgrHnd handle, void *data) {
int left = 0;
uint8_t *pd;
CcSourceCtx *ctx;
AM_UDDrvData *drvData;
UserdataDeviceType *dev;
struct userdata_meta_info_t metaInfo;
ctx = (CcSourceCtx *)handle;
if (ctx == NULL) {
SUBTITLE_LOGE("ctx is null, return!");
return;
}
dev = (UserdataDeviceType *)ctx->privData;
if (dev == NULL) {
SUBTITLE_LOGE("dev is null, return!");
return;
}
if (ctx->param.srcType != CC_SOURCE_TYPE_AMSTREAM_DEVICE &&
ctx->param.srcType != CC_SOURCE_TYPE_MEDIA_PROXY &&
ctx->param.srcType != CC_SOURCE_TYPE_CCDATASERVER_AMSTREAM_DEVICE &&
ctx->param.srcType != CC_SOURCE_TYPE_CCDATASERVER_MEDIA_PROXY) {
SUBTITLE_LOGE("CC Source type(%d) invalid, don't process data!", ctx->param.srcType);
return;
}
drvData = (AM_UDDrvData *)dev->drv_data;
if (!drvData->running) return;
uint32_t ptsVersion = 0;
if (ctx->param.srcType == CC_SOURCE_TYPE_CCDATASERVER_AMSTREAM_DEVICE ||
ctx->param.srcType == CC_SOURCE_TYPE_CCDATASERVER_MEDIA_PROXY) {
ds_data_userdata_t *dsUd = (ds_data_userdata_t *)data;
pd = dsUd->data;
left = dsUd->data_size;
ptsVersion = dsUd->version;
metaInfo = dsUd->meta_info;
SUBTITLE_LOGI("seq ud[version=%d]: %zd+%d", dsUd->version, sizeof(ds_data_userdata_t),
dsUd->data_size);
} else {
struct CcSourceUserdataParam *devUd = (struct CcSourceUserdataParam *)data;
pd = (uint8_t *)devUd->pbuf_addr;
left = devUd->data_size;
ptsVersion = devUd->version;
metaInfo = devUd->meta_info;
}
if (isSubtitleDumpAvailable()) {
dump_cc_data("userdata_callback", pd, left);
}
int r = 0;
do {
if (!drvData->running) return;
drvData->format = INVALID_TYPE;
while (drvData->format == INVALID_TYPE || IS_AFD_TYPE(drvData->format)) {
if (left < 8) break;
drvData->format =
aml_check_userdata_format(pd, drvData->vfmt, left, drvData->videoDecoderType);
if (!IS_CC_TYPE(drvData->format) && !IS_AFD_TYPE(drvData->format)) {
pd += 8;
left -= 8;
} else {
break;
}
}
if (drvData->format == MPEG_CC_TYPE || drvData->format == MPEG_AFD_TYPE) {
r = aml_process_mpeg_userdata(dev, pd, left, ptsVersion, &metaInfo);
} else if (drvData->format == SCTE_CC_TYPE) {
r = aml_process_scte_userdata(dev, pd, left, ptsVersion, &metaInfo);
} else if (drvData->format != INVALID_TYPE) {
r = aml_process_h264_userdata(dev, pd, left, ptsVersion, &metaInfo, drvData->mediaSync);
}
pd += r;
left -= r;
} while (left >= 8);
}
static CcSourceCtx *ccSourceCtx = NULL;
static void start_cc_data_source(UserdataDeviceType *dev, int playerId, int videoDecoderType) {
SUBTITLE_LOGI("playerid:%d, videoDecoderType:%d", playerId, videoDecoderType);
int srcType = CC_SOURCE_TYPE_AMSTREAM_DEVICE; // Default CC_SOURCE_TYPE_AMSTREAM_DEVICE.
#ifdef NEED_CCDATA_FROM_CCDATABASE
srcType = videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA
? CC_SOURCE_TYPE_CCDATASERVER_AMSTREAM_DEVICE
: CC_SOURCE_TYPE_CCDATASERVER_MEDIA_PROXY;
#else
srcType = videoDecoderType == VIDEO_DECODER_TYPE_AMSTREAM_USERDATA
? CC_SOURCE_TYPE_AMSTREAM_DEVICE
: CC_SOURCE_TYPE_MEDIA_PROXY;
#endif
CcSourceStartParam param = {
.srcType = srcType,
.playerId = playerId,
.uDataCb = userdata_callback,
.owner = "subtitleservice",
};
ccSourceCtx = ccSourceManagerCreate();
if (ccSourceCtx != NULL) {
ccSourceCtx->privData = (void *)dev;
ccSourceManagerStart(ccSourceCtx, &param);
}
}
static int aml_open(UserdataDeviceType *dev, const UserdataOpenParaType *para) {
AM_UDDrvData *ud = malloc(sizeof(AM_UDDrvData));
if (!ud) {
SUBTITLE_LOGI("not enough memory");
return USERDATA_ERROR_NO_MEM;
}
ud->vfmt = para->vfmt;
ud->playerId = para->playerid >= 0 ? para->playerid : CC_DEFAULT_PLAYER_ID;
ud->mediasync_id = para->mediasyncid;
ud->videoDecoderType = para->videoDecoderType;
ud->format = INVALID_TYPE;
ud->cc_list = NULL;
ud->free_list = NULL;
ud->mediaSync = NULL;
ud->running = true;
ud->cc_num = 0;
ud->curr_poc = -1;
ud->scte_enable = 1;
SUBTITLE_LOGI("mediasync id:%d, para->videoDecoderType:%d, ud->playerId:%d", ud->mediasync_id,
para->videoDecoderType, ud->playerId);
if (!para->cc_default_stop) ud->mode = USERDATA_MODE_CC;
if (ud->mediasync_id >= 0) {
ud->mediaSync = MediaSync_create();
SUBTITLE_LOGI("create mediaSync:%p", ud->mediaSync);
if (NULL != ud->mediaSync) {
MediaSync_bindInstance(ud->mediaSync, ud->mediasync_id, MEDIA_SUBTITLE);
}
}
dev->drv_data = ud;
start_cc_data_source(dev, ud->playerId, ud->videoDecoderType);
AmlogicSigHandlerInit();
return AM_SUCCESS;
}
static int aml_close(UserdataDeviceType *dev) {
AM_UDDrvData *ud = dev->drv_data;
AM_CCData *cc, *cc_next;
ud->running = 0;
SUBTITLE_LOGI("aml_close mediaSync:%p, ud->mediasync_id:%d", ud->mediaSync, ud->mediasync_id);
if (ud->mediaSync != NULL) {
MediaSync_destroy(ud->mediaSync);
ud->mediaSync = NULL;
}
ccSourceManagerStop(ccSourceCtx);
ccSourceManagerDestroy(ccSourceCtx);
ccSourceCtx = NULL;
for (cc = ud->cc_list; cc; cc = cc_next) {
cc_next = cc->next;
aml_free_cc_data(cc);
}
for (cc = ud->free_list; cc; cc = cc_next) {
cc_next = cc->next;
aml_free_cc_data(cc);
}
free(ud);
return AM_SUCCESS;
}
static int aml_set_mode(UserdataDeviceType *dev, int mode) {
AM_UDDrvData *ud = dev->drv_data;
if (MOD_ON_CC(mode) != MOD_ON_CC(ud->mode)) {
if (MOD_ON_CC(mode)) {
ud->mode |= USERDATA_MODE_CC;
} else {
ud->mode &= ~USERDATA_MODE_CC;
}
}
if (MOD_ON_AFD(mode) != MOD_ON_AFD(ud->mode)) {
if (MOD_ON_AFD(mode)) {
ud->mode |= USERDATA_MODE_AFD;
} else {
ud->mode &= ~USERDATA_MODE_AFD;
}
}
return AM_SUCCESS;
}
static int aml_get_mode(UserdataDeviceType *dev, int *mode) {
AM_UDDrvData *ud = dev->drv_data;
*mode = ud->mode;
return AM_SUCCESS;
}
static int aml_set_param(UserdataDeviceType *dev, int para) {
if (dev == NULL) return USERDATA_ERROR_INVALID_ARG;
AM_UDDrvData *ud = dev->drv_data;
ud->mediasync_id = para;
return AM_SUCCESS;
}