void sei_dump_messages(sei_message_t* head, double timestamp) { cea708_t cea708; sei_message_t* msg; cea708_init(&cea708, timestamp); for (msg = head; msg; msg = sei_message_next(msg)) { uint8_t* data = sei_message_data(msg); size_t size = sei_message_size(msg); fprintf(stderr, "-- Message %p\n-- Message Type: %d\n-- Message Size: %d\n", data, sei_message_type(msg), (int)size); while (size) { fprintf(stderr, "%02X ", *data); ++data; --size; } fprintf(stderr, "\n"); if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { if (LIBCAPTION_OK != cea708_parse_h262(sei_message_data(msg), sei_message_size(msg), &cea708)) { fprintf(stderr, "cea708_parse error\n"); } else { cea708_dump(&cea708); } } } }
void sei_dump_messages (sei_message_t* head) { cea708_t cea708; sei_message_t* msg; cea708_init (&cea708); for (msg = head ; msg ; msg = sei_message_next (msg)) { uint8_t* data = sei_message_data (msg); size_t size = sei_message_size (msg); fprintf (stderr,"-- Message %p\n-- Message Type: %d\n-- Message Size: %d\n", data, sei_message_type (msg), (int) size); while (size) { fprintf (stderr,"%02X ", *data); ++data; --size; } fprintf (stderr,"\n"); if (sei_type_user_data_registered_itu_t_t35 == sei_message_type (msg)) { cea708_parse (sei_message_data (msg), sei_message_size (msg), &cea708); cea708_dump (&cea708); } } }
sei_message_t* sei_message_new (sei_msgtype_t type, uint8_t* data, size_t size) { struct _sei_message_t* msg = (struct _sei_message_t*) malloc (sizeof (struct _sei_message_t) + size); msg->next = 0; msg->type = type; msg->size = size; if (data) { memcpy (sei_message_data (msg), data, size); } else { memset (sei_message_data (msg), 0, size); } return (sei_message_t*) msg; }
void sei_append_708(sei_t* sei, cea708_t* cea708) { sei_message_t* msg = sei_message_new(sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE); msg->size = cea708_render(cea708, sei_message_data(msg), sei_message_size(msg)); sei_message_append(sei, msg); cea708_init(cea708, sei->timestamp); // will confgure using HLS compatiable defaults }
libcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp) { sei_init(sei, timestamp); int ret = 0; // SEI may contain more than one payload while (1 < size) { size_t payloadType = 0; size_t payloadSize = 0; while (0 < size && 255 == (*data)) { payloadType += 255; ++data, --size; } if (0 == size) { return LIBCAPTION_ERROR; } payloadType += (*data); ++data, --size; while (0 < size && 255 == (*data)) { payloadSize += 255; ++data, --size; } if (0 == size) { return LIBCAPTION_ERROR; } payloadSize += (*data); ++data, --size; if (payloadSize) { sei_message_t* msg = sei_message_new((sei_msgtype_t)payloadType, 0, payloadSize); uint8_t* payloadData = sei_message_data(msg); size_t bytes = _copy_to_rbsp(payloadData, payloadSize, data, size); sei_message_append(sei, msg); if (bytes < payloadSize) { return LIBCAPTION_ERROR; } data += bytes; size -= bytes; ++ret; } } // There should be one trailing byte, 0x80. But really, we can just ignore that fact. return LIBCAPTION_OK; }
// we can safely assume sei_render_size() bytes have been allocated for data size_t sei_render(sei_t* sei, uint8_t* data) { if (!sei || !sei->head) { return 0; } size_t escaped_size, size = 2; // nalu_type + stop bit sei_message_t* msg; (*data) = 6; ++data; for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) { int payloadType = sei_message_type(msg); int payloadSize = (int)sei_message_size(msg); uint8_t* payloadData = sei_message_data(msg); while (255 <= payloadType) { (*data) = 255; ++data; ++size; payloadType -= 255; } (*data) = payloadType; ++data; ++size; while (255 <= payloadSize) { (*data) = 255; ++data; ++size; payloadSize -= 255; } (*data) = payloadSize; ++data; ++size; if (0 >= (escaped_size = _copy_from_rbsp(data, payloadData, payloadSize))) { return 0; } data += escaped_size; size += escaped_size; } // write stop bit and return (*data) = 0x80; return size; }
libcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame) { cea708_t cea708; sei_message_t* msg; libcaption_stauts_t status = LIBCAPTION_OK; cea708_init(&cea708, frame->timestamp); for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) { if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), &cea708); status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708)); } } if (LIBCAPTION_READY == status) { frame->timestamp = sei->timestamp; } return status; }
size_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts) { if (MAX_NALU_SIZE <= packet->size) { packet->status = LIBCAPTION_ERROR; // fprintf(stderr, "LIBCAPTION_ERROR\n"); return 0; } // consume upto MAX_NALU_SIZE bytes if (MAX_NALU_SIZE <= packet->size + size) { size = MAX_NALU_SIZE - packet->size; } sei_t sei; size_t header_size, scpos; packet->status = LIBCAPTION_OK; memcpy(&packet->data[packet->size], data, size); packet->size += size; while (packet->status == LIBCAPTION_OK && 0 < (scpos = find_start_code(&packet->data[0], packet->size))) { switch (mpeg_bitstream_packet_type(packet, stream_type)) { default: break; case H262_SEI_PACKET: header_size = 4; if (STREAM_TYPE_H262 == stream_type && scpos > header_size) { cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); packet->status = libcaption_status_update(packet->status, cea708_parse_h262(&packet->data[header_size], scpos - header_size, cea708)); _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); } break; case H264_SEI_PACKET: case H265_SEI_PACKET: header_size = STREAM_TYPE_H264 == stream_type ? 4 : STREAM_TYPE_H265 == stream_type ? 5 : 0; if (header_size && scpos > header_size) { packet->status = libcaption_status_update(packet->status, sei_parse(&sei, &packet->data[header_size], scpos - header_size, dts + cts)); for (sei_message_t* msg = sei_message_head(&sei); msg; msg = sei_message_next(msg)) { if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) { cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts); packet->status = libcaption_status_update(packet->status, cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), cea708)); _mpeg_bitstream_cea708_sort_flush(packet, frame, dts); } } sei_free(&sei); } break; } packet->size -= scpos; memmove(&packet->data[0], &packet->data[scpos], packet->size); } return size; }
int sei_parse_nalu (sei_t* sei, const uint8_t* data, size_t size, double dts, double cts) { assert (0<=cts); // cant present before decode sei->dts = dts; sei->cts = cts; int ret = 0; if (0 == data || 0 == size) { return 0; } uint8_t nal_unit_type = (*data) & 0x1F; ++data; --size; if (6 != nal_unit_type) { return 0; } // SEI may contain more than one payload while (1<size) { int payloadType = 0; int payloadSize = 0; while (0 < size && 255 == (*data)) { payloadType += 255; ++data; --size; } if (0 == size) { goto error; } payloadType += (*data); ++data; --size; while (0 < size && 255 == (*data)) { payloadSize += 255; ++data; --size; } if (0 == size) { goto error; } payloadSize += (*data); ++data; --size; if (payloadSize) { sei_message_t* msg = sei_message_new ( (sei_msgtype_t) payloadType, 0, payloadSize); uint8_t* payloadData = sei_message_data (msg); size_t bytes = _copy_to_rbsp (payloadData, payloadSize, data, size); sei_message_append (sei, msg); if ( (int) bytes < payloadSize) { goto error; } data += bytes; size -= bytes; ++ret; } } // There should be one trailing byte, 0x80. But really, we can just ignore that fact. return ret; error: sei_init (sei); return 0; }