/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, ¶m); } } 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; }