2974 lines
110 KiB
C++
2974 lines
110 KiB
C++
|
|
#ifndef _WIN32
|
|
#endif
|
|
#include "tsPacket.h"
|
|
|
|
#include <fs/file.h>
|
|
#include <fs/systemlog.h>
|
|
|
|
#include <string>
|
|
|
|
#include "bitStream.h"
|
|
#include "crc32.h"
|
|
#include "h264StreamReader.h"
|
|
#include "hevc.h"
|
|
#include "math.h"
|
|
#include "mpegStreamReader.h"
|
|
#include "simplePacketizerReader.h"
|
|
#include "tsMuxer.h"
|
|
#include "vodCoreException.h"
|
|
|
|
using namespace std;
|
|
|
|
enum SubPathType
|
|
{
|
|
SUBPATH_PIP = 7
|
|
};
|
|
|
|
bool isVideoStreamType(int stream_coding_type)
|
|
{
|
|
return stream_coding_type == STREAM_TYPE_VIDEO_MPEG2 || stream_coding_type == STREAM_TYPE_VIDEO_H264 ||
|
|
stream_coding_type == STREAM_TYPE_VIDEO_VC1 || stream_coding_type == STREAM_TYPE_VIDEO_MVC ||
|
|
stream_coding_type == STREAM_TYPE_VIDEO_H265;
|
|
}
|
|
|
|
bool isAudioStreamType(int stream_coding_type)
|
|
{
|
|
return stream_coding_type == 0x80 || stream_coding_type == 0x81 || stream_coding_type == 0x82 ||
|
|
stream_coding_type == 0x83 || stream_coding_type == 0x84 || stream_coding_type == 0x85 ||
|
|
stream_coding_type == 0x86 || stream_coding_type == 0xA1 || stream_coding_type == 0xA2 ||
|
|
|
|
stream_coding_type == STREAM_TYPE_AUDIO_AAC || stream_coding_type == STREAM_TYPE_AUDIO_AAC_RAW ||
|
|
stream_coding_type == STREAM_TYPE_AUDIO_MPEG1 || stream_coding_type == STREAM_TYPE_AUDIO_MPEG2;
|
|
}
|
|
|
|
// ------------ PS PACK -------------------
|
|
bool PS_stream_pack::deserialize(uint8_t* buffer, int buf_size)
|
|
{
|
|
m_pts = 0;
|
|
BitStreamReader bitReader;
|
|
bitReader.setBuffer(buffer, buffer + buf_size);
|
|
if (bitReader.getBits(2) != 1)
|
|
return false; // 0b01 required
|
|
m_pts = (bitReader.getBits(3) << 30);
|
|
if (bitReader.getBit() != 1)
|
|
return false;
|
|
m_pts += (bitReader.getBits(15) << 15);
|
|
if (bitReader.getBit() != 1)
|
|
return false;
|
|
m_pts += bitReader.getBits(15);
|
|
if (bitReader.getBit() != 1)
|
|
return false;
|
|
m_pts_ext = bitReader.getBits(9);
|
|
if (bitReader.getBit() != 1)
|
|
return false;
|
|
m_program_mux_rate = bitReader.getBits(22) * 50 * 8; // convert to bits/sec
|
|
if (bitReader.getBits(2) != 3)
|
|
return false;
|
|
bitReader.skipBits(5); // reserved
|
|
m_pack_stuffing_length = bitReader.getBits(3);
|
|
return true;
|
|
try
|
|
{
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ------------- PAT -----------------------
|
|
|
|
static inline int get16(uint8_t** pp)
|
|
{
|
|
uint8_t* p;
|
|
int c;
|
|
p = *pp;
|
|
c = (p[0] << 8) | p[1];
|
|
p += 2;
|
|
*pp = p;
|
|
return c;
|
|
}
|
|
|
|
TS_program_association_section::TS_program_association_section() : transport_stream_id(1)
|
|
{
|
|
m_nitPID = -1;
|
|
// av_crc_init(tmpAvCrc, 0, 32, 0x04c11db7, sizeof(AVCRC)*257);
|
|
}
|
|
|
|
bool TS_program_association_section::deserialize(uint8_t* buffer, int buf_size)
|
|
{
|
|
m_nitPID = -1;
|
|
buffer++;
|
|
BitStreamReader bitReader;
|
|
try
|
|
{
|
|
bitReader.setBuffer(buffer, buffer + buf_size);
|
|
|
|
int table_id = bitReader.getBits(8);
|
|
if (table_id != 0x0)
|
|
return false;
|
|
|
|
int indicator = bitReader.getBits(2); // section syntax indicator and reserved '0' bit
|
|
if (indicator != 2)
|
|
return false;
|
|
bitReader.skipBits(2); // reserved
|
|
|
|
unsigned section_length = bitReader.getBits(12);
|
|
unsigned crcBit = bitReader.getBitsCount() + (section_length - 4) * 8;
|
|
|
|
transport_stream_id = bitReader.getBits(16);
|
|
bitReader.skipBits(2); // reserved
|
|
bitReader.getBits(5); // int version_number
|
|
bitReader.getBits(1); // int current_next_indicator
|
|
|
|
bitReader.getBits(8); // int section_number
|
|
bitReader.getBits(8); // int last_section_number
|
|
|
|
pmtPids.clear();
|
|
// while(get_bits_count(&bitContext) < crcBit)
|
|
while (bitReader.getBitsCount() < crcBit)
|
|
{
|
|
int program_number = bitReader.getBits(16);
|
|
bitReader.skipBits(3); // reserved
|
|
int program_pid = bitReader.getBits(13);
|
|
if (program_number != 0) // not a network pid
|
|
pmtPids.insert(std::make_pair(program_pid, program_number));
|
|
else
|
|
m_nitPID = program_pid;
|
|
}
|
|
// if (get_bits_count(&bitContext) != crcBit)
|
|
if (bitReader.getBitsCount() != crcBit)
|
|
return false;
|
|
// uint32_t crc = bitReader.getBits(32);
|
|
// uint32_t rez = av_crc(tmpAvCrc, -1, buffer, get_bits_count(&bitContext)/8-4);
|
|
// rez = my_htonl(rez);
|
|
return true;
|
|
}
|
|
catch (BitStreamReader&)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint32_t TS_program_association_section::serialize(uint8_t* buffer, int buf_size)
|
|
{
|
|
buffer[0] = 0;
|
|
buffer++;
|
|
BitStreamWriter bitWriter;
|
|
|
|
// init_bitWriter.putBits(buffer, buf_size*8);
|
|
bitWriter.setBuffer(buffer, buffer + buf_size);
|
|
|
|
bitWriter.putBits(8, 0);
|
|
bitWriter.putBits(2, 2); // indicator
|
|
bitWriter.putBits(2, 3); // reserved
|
|
|
|
unsigned section_length = 9 + pmtPids.size() * 4;
|
|
bitWriter.putBits(12, section_length);
|
|
bitWriter.putBits(16, transport_stream_id);
|
|
bitWriter.putBits(2, 3); // reserved
|
|
bitWriter.putBits(5, 0); // version
|
|
bitWriter.putBits(1, 1); // current next indicator
|
|
|
|
bitWriter.putBits(16, 0); // section and last section number
|
|
for (std::map<int, int>::const_iterator itr = pmtPids.begin(); itr != pmtPids.end(); ++itr)
|
|
{
|
|
bitWriter.putBits(16, itr->second); // program number
|
|
bitWriter.putBits(3, 7); // current next indicator
|
|
bitWriter.putBits(13, itr->first); // pid
|
|
}
|
|
// uint32_t crc = av_crc(tmpAvCrc, 0xffffffff, buffer, bitWriter.getBitsCount()/8);
|
|
bitWriter.flushBits();
|
|
uint32_t crc = calculateCRC32(buffer, bitWriter.getBitsCount() / 8);
|
|
// bitWriter.putBits(32, my_htonl(crc));
|
|
uint32_t* crcPtr = (uint32_t*)(buffer + bitWriter.getBitsCount() / 8);
|
|
*crcPtr = my_htonl(crc);
|
|
// flush_put_bits(&bitContext);
|
|
// return put_bits_count(&bitContext)/8 + 1;
|
|
return bitWriter.getBitsCount() / 8 + 5;
|
|
}
|
|
|
|
// ------------- PMT ------------------------
|
|
|
|
TS_program_map_section::TS_program_map_section()
|
|
: video_pid(0), audio_pid(0), sub_pid(0), pcr_pid(0), program_number(0), casID(0), casPID(0)
|
|
{
|
|
// av_crc_init(tmpAvCrc, 0, 32, 0x04C11DB7L, sizeof(AVCRC)*257);
|
|
|
|
video_type = -1;
|
|
audio_type = -1;
|
|
}
|
|
|
|
bool TS_program_map_section::isFullBuff(uint8_t* buffer, int buf_size)
|
|
{
|
|
uint8_t pointerField = *buffer;
|
|
uint8_t* bufEnd = buffer + buf_size;
|
|
BitStreamReader bitReader;
|
|
try
|
|
{
|
|
bitReader.setBuffer(buffer + 1 + pointerField, buffer + buf_size);
|
|
|
|
int table_id = bitReader.getBits(8);
|
|
if (table_id != 0x02)
|
|
return false;
|
|
|
|
int indicator = bitReader.getBits(2); // section syntax indicator and reserved '0' bit
|
|
if (indicator != 2)
|
|
return false;
|
|
bitReader.skipBits(2); // reserved
|
|
|
|
int section_length = bitReader.getBits(12);
|
|
return bitReader.getBuffer() + bitReader.getBitsCount() / 8 + section_length <= bufEnd;
|
|
}
|
|
catch (BitStreamException&)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void TS_program_map_section::extractPMTDescriptors(uint8_t* curPos, int es_info_len)
|
|
{
|
|
uint8_t* end = curPos + es_info_len;
|
|
while (curPos < end)
|
|
{
|
|
uint8_t tag = *curPos;
|
|
uint8_t len = curPos[1];
|
|
curPos += 2;
|
|
if (tag == TS_CAS_DESCRIPTOR_TAG && len >= 4)
|
|
{
|
|
casID = (curPos[0] << 8) + curPos[1];
|
|
casPID = ((curPos[2] & 0x0f) << 8) + curPos[3];
|
|
}
|
|
else if (tag == TS_COPY_CONTROL_DESCRIPTOR_TAG && len >= 2)
|
|
{
|
|
uint16_t casSystemId = (curPos[0] << 8) + curPos[1];
|
|
}
|
|
curPos += len;
|
|
}
|
|
}
|
|
|
|
bool TS_program_map_section::deserialize(uint8_t* buffer, int buf_size)
|
|
{
|
|
if (buf_size < 1)
|
|
return false;
|
|
uint8_t pointerField = *buffer;
|
|
uint8_t* bufferEnd = buffer + buf_size;
|
|
BitStreamReader bitReader;
|
|
try
|
|
{
|
|
bitReader.setBuffer(buffer + 1 + pointerField, buffer + buf_size);
|
|
|
|
// bitReader.skipBits(8); // skip zero byte?
|
|
|
|
int table_id = bitReader.getBits(8);
|
|
if (table_id != 0x02)
|
|
return false;
|
|
|
|
int indicator = bitReader.getBits(2); // section syntax indicator and reserved '0' bit
|
|
if (indicator != 2)
|
|
return false;
|
|
bitReader.skipBits(2); // reserved
|
|
|
|
int section_length = bitReader.getBits(12);
|
|
uint8_t* crcPos = bitReader.getBuffer() + bitReader.getBitsCount() / 8 + section_length - 4;
|
|
if (crcPos > bufferEnd)
|
|
{
|
|
LTRACE(LT_WARN, 0, "Bad PMT table. skipped");
|
|
return false;
|
|
}
|
|
|
|
program_number = bitReader.getBits(16);
|
|
bitReader.skipBits(2); // reserved
|
|
bitReader.getBits(5); // int version_number
|
|
int nextIndicator = bitReader.getBits(1); // int current_next_indicator
|
|
int sectionNumber = bitReader.getBits(8); // int section_number
|
|
int lastSectionNumber = bitReader.getBits(8); // int last_section_number
|
|
bitReader.skipBits(3); // reserved
|
|
pcr_pid = bitReader.getBits(13);
|
|
|
|
// we set video=pcr pid by default.
|
|
// the video PID is not available in the scrambled channel veriMatrix 239.255.2.58:5500.
|
|
// Is that similar to the es_info_len crap?
|
|
video_pid = pcr_pid;
|
|
|
|
bitReader.skipBits(4); // reserved
|
|
int program_info_len = bitReader.getBits(12);
|
|
uint8_t* curPos = bitReader.getBuffer() + bitReader.getBitsCount() / 8;
|
|
extractPMTDescriptors(curPos, program_info_len);
|
|
curPos += program_info_len;
|
|
while (curPos < crcPos)
|
|
{
|
|
int stream_type = *curPos++;
|
|
int elementary_pid = get16(&curPos) & 0x1fff;
|
|
switch (stream_type)
|
|
{
|
|
case STREAM_TYPE_VIDEO_MPEG1:
|
|
case STREAM_TYPE_VIDEO_MPEG2:
|
|
case STREAM_TYPE_VIDEO_MPEG4:
|
|
case STREAM_TYPE_VIDEO_H264:
|
|
case STREAM_TYPE_VIDEO_H265:
|
|
case STREAM_TYPE_VIDEO_MVC:
|
|
case STREAM_TYPE_VIDEO_VC1:
|
|
video_pid = elementary_pid;
|
|
video_type = stream_type;
|
|
break;
|
|
|
|
case STREAM_TYPE_AUDIO_MPEG1:
|
|
case STREAM_TYPE_AUDIO_MPEG2:
|
|
case STREAM_TYPE_AUDIO_AAC:
|
|
case STREAM_TYPE_AUDIO_AC3:
|
|
case STREAM_TYPE_AUDIO_EAC3:
|
|
case STREAM_TYPE_AUDIO_EAC3_ATSC:
|
|
case STREAM_TYPE_AUDIO_DTS:
|
|
audio_pid = elementary_pid;
|
|
audio_type = stream_type;
|
|
break;
|
|
case STREAM_TYPE_SUBTITLE_DVB:
|
|
sub_pid = elementary_pid;
|
|
break;
|
|
}
|
|
PMTStreamInfo pmtStreamInfo(stream_type, elementary_pid, 0, 0, 0, "", false);
|
|
int es_info_len = get16(&curPos) & 0xfff;
|
|
if (curPos + es_info_len > crcPos)
|
|
{
|
|
LTRACE(LT_WARN, 0, "Bad PMT table. skipped");
|
|
return false;
|
|
}
|
|
extractDescriptors(curPos, es_info_len, pmtStreamInfo);
|
|
pidList.insert(std::make_pair(elementary_pid, pmtStreamInfo));
|
|
curPos += es_info_len;
|
|
}
|
|
if (curPos != crcPos)
|
|
return false;
|
|
|
|
uint32_t rez = calculateCRC32(buffer, curPos - buffer);
|
|
rez = my_htonl(rez);
|
|
return true;
|
|
}
|
|
catch (BitStreamException&)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void TS_program_map_section::extractDescriptors(uint8_t* curPos, int es_info_len, PMTStreamInfo& pmtInfo)
|
|
{
|
|
uint8_t* end = curPos + es_info_len;
|
|
while (curPos < end)
|
|
{
|
|
uint8_t tag = *curPos;
|
|
uint8_t len = curPos[1];
|
|
curPos += 2;
|
|
uint8_t* descrBuf = curPos;
|
|
if (tag == TS_REGISTRATION_DESCRIPTOR_TAG)
|
|
{
|
|
uint32_t format_identifier = *((uint32_t*)descrBuf);
|
|
descrBuf += 4;
|
|
}
|
|
else if (tag == TS_LANG_DESCRIPTOR_TAG)
|
|
{
|
|
for (int i = 0; i < 3; i++) pmtInfo.m_lang[i] = descrBuf[i];
|
|
}
|
|
curPos += len;
|
|
}
|
|
}
|
|
|
|
uint32_t TS_program_map_section::serialize(uint8_t* buffer, int max_buf_size, bool blurayMode, bool hdmvDescriptors)
|
|
{
|
|
buffer[0] = 0;
|
|
buffer++;
|
|
BitStreamWriter bitWriter;
|
|
bitWriter.setBuffer(buffer, buffer + max_buf_size);
|
|
bitWriter.putBits(8, 2); // table id
|
|
|
|
uint16_t* LengthPos1 = (uint16_t*)(bitWriter.getBuffer() + bitWriter.getBitsCount() / 8);
|
|
bitWriter.putBits(2, 2); // indicator
|
|
bitWriter.putBits(2, 3); // reserved
|
|
bitWriter.putBits(12, 0); // skip lengthField
|
|
int beforeCount1 = bitWriter.getBitsCount() / 8;
|
|
|
|
bitWriter.putBits(16, program_number);
|
|
bitWriter.putBits(2, 3); // reserved
|
|
bitWriter.putBits(5, 0); // version_number
|
|
bitWriter.putBits(1, 1); // current next indicator
|
|
bitWriter.putBits(16, 0); // section_number and last_section_number
|
|
bitWriter.putBits(3, 7); // reserved
|
|
bitWriter.putBits(13, pcr_pid); // reserved
|
|
|
|
uint16_t* LengthPos2 = (uint16_t*)(bitWriter.getBuffer() + bitWriter.getBitsCount() / 8);
|
|
bitWriter.putBits(4, 15); // reserved
|
|
bitWriter.putBits(12, 0); // program info len
|
|
int beforeCount2 = bitWriter.getBitsCount() / 8;
|
|
|
|
if (hdmvDescriptors)
|
|
{
|
|
// put 'HDMV' registration descriptor
|
|
bitWriter.putBits(8, 0x05);
|
|
bitWriter.putBits(8, 0x04);
|
|
bitWriter.putBits(32, 0x48444d56);
|
|
|
|
// put DTCP descriptor
|
|
bitWriter.putBits(8, 0x88);
|
|
bitWriter.putBits(8, 0x04);
|
|
bitWriter.putBits(32, 0x0ffffcfc);
|
|
}
|
|
|
|
if (casPID)
|
|
{
|
|
// put CAS descriptor
|
|
bitWriter.putBits(8, TS_CAS_DESCRIPTOR_TAG);
|
|
bitWriter.putBits(8, 0x04);
|
|
bitWriter.putBits(16, casID);
|
|
bitWriter.putBits(16, casPID);
|
|
}
|
|
*LengthPos2 = my_htons(0xf000 + bitWriter.getBitsCount() / 8 - beforeCount2);
|
|
|
|
if (video_pid)
|
|
{
|
|
bitWriter.putBits(8, video_type);
|
|
bitWriter.putBits(3, 7); // reserved
|
|
bitWriter.putBits(13, video_pid);
|
|
bitWriter.putBits(4, 15); // reserved
|
|
bitWriter.putBits(12, 0); // es_info_len
|
|
}
|
|
|
|
if (audio_pid)
|
|
{
|
|
// bitWriter.putBits( 8, 0x04);
|
|
bitWriter.putBits(8, audio_type);
|
|
bitWriter.putBits(3, 7); // reserved
|
|
bitWriter.putBits(13, audio_pid);
|
|
bitWriter.putBits(4, 15); // reserved
|
|
bitWriter.putBits(12, 0); // es_info_len
|
|
}
|
|
|
|
if (sub_pid)
|
|
{
|
|
bitWriter.putBits(8, STREAM_TYPE_SUBTITLE_DVB);
|
|
bitWriter.putBits(3, 7); // reserved
|
|
bitWriter.putBits(13, sub_pid);
|
|
bitWriter.putBits(4, 15); // reserved
|
|
bitWriter.putBits(12, 0); // es_info_len
|
|
}
|
|
|
|
for (PIDListMap::const_iterator itr = pidList.begin(); itr != pidList.end(); ++itr)
|
|
{
|
|
if (itr->second.m_streamType == 0x90 && !hdmvDescriptors)
|
|
LTRACE(LT_WARN, 2, "Warning: PGS might not work without HDMV descriptors.");
|
|
|
|
bitWriter.putBits(8, itr->second.m_streamType);
|
|
bitWriter.putBits(3, 7); // reserved
|
|
bitWriter.putBits(13, itr->second.m_pid);
|
|
|
|
uint16_t* esInfoLen = (uint16_t*)(bitWriter.getBuffer() + bitWriter.getBitsCount() / 8);
|
|
bitWriter.putBits(4, 15); // reserved
|
|
bitWriter.putBits(12, 0); // es_info_len
|
|
int beforeCount = bitWriter.getBitsCount() / 8;
|
|
|
|
for (int j = 0; j < itr->second.m_esInfoLen; j++)
|
|
bitWriter.putBits(8, itr->second.m_esInfoData[j]); // es_info_len
|
|
|
|
if (*itr->second.m_lang && !blurayMode)
|
|
{
|
|
bitWriter.putBits(8, TS_LANG_DESCRIPTOR_TAG); // lang descriptor ID
|
|
bitWriter.putBits(8, 4); // lang descriptor len
|
|
for (int k = 0; k < 3; k++) bitWriter.putBits(8, itr->second.m_lang[k]); // lang code[i]
|
|
bitWriter.putBits(8, 0);
|
|
}
|
|
*esInfoLen = my_htons(0xf000 + bitWriter.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
*LengthPos1 = my_htons(0xb000 + bitWriter.getBitsCount() / 8 - beforeCount1 + 4);
|
|
bitWriter.flushBits();
|
|
|
|
// uint32_t crc = av_crc(tmpAvCrc, 0xffffffff, buffer, bitWriter.getBitsCount()/8);
|
|
uint32_t crc = calculateCRC32(buffer, bitWriter.getBitsCount() / 8);
|
|
|
|
uint32_t* crcPtr = (uint32_t*)(buffer + bitWriter.getBitsCount() / 8);
|
|
*crcPtr = my_htonl(crc);
|
|
|
|
return bitWriter.getBitsCount() / 8 + 5;
|
|
}
|
|
|
|
// --------------------- CLPIParser -----------------------------
|
|
|
|
void CLPIStreamInfo::ISRC(BitStreamReader& reader)
|
|
{
|
|
readString(country_code, reader, 2);
|
|
readString(copyright_holder, reader, 3);
|
|
readString(recording_year, reader, 2);
|
|
readString(recording_number, reader, 5);
|
|
}
|
|
|
|
void CLPIStreamInfo::composeISRC(BitStreamWriter& writer) const
|
|
{
|
|
/*
|
|
writeString(country_code, writer, 2);
|
|
writeString(copyright_holder, writer, 3);
|
|
writeString(recording_year, writer, 2);
|
|
writeString(recording_number, writer, 5);*/
|
|
writeString("\x30\x30", writer, 2);
|
|
writeString("\x30\x30\x30", writer, 3);
|
|
writeString("\x30\x30", writer, 2);
|
|
writeString("\x30\x30\x30\x30\x30", writer, 5);
|
|
}
|
|
|
|
void CLPIStreamInfo::parseStreamCodingInfo(BitStreamReader& reader)
|
|
{
|
|
int length = reader.getBits(8);
|
|
stream_coding_type = reader.getBits(8);
|
|
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
video_format = reader.getBits(4);
|
|
frame_rate_index = reader.getBits(4);
|
|
aspect_ratio_index = reader.getBits(4);
|
|
reader.skipBits(2); // reserved_for_future_use 2 bslbf
|
|
bool cc_flag = reader.getBit();
|
|
reader.skipBits(17); // reserved_for_future_use 17 bslbf
|
|
ISRC(reader);
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (isAudioStreamType(stream_coding_type))
|
|
{
|
|
audio_presentation_type = reader.getBits(4);
|
|
sampling_frequency_index = reader.getBits(4);
|
|
readString(language_code, reader, 3);
|
|
ISRC(reader);
|
|
reader.skipBits(32);
|
|
}
|
|
else if (stream_coding_type == 0x90)
|
|
{
|
|
// Presentation Graphics stream
|
|
readString(language_code, reader, 3);
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
ISRC(reader);
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x91)
|
|
{
|
|
// Interactive Graphics stream
|
|
readString(language_code, reader, 3);
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
ISRC(reader);
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x92)
|
|
{
|
|
// Text subtitle stream
|
|
character_code = reader.getBits(8);
|
|
readString(language_code, reader, 3);
|
|
ISRC(reader);
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
}
|
|
}
|
|
|
|
void CLPIStreamInfo::composeStreamCodingInfo(BitStreamWriter& writer) const
|
|
{
|
|
uint8_t* lengthPos = writer.getBuffer() + writer.getBitsCount() / 8;
|
|
writer.putBits(8, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, stream_coding_type);
|
|
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
writer.putBits(4, video_format);
|
|
writer.putBits(4, frame_rate_index);
|
|
writer.putBits(4, aspect_ratio_index);
|
|
writer.putBits(2, 0); // reserved_for_future_use 2 bslbf
|
|
writer.putBit(0); // cc_flag
|
|
writer.putBit(0); // reserved
|
|
if (HDR & 18)
|
|
writer.putBits(8, 0x12); // HDR10 or HDR10plus
|
|
else if (HDR == 4)
|
|
writer.putBits(8, 0x22); // DV
|
|
else
|
|
writer.putBits(8, 0);
|
|
if (HDR == 16)
|
|
writer.putBits(8, 0x80); // HDR10plus
|
|
else
|
|
writer.putBits(8, 0);
|
|
composeISRC(writer);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (isAudioStreamType(stream_coding_type))
|
|
{
|
|
writer.putBits(4, audio_presentation_type);
|
|
writer.putBits(4, sampling_frequency_index);
|
|
writeString(language_code, writer, 3);
|
|
composeISRC(writer);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x90)
|
|
{
|
|
// Presentation Graphics stream
|
|
writeString(language_code, writer, 3);
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
composeISRC(writer);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x91)
|
|
{
|
|
// Interactive Graphics stream
|
|
writeString(language_code, writer, 3);
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
composeISRC(writer);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x92)
|
|
{
|
|
// Text subtitle stream
|
|
writer.putBits(8, character_code);
|
|
writeString(language_code, writer, 3);
|
|
composeISRC(writer);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
*lengthPos = writer.getBitsCount() / 8 - beforeCount;
|
|
}
|
|
|
|
void CLPIParser::parseProgramInfo(uint8_t* buffer, uint8_t* end, std::vector<CLPIProgramInfo>& programInfoMap,
|
|
std::map<int, CLPIStreamInfo>& streamInfoMap)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, end);
|
|
uint32_t length = reader.getBits(32);
|
|
reader.skipBits(8); // reserved_for_word_align
|
|
uint8_t number_of_program_sequences = reader.getBits(8);
|
|
for (int i = 0; i < number_of_program_sequences; i++)
|
|
{
|
|
programInfoMap.push_back(CLPIProgramInfo());
|
|
programInfoMap[i].SPN_program_sequence_start = reader.getBits(32);
|
|
programInfoMap[i].program_map_PID = reader.getBits(16);
|
|
programInfoMap[i].number_of_streams_in_ps = reader.getBits(8);
|
|
reader.skipBits(8);
|
|
for (int stream_index = 0; stream_index < programInfoMap[i].number_of_streams_in_ps; stream_index++)
|
|
{
|
|
int pid = reader.getBits(16);
|
|
CLPIStreamInfo streamInfo;
|
|
streamInfo.parseStreamCodingInfo(reader);
|
|
streamInfoMap.insert(std::make_pair(pid, streamInfo));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLPIParser::composeProgramInfo(BitStreamWriter& writer, bool isSsExt)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0); // reserved
|
|
writer.putBits(8, 1); // number_of_program_sequences
|
|
// for (int i=0; i < number_of_program_sequences; i++)
|
|
{
|
|
// m_programInfo.push_back(CLPIProgramInfo());
|
|
writer.putBits(32, 0); // m_programInfo[i].SPN_program_sequence_start
|
|
writer.putBits(16, DEFAULT_PMT_PID); // m_programInfo[i].program_map_PID =
|
|
|
|
int streams = 0;
|
|
for (std::map<int, CLPIStreamInfo>::const_iterator itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
const CLPIStreamInfo& si = itr->second;
|
|
bool streamOK = isSsExt && si.stream_coding_type == 0x20 || !isSsExt && si.stream_coding_type != 0x20;
|
|
if (streamOK)
|
|
streams++;
|
|
}
|
|
|
|
writer.putBits(8, streams); // m_programInfo[i].number_of_streams_in_ps
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
// for (int i=0; i < m_streamInfo.size(); i++)
|
|
for (std::map<int, CLPIStreamInfo>::const_iterator itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
const CLPIStreamInfo& si = itr->second;
|
|
bool streamOK = isSsExt && si.stream_coding_type == 0x20 || !isSsExt && si.stream_coding_type != 0x20;
|
|
if (!streamOK)
|
|
continue;
|
|
writer.putBits(16, itr->first); // pid
|
|
itr->second.composeStreamCodingInfo(writer);
|
|
}
|
|
}
|
|
|
|
if (isSsExt && writer.getBitsCount() % 32 != 0)
|
|
writer.putBits(16, 0);
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::TS_type_info_block(BitStreamReader& reader)
|
|
{
|
|
uint16_t length = reader.getBits(16);
|
|
uint8_t Validity_flags = reader.getBits(8); // 1000 0000b is tipical value
|
|
CLPIStreamInfo::readString(format_identifier, reader, 4); // HDMV
|
|
// Network_information 8*9 bslbf zero
|
|
for (int i = 0; i < 9; i++) reader.skipBits(8);
|
|
// Stream_format_name 8*16 bslbf zero
|
|
for (int i = 0; i < 16; i++) reader.skipBits(8);
|
|
}
|
|
|
|
void CLPIParser::composeTS_type_info_block(BitStreamWriter& writer)
|
|
{
|
|
// uint16_t length = reader.getBits(16);
|
|
uint16_t* lengthPos = (uint16_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(16, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0x80); // Validity_flags
|
|
CLPIStreamInfo::writeString("HDMV", writer, 4);
|
|
// Network_information 8*9 bslbf zero
|
|
for (int i = 0; i < 9; i++) writer.putBits(8, 0);
|
|
// Stream_format_name 8*16 bslbf zero
|
|
for (int i = 0; i < 16; i++) writer.putBits(8, 0);
|
|
*lengthPos = my_ntohs(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::parseClipInfo(BitStreamReader& reader)
|
|
{
|
|
uint32_t length = reader.getBits(32);
|
|
reader.skipBits(16); // reserved_for_future_use 16 bslbf
|
|
clip_stream_type = reader.getBits(8); // 1 - AV stream
|
|
application_type = reader.getBits(8); // 1 - Main TS for a main-path of Movie
|
|
reader.skipBits(31); // reserved_for_future_use 31 bslbf
|
|
is_ATC_delta = reader.getBit(); // 1 bslbf
|
|
TS_recording_rate = reader.getBits(32); // kbps in bytes/sec
|
|
number_of_source_packets = reader.getBits(32); // number of TS packets?
|
|
for (int i = 0; i < 32; i++) reader.skipBits(32);
|
|
TS_type_info_block(reader);
|
|
/*
|
|
if (is_ATC_delta==1b) {
|
|
reserved_for_future_use 8 bslbf
|
|
number_of_ATC_delta_entries 8 uimsbf
|
|
for (i=0; i<number_of_ATC_delta_entries; i++) {
|
|
ATC_delta[i] 32 uimsbf
|
|
following_Clip_Information_file_name[i] 8*5 bslbf
|
|
Clip_codec_identifier 8*4 bslbf
|
|
reserved_for_future_use 8 bslbf
|
|
}
|
|
}
|
|
if (application_type==6){
|
|
reserved_for_future_use 8 bslbf
|
|
number_of_font_files 8 uimsbf
|
|
for (font_id=0; font_id<number_of_font_files; font_id++) {
|
|
font_file_name[font_id] 8*5 bslbf
|
|
reserved_for_future_use 8 bslbf
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CLPIParser::composeClipInfo(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(16, 0); // reserved_for_future_use 16 bslbf
|
|
writer.putBits(8, clip_stream_type); // 1 - AV stream
|
|
writer.putBits(8, application_type); // 1 - Main TS for a main-path of Movie
|
|
writer.putBits(31, 0); // reserved_for_future_use 31 bslbf
|
|
writer.putBit(is_ATC_delta); // 1 bslbf
|
|
writer.putBits(32, TS_recording_rate); // kbps in bytes/sec
|
|
writer.putBits(32, number_of_source_packets); // number of TS packets?
|
|
for (int i = 0; i < 32; i++) writer.putBits(32, 0); // reserved
|
|
composeTS_type_info_block(writer);
|
|
if (is_ATC_delta)
|
|
THROW(ERR_COMMON, "CLPI is_ATC_delta is not implemented now.");
|
|
if (application_type == 6)
|
|
THROW(ERR_COMMON, "CLPI application_type==6 is not implemented now.");
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::parseSequenceInfo(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, end);
|
|
uint32_t length = reader.getBits(32);
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
uint8_t number_of_ATC_sequences = reader.getBits(8); // 1 is tipical value
|
|
for (int atc_id = 0; atc_id < number_of_ATC_sequences; atc_id++)
|
|
{
|
|
uint32_t SPN_ATC_start = reader.getBits(32); // 0 is tipical value
|
|
uint8_t number_of_STC_sequences = reader.getBits(8);
|
|
int offset_STC_id = reader.getBits(8);
|
|
for (int stc_id = offset_STC_id; stc_id < number_of_STC_sequences + offset_STC_id; stc_id++)
|
|
{
|
|
int PCR_PID = reader.getBits(16);
|
|
uint32_t SPN_STC_start = reader.getBits(32);
|
|
presentation_start_time = reader.getBits(32);
|
|
presentation_end_time = reader.getBits(32);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLPIParser::composeSequenceInfo(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0); // reserved_for_word_align 8 bslbf
|
|
writer.putBits(8, 1); // number_of_ATC_sequences
|
|
// for (int atc_id = 0; atc_id < number_of_ATC_sequences; atc_id++)
|
|
{
|
|
writer.putBits(32, 0); // SPN_ATC_start
|
|
writer.putBits(8, 1); // number_of_STC_sequences
|
|
writer.putBits(8, 0); // offset_STC_id
|
|
// for (int stc_id=offset_STC_id; stc_id < number_of_STC_sequences + offset_STC_id; stc_id++)
|
|
{
|
|
writer.putBits(16, DEFAULT_PCR_PID);
|
|
writer.putBits(32, 0); // SPN_STC_start
|
|
writer.putBits(32, presentation_start_time);
|
|
writer.putBits(32, presentation_end_time);
|
|
}
|
|
}
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::parseCPI(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, end);
|
|
uint32_t length = reader.getBits(32);
|
|
if (length != 0)
|
|
{
|
|
// reserved_for_word_align 12 bslbf
|
|
reader.skipBits(12);
|
|
int CPI_type = reader.getBits(4); // 1 is tipical value
|
|
CPI_type = CPI_type;
|
|
// EP_map(reader);
|
|
}
|
|
}
|
|
|
|
void CLPIParser::EP_map(BitStreamReader& reader) {}
|
|
|
|
void CLPIParser::composeCPI(BitStreamWriter& writer, bool isCPIExt)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
|
|
if (isDependStream && !isCPIExt || !isDependStream && isCPIExt)
|
|
return; // CPI_SS for MVC depend stream only and vice versa: standard CPI for standard video stream
|
|
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
// if (length != 0)
|
|
{
|
|
// reserved_for_word_align 12 bslbf
|
|
writer.putBits(12, 0);
|
|
writer.putBits(4, 1); // CPI_type
|
|
composeEP_map(writer, isCPIExt);
|
|
}
|
|
|
|
if (isCPIExt && writer.getBitsCount() % 32 != 0)
|
|
writer.putBits(16, 0);
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::composeEP_map(BitStreamWriter& writer, bool isSSExt)
|
|
{
|
|
uint32_t beforeCount = writer.getBitsCount() / 8;
|
|
std::vector<CLPIStreamInfo> processStream;
|
|
int EP_stream_type = 1; //[k] 4 bslbf
|
|
for (std::map<int, CLPIStreamInfo>::iterator itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
int coding_type = itr->second.stream_coding_type;
|
|
if (isSSExt)
|
|
{
|
|
if (coding_type == 0x20)
|
|
processStream.push_back(itr->second);
|
|
}
|
|
else
|
|
{
|
|
if (coding_type != 0x20 && isVideoStreamType(coding_type))
|
|
processStream.push_back(itr->second);
|
|
}
|
|
}
|
|
if (processStream.size() == 0)
|
|
for (std::map<int, CLPIStreamInfo>::iterator itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
int coding_type = itr->second.stream_coding_type;
|
|
if (isAudioStreamType(coding_type))
|
|
{
|
|
processStream.push_back(itr->second);
|
|
if (itr->second.isSecondary)
|
|
EP_stream_type = 4;
|
|
else
|
|
EP_stream_type = 3;
|
|
break;
|
|
}
|
|
}
|
|
if (processStream.size() == 0)
|
|
THROW(ERR_COMMON, "Can't create EP map. One audio or video stream is needed.");
|
|
// ------------------
|
|
writer.putBits(8, 0); // reserved_for_word_align 8 bslbf
|
|
writer.putBits(8, processStream.size()); // number_of_stream_PID_entries 8 uimsbf
|
|
std::vector<uint32_t*> epStartAddrPos;
|
|
|
|
for (auto& i : processStream)
|
|
{
|
|
writer.putBits(16, i.streamPID); // stream_PID[k] 16 bslbf
|
|
writer.putBits(10, 0); // reserved_for_word_align 10 bslbf
|
|
writer.putBits(4, EP_stream_type);
|
|
std::vector<BluRayCoarseInfo> coarseInfo = buildCoarseInfo(i);
|
|
writer.putBits(16, coarseInfo.size()); // number_of_EP_coarse_entries[k] 16 uimsbf
|
|
if (i.m_index.size() > 0)
|
|
writer.putBits(18, i.m_index[m_clpiNum].size()); // number_of_EP_fine_entries[k] 18 uimsbf
|
|
else
|
|
writer.putBits(18, 0);
|
|
epStartAddrPos.push_back((uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8));
|
|
writer.putBits(32, 0); // EP_map_for_one_stream_PID_start_address[k] 32 uimsbf
|
|
}
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0); // padding_word 16 bslbf
|
|
|
|
for (size_t i = 0; i < processStream.size(); ++i)
|
|
{
|
|
*epStartAddrPos[i] = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
composeEP_map_for_one_stream_PID(writer, processStream[i]);
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0); // padding_word 16 bslbf
|
|
}
|
|
}
|
|
|
|
std::vector<BluRayCoarseInfo> CLPIParser::buildCoarseInfo(M2TSStreamInfo& streamInfo)
|
|
{
|
|
std::vector<BluRayCoarseInfo> rez;
|
|
if (streamInfo.m_index.size() == 0)
|
|
return rez;
|
|
uint32_t cnt = 0;
|
|
uint32_t lastPktCnt = 0;
|
|
uint32_t lastCoarsePts = 0;
|
|
PMTIndex& curIndex = streamInfo.m_index[m_clpiNum];
|
|
for (PMTIndex::const_iterator itr = curIndex.begin(); itr != curIndex.end(); ++itr)
|
|
{
|
|
const PMTIndexData& indexData = itr->second;
|
|
uint32_t newCoarsePts = itr->first >> 19;
|
|
uint32_t lastCoarseSPN = lastPktCnt & 0xfffe0000;
|
|
uint32_t newCoarseSPN = indexData.m_pktCnt & 0xfffe0000;
|
|
if (rez.size() == 0 || newCoarsePts != lastCoarsePts || lastCoarseSPN != newCoarseSPN)
|
|
{
|
|
rez.push_back(BluRayCoarseInfo(newCoarsePts, cnt, indexData.m_pktCnt));
|
|
}
|
|
lastCoarsePts = newCoarsePts;
|
|
lastPktCnt = indexData.m_pktCnt;
|
|
cnt++;
|
|
}
|
|
return rez;
|
|
}
|
|
|
|
void CLPIParser::composeEP_map_for_one_stream_PID(BitStreamWriter& writer, M2TSStreamInfo& streamInfo)
|
|
{
|
|
uint32_t* epFineStartAddr = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
uint32_t beforePos = writer.getBitsCount() / 8;
|
|
writer.putBits(32, 0); // EP_fine_table_start_address 32 uimsbf
|
|
std::vector<BluRayCoarseInfo> coarseInfo = buildCoarseInfo(streamInfo);
|
|
for (auto& i : coarseInfo)
|
|
{
|
|
writer.putBits(18, i.m_fineRefID); // ref_to_EP_fine_id[i] 18 uimsbf
|
|
writer.putBits(14, i.m_coarsePts); // PTS_EP_coarse[i] 14 uimsbf
|
|
writer.putBits(32, i.m_pktCnt); // SPN_EP_coarse[i] 32 uimsbf
|
|
}
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0); // padding_word 16 bslbf
|
|
*epFineStartAddr = my_htonl(writer.getBitsCount() / 8 - beforePos);
|
|
if (streamInfo.m_index.size() > 0)
|
|
{
|
|
const PMTIndex& curIndex = streamInfo.m_index[m_clpiNum];
|
|
for (PMTIndex::const_iterator itr = curIndex.begin(); itr != curIndex.end(); ++itr)
|
|
{
|
|
const PMTIndexData& indexData = itr->second;
|
|
writer.putBit(0); // is_angle_change_point[EP_fine_id] 1 bslbf
|
|
int endCode = 0;
|
|
if (indexData.m_frameLen > 0)
|
|
{
|
|
if (is4K())
|
|
{
|
|
if (indexData.m_frameLen < 786432)
|
|
endCode = 1;
|
|
else if (indexData.m_frameLen < 1572864)
|
|
endCode = 2;
|
|
else if (indexData.m_frameLen < 2359296)
|
|
endCode = 3;
|
|
else if (indexData.m_frameLen < 3145728)
|
|
endCode = 4;
|
|
else if (indexData.m_frameLen < 3932160)
|
|
endCode = 5;
|
|
else if (indexData.m_frameLen < 4718592)
|
|
endCode = 6;
|
|
else
|
|
endCode = 7;
|
|
}
|
|
else
|
|
{
|
|
if (indexData.m_frameLen < 131072)
|
|
endCode = 1;
|
|
else if (indexData.m_frameLen < 262144)
|
|
endCode = 2;
|
|
else if (indexData.m_frameLen < 393216)
|
|
endCode = 3;
|
|
else if (indexData.m_frameLen < 589824)
|
|
endCode = 4;
|
|
else if (indexData.m_frameLen < 917504)
|
|
endCode = 5;
|
|
else if (indexData.m_frameLen < 1310720)
|
|
endCode = 6;
|
|
else
|
|
endCode = 7;
|
|
}
|
|
}
|
|
writer.putBits(3, endCode); // I_end_position_offset[EP_fine_id] 3 bslbf
|
|
writer.putBits(11, (itr->first >> 9) % 2048); // PTS_EP_fine[EP_fine_id] 11 uimsbf
|
|
writer.putBits(17, indexData.m_pktCnt % (65536 * 2)); // SPN_EP_fine[EP_fine_id] 17 uimsbf
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLPIParser::parseClipMark(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, end);
|
|
uint32_t length = reader.getBits(32);
|
|
}
|
|
|
|
void CLPIParser::composeClipMark(BitStreamWriter& writer) { writer.putBits(32, 0); }
|
|
|
|
bool CLPIParser::parse(const char* fileName)
|
|
{
|
|
File file;
|
|
if (!file.open(fileName, File::ofRead))
|
|
return false;
|
|
uint64_t fileSize;
|
|
if (!file.size(&fileSize))
|
|
return false;
|
|
uint8_t* buffer = new uint8_t[fileSize];
|
|
if (!file.read(buffer, fileSize))
|
|
{
|
|
delete[] buffer;
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
parse(buffer, fileSize);
|
|
delete[] buffer;
|
|
return true;
|
|
}
|
|
catch (...)
|
|
{
|
|
delete[] buffer;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CLPIParser::HDMV_LPCM_down_mix_coefficient(uint8_t* buffer, int dataLength) {}
|
|
|
|
void CLPIParser::Extent_Start_Point(uint8_t* buffer, int dataLength)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, buffer + dataLength);
|
|
uint32_t length = reader.getBits(32);
|
|
|
|
reader.skipBits(16); // reserved_for_future_use
|
|
int number_of_extent_start_points = reader.getBits(16);
|
|
SPN_extent_start.resize(number_of_extent_start_points);
|
|
for (int i = 0; i < number_of_extent_start_points; ++i)
|
|
{
|
|
SPN_extent_start[i] = reader.getBits(32);
|
|
}
|
|
}
|
|
|
|
void CLPIParser::ProgramInfo_SS(uint8_t* buffer, int dataLength)
|
|
{
|
|
parseProgramInfo(buffer, buffer + dataLength, m_programInfoMVC, m_streamInfoMVC);
|
|
}
|
|
|
|
void CLPIParser::CPI_SS(uint8_t* buffer, int dataLength) { parseCPI(buffer, buffer + dataLength); }
|
|
|
|
void CLPIParser::composeExtentStartPoint(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(16, 0); // reserved
|
|
writer.putBits(16, interleaveInfo.size());
|
|
|
|
uint32_t sum = 0;
|
|
for (auto& i : interleaveInfo)
|
|
{
|
|
sum += i;
|
|
writer.putBits(32, sum);
|
|
}
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void CLPIParser::composeExtentInfo(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(32, 0); // skip lengthField
|
|
|
|
if (interleaveInfo.empty())
|
|
return;
|
|
|
|
writer.putBits(32, 0); // skip data block start address
|
|
writer.putBits(24, 0); // reserved for world align. not used
|
|
|
|
int entries = isDependStream ? 3 : 1;
|
|
writer.putBits(8, entries);
|
|
|
|
// write Extent_Start_Point header
|
|
|
|
writer.putBits(32, 0x00020004); // extent start point
|
|
uint32_t* extentStartPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
|
|
writer.putBits(32, 0); // skip extent start address
|
|
writer.putBits(32, 0); // skip extent dataLen
|
|
|
|
uint32_t* CPI_SS_StartPos = 0;
|
|
uint32_t* ProgramInfo_StartPos = 0;
|
|
if (isDependStream)
|
|
{
|
|
// write ProgramInfo_SS header
|
|
writer.putBits(32, 0x00020005); // extent start point
|
|
ProgramInfo_StartPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
|
|
writer.putBits(32, 0); // skip extent start address
|
|
writer.putBits(32, 0); // skip extent dataLen
|
|
|
|
// write CPI_SS header
|
|
writer.putBits(32, 0x00020006); // extent start point
|
|
CPI_SS_StartPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
|
|
writer.putBits(32, 0); // skip extent start address
|
|
writer.putBits(32, 0); // skip extent dataLen
|
|
}
|
|
|
|
while (writer.getBitsCount() % 32) writer.putBits(16, 0);
|
|
|
|
lengthPos[1] =
|
|
my_htonl(writer.getBitsCount() / 8 - beforeCount); // data_block_start_address, same as extentStart point start
|
|
|
|
// write Extent_Start_Point body
|
|
*extentStartPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
int beforeExtentCount = writer.getBitsCount() / 8;
|
|
composeExtentStartPoint(writer);
|
|
extentStartPos[1] = my_htonl(writer.getBitsCount() / 8 - beforeExtentCount);
|
|
|
|
if (isDependStream)
|
|
{
|
|
// write ProgramInfo_SS body
|
|
*ProgramInfo_StartPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
beforeExtentCount = writer.getBitsCount() / 8;
|
|
composeProgramInfo(writer, true);
|
|
ProgramInfo_StartPos[1] = my_htonl(writer.getBitsCount() / 8 - beforeExtentCount);
|
|
|
|
// write CPI_SS body
|
|
*CPI_SS_StartPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
beforeExtentCount = writer.getBitsCount() / 8;
|
|
composeCPI(writer, true);
|
|
CPI_SS_StartPos[1] = my_htonl(writer.getBitsCount() / 8 - beforeExtentCount);
|
|
}
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount - 4);
|
|
}
|
|
|
|
void CLPIParser::parseExtensionData(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
// added for 3D compatibility
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, end);
|
|
uint32_t length = reader.getBits(32);
|
|
if (length == 0)
|
|
return;
|
|
|
|
int data_block_start_address = reader.getBits(32);
|
|
reader.skipBits(24);
|
|
int entries = reader.getBits(8);
|
|
for (int i = 0; i < entries; ++i)
|
|
{
|
|
uint32_t dataID = reader.getBits(32);
|
|
uint32_t dataAddress = reader.getBits(32);
|
|
uint32_t dataLength = reader.getBits(32);
|
|
|
|
if (dataAddress + dataLength > (uint32_t)(end - buffer))
|
|
{
|
|
LTRACE(LT_WARN, 2, "Invalid extended clip info entry skipped.");
|
|
continue;
|
|
}
|
|
|
|
switch (dataID)
|
|
{
|
|
case 0x00010002:
|
|
HDMV_LPCM_down_mix_coefficient(buffer + dataAddress, dataLength);
|
|
break;
|
|
case 0x00020004:
|
|
Extent_Start_Point(buffer + dataAddress, dataLength);
|
|
break;
|
|
case 0x00020005:
|
|
ProgramInfo_SS(buffer + dataAddress, dataLength);
|
|
break;
|
|
case 0x00020006:
|
|
CPI_SS(buffer + dataAddress, dataLength);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLPIParser::parse(uint8_t* buffer, int len)
|
|
{
|
|
BitStreamReader reader;
|
|
try
|
|
{
|
|
reader.setBuffer(buffer, buffer + len);
|
|
|
|
CLPIStreamInfo::readString(type_indicator, reader, 4);
|
|
CLPIStreamInfo::readString(version_number, reader, 4);
|
|
uint32_t sequenceInfo_start_address = reader.getBits(32);
|
|
uint32_t programInfo_start_address = reader.getBits(32);
|
|
uint32_t CPI_start_address = reader.getBits(32);
|
|
uint32_t clipMark_start_address = reader.getBits(32);
|
|
uint32_t extensionData_start_address = reader.getBits(32);
|
|
for (int i = 0; i < 3; i++) reader.skipBits(32); // reserved_for_future_use 96 bslbf
|
|
parseClipInfo(reader);
|
|
// for (int i=0; i<N1; i++) padding_word 16 bslbf
|
|
parseSequenceInfo(buffer + sequenceInfo_start_address, buffer + len);
|
|
// for (i=0; i<N2; i++) { padding_word 16 bslbf
|
|
parseProgramInfo(buffer + programInfo_start_address, buffer + len, m_programInfo, m_streamInfo);
|
|
// for (i=0; i<N3; i++) padding_word 16 bslbf
|
|
parseCPI(buffer + CPI_start_address, buffer + len);
|
|
// for (i=0; i<N4; i++) padding_word 16 bslbf
|
|
parseClipMark(buffer + clipMark_start_address, buffer + len);
|
|
// for (i=0; i<N5; i++) padding_word 16 bslbf
|
|
if (extensionData_start_address)
|
|
parseExtensionData(buffer + extensionData_start_address, buffer + len);
|
|
// for (i=0; i<N6; i++) padding_word 16 bslbf
|
|
}
|
|
catch (BitStreamException&)
|
|
{
|
|
THROW(ERR_COMMON, "Can't parse clip info file: unexpected end of data");
|
|
}
|
|
}
|
|
|
|
int CLPIParser::compose(uint8_t* buffer, int bufferSize)
|
|
{
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
CLPIStreamInfo::writeString("HDMV", writer, 4);
|
|
CLPIStreamInfo::writeString(version_number, writer, 4);
|
|
uint32_t* sequenceInfo_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // sequenceInfo_start_address
|
|
uint32_t* programInfo_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // programInfo_start_address
|
|
uint32_t* CPI_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // CPI start address
|
|
uint32_t* clipMark_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // clipMark
|
|
uint32_t* extentInfo_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // extent info
|
|
for (int i = 0; i < 3; i++) writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
|
|
composeClipInfo(writer);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
*sequenceInfo_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composeSequenceInfo(writer);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
|
|
*programInfo_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composeProgramInfo(writer, false);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
|
|
*CPI_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composeCPI(writer, false);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
|
|
*clipMark_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composeClipMark(writer);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
|
|
*extentInfo_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composeExtentInfo(writer);
|
|
while (writer.getBitsCount() % 32 != 0) writer.putBits(8, 0);
|
|
|
|
writer.flushBits();
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
|
|
// -------------------------- MPLSParser ----------------------------
|
|
|
|
MPLSParser::MPLSParser()
|
|
: m_chapterLen(0), number_of_SubPaths(0), m_m2tsOffset(0), isDependStreamExist(false), mvc_base_view_r(false)
|
|
{
|
|
number_of_primary_video_stream_entries = 0;
|
|
number_of_primary_audio_stream_entries = 0;
|
|
number_of_PG_textST_stream_entries = 0;
|
|
number_of_IG_stream_entries = 0;
|
|
number_of_secondary_audio_stream_entries = 0;
|
|
number_of_secondary_video_stream_entries = 0;
|
|
number_of_PiP_PG_textST_stream_entries_plus = 0;
|
|
number_of_DolbyVision_video_stream_entries = 0;
|
|
}
|
|
|
|
bool MPLSParser::parse(const char* fileName)
|
|
{
|
|
File file;
|
|
if (!file.open(fileName, File::ofRead))
|
|
return false;
|
|
uint64_t fileSize;
|
|
if (!file.size(&fileSize))
|
|
return false;
|
|
uint8_t* buffer = new uint8_t[fileSize];
|
|
if (!file.read(buffer, fileSize))
|
|
{
|
|
delete[] buffer;
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
parse(buffer, fileSize);
|
|
delete[] buffer;
|
|
return true;
|
|
}
|
|
catch (...)
|
|
{
|
|
delete[] buffer;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void MPLSParser::parse(uint8_t* buffer, int len)
|
|
{
|
|
BitStreamReader reader;
|
|
try
|
|
{
|
|
reader.setBuffer(buffer, buffer + len);
|
|
char type_indicator[5];
|
|
char version_number[5];
|
|
CLPIStreamInfo::readString(type_indicator, reader, 4);
|
|
CLPIStreamInfo::readString(version_number, reader, 4);
|
|
int playList_start_address = reader.getBits(32);
|
|
int playListMark_start_address = reader.getBits(32);
|
|
int extensionData_start_address = reader.getBits(32);
|
|
for (int i = 0; i < 5; i++) reader.skipBits(32); // reserved_for_future_use 160 bslbf
|
|
AppInfoPlayList(reader);
|
|
parsePlayList(buffer + playList_start_address, len - playList_start_address);
|
|
/*
|
|
for (int i=0; i<N1; i++) {
|
|
padding_word 16 bslbf
|
|
}
|
|
PlayList();
|
|
for (int i=0; i<N2; i++) {
|
|
padding_word 16 bslbf
|
|
}
|
|
*/
|
|
parsePlayListMark(buffer + playListMark_start_address, len - playListMark_start_address);
|
|
|
|
if (extensionData_start_address)
|
|
{
|
|
parseExtensionData(buffer + extensionData_start_address, buffer + len);
|
|
}
|
|
|
|
/*
|
|
for (i=0; i<N3; i++) {
|
|
padding_word 16 bslbf
|
|
}
|
|
ExtensionData();
|
|
for (i=0; i<N4; i++) {
|
|
padding_word 16 bslbf
|
|
}
|
|
*/
|
|
}
|
|
catch (BitStreamException&)
|
|
{
|
|
THROW(ERR_COMMON, "Can't parse media playlist file: unexpected end of data");
|
|
}
|
|
}
|
|
|
|
void MPLSParser::SubPath_extension(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0); // reserved
|
|
writer.putBits(8, 8); // SubPath_type = 8
|
|
writer.putBits(15, 0); // reserved
|
|
writer.putBit(0); // is_repeat_SubPath
|
|
writer.putBits(8, 0); // reserved
|
|
|
|
std::vector<PMTIndex> pmtIndexList = getMVCDependStream().m_index;
|
|
writer.putBits(8, pmtIndexList.size()); // number_of_SubPlayItems
|
|
for (size_t i = 0; i < pmtIndexList.size(); ++i) composeSubPlayItem(writer, i, 0, pmtIndexList);
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
int MPLSParser::composeSubPathEntryExtension(uint8_t* buffer, int bufferSize)
|
|
{
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
try
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
// composeSubPath(writer, 0, dependStreamInfo.m_index, 5); // MVC dep stream type
|
|
writer.putBits(16, 1); // one subpath
|
|
SubPath_extension(writer);
|
|
|
|
if (writer.getBitsCount() % 32 != 0)
|
|
writer.putBits(16, 0);
|
|
|
|
writer.flushBits();
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
catch (...)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int MPLSParser::composeSTN_tableSS(uint8_t* buffer, int bufferSize)
|
|
{
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
try
|
|
{
|
|
MPLSStreamInfo streamInfoMVC = getMVCDependStream();
|
|
for (size_t PlayItem_id = 0; PlayItem_id < streamInfoMVC.m_index.size(); PlayItem_id++)
|
|
{
|
|
composeSTN_table(writer, PlayItem_id, true);
|
|
// connection_condition = 6;
|
|
}
|
|
writer.flushBits();
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
catch (...)
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
int MPLSParser::composeUHD_metadata(uint8_t* buffer, int bufferSize)
|
|
{
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
try
|
|
{
|
|
writer.putBits(32, 0x20);
|
|
writer.putBits(32, 1 << 24);
|
|
writer.putBits(32, 1 << 28);
|
|
for (int i = 0; i < 6; i++) writer.putBits(32, HDR10_metadata[i]);
|
|
writer.flushBits();
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
catch (...)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int MPLSParser::compose(uint8_t* buffer, int bufferSize, DiskType dt)
|
|
{
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
if (i.isSecondary)
|
|
{
|
|
number_of_SubPaths++;
|
|
subPath_type = 7; // PIP not fully implemented yet
|
|
}
|
|
else if (i.HDR & 4)
|
|
{
|
|
number_of_SubPaths++;
|
|
subPath_type = 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
|
|
std::string type_indicator = "MPLS";
|
|
std::string version_number;
|
|
if (dt == DT_BLURAY)
|
|
version_number = (isV3() ? "0300" : "0200");
|
|
else
|
|
version_number = "0100";
|
|
CLPIStreamInfo::writeString(type_indicator.c_str(), writer, 4);
|
|
CLPIStreamInfo::writeString(version_number.c_str(), writer, 4);
|
|
uint32_t* playList_bit_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0);
|
|
uint32_t* playListMark_bit_pos = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0);
|
|
uint32_t* extDataStartAddr = (uint32_t*)(buffer + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // extension data start address
|
|
for (int i = 0; i < 5; i++) writer.putBits(32, 0); // reserved_for_future_use 160 bslbf
|
|
composeAppInfoPlayList(writer);
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0);
|
|
*playList_bit_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composePlayList(writer);
|
|
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0);
|
|
*playListMark_bit_pos = my_htonl(writer.getBitsCount() / 8);
|
|
composePlayListMark(writer);
|
|
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0);
|
|
|
|
if (number_of_SubPaths > 0 || isDependStreamExist || isV3())
|
|
{
|
|
*extDataStartAddr = my_htonl(writer.getBitsCount() / 8);
|
|
uint8_t buffer[1024 * 4];
|
|
MPLSStreamInfo& mainStreamInfo = getMainStream();
|
|
vector<ExtDataBlockInfo> blockVector;
|
|
|
|
// for (int i = 0; i < number_of_SubPaths; ++i)
|
|
if (number_of_SubPaths > 0 && subPath_type == 7)
|
|
{
|
|
int bufferSize = composePip_metadata(buffer, sizeof(buffer), mainStreamInfo.m_index);
|
|
ExtDataBlockInfo extDataBlock(buffer, bufferSize, 1, 1);
|
|
blockVector.push_back(extDataBlock);
|
|
}
|
|
|
|
if (isDependStreamExist)
|
|
{
|
|
int bufferSize = composeSTN_tableSS(buffer, sizeof(buffer));
|
|
ExtDataBlockInfo extDataBlock(buffer, bufferSize, 2, 1);
|
|
blockVector.push_back(extDataBlock);
|
|
|
|
bufferSize = composeSubPathEntryExtension(buffer, sizeof(buffer));
|
|
ExtDataBlockInfo extDataBlock2(buffer, bufferSize, 2, 2);
|
|
blockVector.push_back(extDataBlock2);
|
|
}
|
|
|
|
if (isV3())
|
|
{
|
|
int bufferSize = composeUHD_metadata(buffer, sizeof(buffer));
|
|
ExtDataBlockInfo extDataBlock(buffer, bufferSize, 3, 5);
|
|
blockVector.push_back(extDataBlock);
|
|
}
|
|
|
|
composeExtensionData(writer, blockVector);
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBits(8, 0);
|
|
}
|
|
|
|
writer.flushBits();
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
|
|
void MPLSParser::AppInfoPlayList(BitStreamReader& reader)
|
|
{
|
|
uint32_t length = reader.getBits(32);
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
PlayList_playback_type = reader.getBits(8); // 8 bslbf
|
|
if (PlayList_playback_type == 2 || PlayList_playback_type == 3)
|
|
{ // 1 == Sequential playback of PlayItems
|
|
playback_count = reader.getBits(16); // 16 uimsbf
|
|
}
|
|
else
|
|
{
|
|
reader.skipBits(16); // reserved_for_future_use 16 bslbf
|
|
}
|
|
UO_mask_table(reader);
|
|
bool PlayList_random_access_flag = reader.getBit(); // 1 bslbf
|
|
bool audio_mix_app_flag = reader.getBits(1);
|
|
bool lossless_may_bypass_mixer_flag = reader.getBit(); // 1 bslbf
|
|
mvc_base_view_r = reader.getBit();
|
|
reader.skipBits(12); // reserved_for_future_use 13 bslbf
|
|
}
|
|
|
|
void MPLSParser::composeAppInfoPlayList(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
writer.putBits(8, PlayList_playback_type); // 8 bslbf
|
|
if (PlayList_playback_type == 2 || PlayList_playback_type == 3)
|
|
{ // 1 == Sequential playback of PlayItems
|
|
writer.putBits(16, playback_count); // 16 uimsbf
|
|
}
|
|
else
|
|
{
|
|
writer.putBits(16, 0); // reserved_for_future_use 16 bslbf
|
|
}
|
|
writer.putBits(28, 0); // UO_mask_table;
|
|
writer.putBits(4, isV3() ? 15 : 0); // UO_mask_table;
|
|
writer.putBit(0); // reserved
|
|
writer.putBit(isV3() ? 1 : 0); // UO_mask_table: SecondaryPGStreamNumberChange
|
|
writer.putBits(30, 0); // UO_mask_table cont;
|
|
writer.putBit(0); // PlayList_random_access_flag
|
|
writer.putBit(1); // audio_mix_app_flag. 0 == no secondary audio, 1- allow secondary audio if exist
|
|
writer.putBit(0); // lossless_may_bypass_mixer_flag
|
|
writer.putBit(mvc_base_view_r);
|
|
writer.putBits(12, 0); // reserved_for_future_use 13 bslbf
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void MPLSParser::UO_mask_table(BitStreamReader& reader)
|
|
{
|
|
reader.skipBit(); // reserved_for_future_use // reserved for menu call mask 1 bslbf
|
|
reader.skipBit(); // reserved_for_future_use // reserved for title search mask 1 bslbf
|
|
chapter_search_mask = reader.getBit(); // 1 bslbf
|
|
time_search_mask = reader.getBit(); // 1 bslbf
|
|
skip_to_next_point_mask = reader.getBit(); // 1 bslbf
|
|
skip_back_to_previous_point_mask = reader.getBit(); // 1 bslbf
|
|
reader.skipBit(); // reserved_for_future_use // reserved for play FirstPlay mask 1 bslbf
|
|
stop_mask = reader.getBit(); // 1 bslbf
|
|
pause_on_mask = reader.getBit(); // 1 bslbf
|
|
reader.skipBit(); // reserved_for_future_use // reserved for pause off mask 1 bslbf
|
|
still_off_mask = reader.getBit(); // 1 bslbf
|
|
forward_play_mask = reader.getBit(); // 1 bslbf
|
|
backward_play_mask = reader.getBit(); // 1 bslbf
|
|
resume_mask = reader.getBit(); // 1 bslbf
|
|
move_up_selected_button_mask = reader.getBit(); // 1 bslbf
|
|
move_down_selected_button_mask = reader.getBit(); // 1 bslbf
|
|
move_left_selected_button_mask = reader.getBit(); // 1 bslbf
|
|
move_right_selected_button_mask = reader.getBit(); // 1 bslbf
|
|
select_button_mask = reader.getBit(); // 1 bslbf
|
|
activate_button_mask = reader.getBit(); // 1 bslbf
|
|
select_button_and_activate_mask = reader.getBit(); // 1 bslbf
|
|
primary_audio_stream_number_change_mask = reader.getBit(); // 1 bslbf
|
|
reader.skipBit(); // reserved_for_future_use 1 bslbf
|
|
angle_number_change_mask = reader.getBit(); // 1 bslbf
|
|
popup_on_mask = reader.getBit(); // 1 bslbf
|
|
popup_off_mask = reader.getBit(); // 1 bslbf
|
|
PG_textST_enable_disable_mask = reader.getBit(); // 1 bslbf
|
|
PG_textST_stream_number_change_mask = reader.getBit(); // 1 bslbf
|
|
secondary_video_enable_disable_mask = reader.getBit(); // 1 bslbf
|
|
secondary_video_stream_number_change_mask = reader.getBit(); // 1 bslbf
|
|
secondary_audio_enable_disable_mask = reader.getBit(); // 1 bslbf
|
|
secondary_audio_stream_number_change_mask = reader.getBit(); //
|
|
reader.skipBit(); // reserved_for_future_use 1 bslbf
|
|
PiP_PG_textST_stream_number_change_mask = reader.getBit(); // 1 bslbf
|
|
reader.skipBits(30); // reserved_for_future_use 30 bslbf
|
|
}
|
|
|
|
void MPLSParser::parsePlayList(uint8_t* buffer, int len)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, buffer + len);
|
|
uint32_t length = reader.getBits(32);
|
|
reader.skipBits(16); // reserved_for_future_use 16 bslbf
|
|
int number_of_PlayItems = reader.getBits(16); // 16 uimsbf
|
|
number_of_SubPaths = reader.getBits(16); // 16 uimsbf
|
|
for (int PlayItem_id = 0; PlayItem_id < number_of_PlayItems; PlayItem_id++)
|
|
{
|
|
parsePlayItem(reader, PlayItem_id);
|
|
}
|
|
for (int SubPath_id = 0; SubPath_id < number_of_SubPaths; SubPath_id++)
|
|
{
|
|
// SubPath(); // not implemented now
|
|
}
|
|
}
|
|
|
|
MPLSStreamInfo& MPLSParser::getMainStream()
|
|
{
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(coding_type))
|
|
return i;
|
|
}
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int coding_type = i.stream_coding_type;
|
|
if (isAudioStreamType(coding_type))
|
|
return i;
|
|
}
|
|
THROW(ERR_COMMON, "Can't find stream index. One audio or video stream is needed.");
|
|
}
|
|
|
|
int MPLSParser::pgIndexToFullIndex(int value)
|
|
{
|
|
int cnt = 0;
|
|
for (size_t i = 0; i < m_streamInfo.size(); ++i)
|
|
{
|
|
if (m_streamInfo[i].stream_coding_type == 0x90)
|
|
{
|
|
if (cnt++ == value)
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
MPLSStreamInfo MPLSParser::getStreamByPID(int pid) const
|
|
{
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
if (i.streamPID == pid)
|
|
return i;
|
|
}
|
|
return MPLSStreamInfo();
|
|
}
|
|
|
|
std::vector<MPLSStreamInfo> MPLSParser::getPgStreams() const
|
|
{
|
|
std::vector<MPLSStreamInfo> pgStreams;
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int coding_type = i.stream_coding_type;
|
|
if (coding_type == 0x90)
|
|
pgStreams.push_back(i);
|
|
}
|
|
return pgStreams;
|
|
}
|
|
|
|
MPLSStreamInfo& MPLSParser::getMVCDependStream()
|
|
{
|
|
for (auto& i : m_streamInfoMVC)
|
|
{
|
|
int coding_type = i.stream_coding_type;
|
|
if (coding_type == 0x20)
|
|
return i;
|
|
}
|
|
THROW(ERR_COMMON, "Can't find stream index. One audio or video stream is needed.");
|
|
}
|
|
|
|
void MPLSParser::composePlayList(BitStreamWriter& writer)
|
|
{
|
|
// uint32_t length = reader.getBits(32);
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
writer.putBits(16, 0); // reserved_for_future_use 16 bslbf
|
|
MPLSStreamInfo& mainStreamInfo = getMainStream();
|
|
writer.putBits(16, mainStreamInfo.m_index.size()); // 16 uimsbf number_of_PlayItems
|
|
writer.putBits(16, number_of_SubPaths); // number_of_SubPaths
|
|
// connection_condition = 1;
|
|
for (size_t PlayItem_id = 0; PlayItem_id < mainStreamInfo.m_index.size(); PlayItem_id++)
|
|
{
|
|
composePlayItem(writer, PlayItem_id, mainStreamInfo.m_index);
|
|
// connection_condition = 6;
|
|
}
|
|
|
|
MPLSStreamInfo& dependStreamInfo = mainStreamInfo;
|
|
|
|
for (size_t SubPath_id = 0; SubPath_id < number_of_SubPaths * dependStreamInfo.m_index.size(); SubPath_id++)
|
|
{
|
|
composeSubPath(writer, SubPath_id, dependStreamInfo.m_index, subPath_type); // pip
|
|
}
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void MPLSParser::composeSubPath(BitStreamWriter& writer, int subPathNum, std::vector<PMTIndex>& pmtIndexList, int type)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, 0); // reserved_for_future_use
|
|
writer.putBits(
|
|
8, type); // SubPath_type = 7 (In-mux and Synchronous type of Picture-in-Picture), 5 - MVC depend stream
|
|
writer.putBits(15, 0); // reserved_for_future_use
|
|
writer.putBits(1, 0); // is_repeat_SubPath = false
|
|
writer.putBits(8, 0); // reserved_for_future_use
|
|
|
|
/*
|
|
vector<StreamInfo*> secondary;
|
|
for (int i = 0; i < m_streamInfo.size(); i++)
|
|
{
|
|
int stream_coding_type = m_streamInfo[i].stream_coding_type;
|
|
if (m_streamInfo[i].isSecondary && (stream_coding_type==0x02 || stream_coding_type==0x1B ||
|
|
stream_coding_type==0xEA)) secondary.push_back(m_streamInfo[i]);
|
|
}
|
|
*/
|
|
writer.putBits(8, pmtIndexList.size()); // number_of_SubPlayItems
|
|
for (size_t i = 0; i < pmtIndexList.size(); i++)
|
|
{
|
|
composeSubPlayItem(writer, i, subPathNum, pmtIndexList);
|
|
}
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void MPLSParser::composeSubPlayItem(BitStreamWriter& writer, int playItemNum, int subPathNum,
|
|
std::vector<PMTIndex>& pmtIndexList)
|
|
{
|
|
uint16_t* lengthPos = (uint16_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(16, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
|
|
int fileNum = playItemNum;
|
|
if (isDependStreamExist)
|
|
{
|
|
fileNum *= 2;
|
|
fileNum++;
|
|
}
|
|
|
|
std::string clip_Information_file_name = strPadLeft(int32ToStr(fileNum + m_m2tsOffset), 5, '0');
|
|
CLPIStreamInfo::writeString(clip_Information_file_name.c_str(), writer, 5);
|
|
char clip_codec_identifier[] = "M2TS";
|
|
CLPIStreamInfo::writeString(clip_codec_identifier, writer, 4);
|
|
int connection_condition = playItemNum == 0 ? 1 : 6;
|
|
writer.putBits(27, 0); // reserved_for_future_use
|
|
writer.putBits(4, connection_condition); // 4 bslbf
|
|
writer.putBit(0); // is_multi_Clip_entries
|
|
writer.putBits(8, ref_to_STC_id); // 8 uimsbf
|
|
|
|
if (playItemNum == 0)
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
else if (pmtIndexList[playItemNum - 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[playItemNum].begin()->first / 2);
|
|
else
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
|
|
if (playItemNum == pmtIndexList.size() - 1)
|
|
writer.putBits(32, OUT_time); // 32 uimsbf
|
|
else if (pmtIndexList[playItemNum + 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[playItemNum + 1].begin()->first / 2); // 32 uimsbf
|
|
else
|
|
writer.putBits(32, OUT_time); // 32 uimsbf
|
|
|
|
writer.putBits(16, playItemNum); // sync_PlayItem_id. reference to play_item id.
|
|
// sync_start_PTS_of_PlayItem
|
|
if (playItemNum == 0)
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
else if (pmtIndexList[playItemNum - 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[playItemNum].begin()->first / 2);
|
|
else
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
|
|
// writer.flushBits();
|
|
*lengthPos = my_htons(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
int MPLSParser::composePip_metadata(uint8_t* buffer, int bufferSize, std::vector<PMTIndex>& pmtIndexList)
|
|
{
|
|
// The ID1 value and the ID2 value of the ExtensionData() shall be set to 0x0001 and 0x0001
|
|
BitStreamWriter writer;
|
|
writer.setBuffer(buffer, buffer + bufferSize);
|
|
uint32_t* lengthPos = (uint32_t*)buffer;
|
|
writer.putBits(32, 0); // int length = reader.getBits(8); //8 uimsbf
|
|
|
|
vector<MPLSStreamInfo> pipStreams;
|
|
int mainVSize = 0;
|
|
int mainHSize = 0;
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
if (i.isSecondary)
|
|
{
|
|
pipStreams.push_back(i);
|
|
}
|
|
else
|
|
{
|
|
mainHSize = i.width;
|
|
mainVSize = i.height;
|
|
}
|
|
}
|
|
}
|
|
|
|
writer.putBits(16, pipStreams.size() * pmtIndexList.size());
|
|
vector<uint32_t*> blockDataAddressPos;
|
|
for (size_t i = 0; i < pmtIndexList.size(); ++i)
|
|
{
|
|
for (size_t k = 0; k < pipStreams.size(); k++)
|
|
{
|
|
PIPParams pipParams = pipStreams[k].pipParams;
|
|
// metadata_block_header[k]() {
|
|
writer.putBits(16, i); // ref_to_PlayItem_id
|
|
writer.putBits(8, k); // ref_to_secondary_video_stream_id
|
|
writer.putBits(8, 0); // reserved_for_future_use
|
|
writer.putBits(
|
|
4, pipParams.lumma >= 0 ? 1 : 0); // pip_timeline_type == 1. Synchronous type of Picture-in-Picture
|
|
writer.putBit(1); // is_luma_key = 0
|
|
writer.putBit(1); // trick_playing_flag. keep PIP windows when tricking
|
|
writer.putBits(10, 0); // reserved_for_word_align
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
if (pipParams.lumma >= 0)
|
|
{ // is_luma_key==1b
|
|
writer.putBits(8, pipParams.lumma); // transparent Y pixels
|
|
}
|
|
else
|
|
{
|
|
writer.putBits(8, 0); // reserved_for_future_use 16 bslbf
|
|
}
|
|
writer.putBits(16, 0); // reserved_for_future_use 16 bslbf
|
|
blockDataAddressPos.push_back((uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8));
|
|
writer.putBits(32, 0); // metadata_block_data_start_address
|
|
}
|
|
}
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBit(0);
|
|
for (size_t i = 0; i < pmtIndexList.size(); ++i)
|
|
{
|
|
for (size_t k = 0; k < pipStreams.size(); ++k)
|
|
{
|
|
PIPParams pipParams = pipStreams[k].pipParams;
|
|
|
|
*(blockDataAddressPos[i * pipStreams.size() + k]) = my_htonl(writer.getBitsCount() / 8);
|
|
|
|
writer.putBits(16, 1); // number_of_pip_metadata_entries
|
|
{
|
|
if (i == 0)
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
else if (pmtIndexList[i - 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[i].begin()->first / 2);
|
|
else
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
|
|
int hPos = 0;
|
|
int vPos = 0;
|
|
|
|
if (!pipParams.isFullScreen())
|
|
{
|
|
hPos = pipParams.hOffset;
|
|
vPos = pipParams.vOffset;
|
|
|
|
int pipWidth = pipStreams[k].width * pipParams.getScaleCoeff();
|
|
int pipHeight = pipStreams[k].height * pipParams.getScaleCoeff();
|
|
|
|
if (pipParams.corner == PIPParams::TopRight || pipParams.corner == PIPParams::BottomRight)
|
|
hPos = mainHSize - pipWidth - pipParams.hOffset;
|
|
if (pipParams.corner == PIPParams::BottomRight || pipParams.corner == PIPParams::BottomLeft)
|
|
vPos = mainVSize - pipHeight - pipParams.vOffset;
|
|
}
|
|
|
|
writer.putBits(12, hPos);
|
|
writer.putBits(12, vPos);
|
|
|
|
writer.putBits(4, pipParams.scaleIndex); // pip_scale[i] 4 uimsbf. 1 == no_scale
|
|
writer.putBits(4, 0); // reserved_for_future_use 4 bslbf
|
|
}
|
|
while (writer.getBitsCount() % 16 != 0) writer.putBit(0);
|
|
}
|
|
}
|
|
|
|
writer.flushBits();
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - 4);
|
|
return writer.getBitsCount() / 8;
|
|
}
|
|
|
|
void MPLSParser::parseStnTableSS(uint8_t* data, int dataLength)
|
|
{
|
|
try
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(data, data + dataLength);
|
|
int len = reader.getBits(16);
|
|
bool fixedOffsetDuringPopup = reader.getBit();
|
|
reader.skipBits(15); // reserved
|
|
|
|
int firstPgIndex = number_of_primary_video_stream_entries + number_of_primary_audio_stream_entries;
|
|
|
|
for (int i = 0; i < number_of_primary_video_stream_entries; ++i)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
// m_streamInfoMVC.push_back(streamInfo);
|
|
|
|
streamInfo.parseStreamEntry(reader);
|
|
|
|
int attrSSLen = reader.getBits(8);
|
|
reader.skipBits(8); // coding type 0x20
|
|
int format = reader.getBits(4);
|
|
int frameRate = reader.getBits(4);
|
|
reader.skipBits(24); // reserved
|
|
|
|
reader.skipBits(10); // reserved
|
|
int number_of_offset_sequences = reader.getBits(6);
|
|
}
|
|
|
|
for (int i = 0; i < number_of_PG_textST_stream_entries; ++i)
|
|
{
|
|
int PG_textST_offset_sequence_id = reader.getBits(8);
|
|
int idx = pgIndexToFullIndex(i);
|
|
if (idx != -1)
|
|
m_streamInfo[idx].offsetId = PG_textST_offset_sequence_id;
|
|
|
|
reader.skipBits(4); // reserved
|
|
bool dialog_region_offset_valid = reader.getBit();
|
|
m_streamInfo[idx].isSSPG = reader.getBit();
|
|
bool isTopAS = reader.getBit();
|
|
bool isBottomAS = reader.getBit();
|
|
if (m_streamInfo[idx].isSSPG)
|
|
{
|
|
m_streamInfo[idx].leftEye = new MPLSStreamInfo();
|
|
m_streamInfo[idx].leftEye->parseStreamEntry(reader); // left eye
|
|
m_streamInfo[idx].rightEye = new MPLSStreamInfo();
|
|
m_streamInfo[idx].rightEye->parseStreamEntry(reader); // right eye
|
|
reader.skipBits(8); // reserved
|
|
m_streamInfo[idx].SS_PG_offset_sequence_id = reader.getBits(8);
|
|
}
|
|
if (isTopAS)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader); // left eye
|
|
reader.skipBits(8); // reserved
|
|
int top_AS_offset_sequence_id = reader.getBits(8);
|
|
}
|
|
if (isBottomAS)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader); // left eye
|
|
reader.skipBits(8); // reserved
|
|
int bottom_AS_offset_sequence_id = reader.getBits(8);
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
void MPLSParser::parseSubPathEntryExtension(uint8_t* data, int dataLen)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(data, data + dataLen);
|
|
try
|
|
{
|
|
uint32_t length = reader.getBits(32);
|
|
uint16_t size = reader.getBits(16);
|
|
// for (int i = 0; i < size; ++i)
|
|
if (size > 0)
|
|
{
|
|
// subpath extension
|
|
uint32_t len2 = reader.getBits(32);
|
|
reader.skipBits(8); // reserved field
|
|
int type = reader.getBits(8);
|
|
if (type == 8 || type == 9)
|
|
{
|
|
reader.skipBits(24);
|
|
int subPlayItems = reader.getBits(8);
|
|
for (int i = 0; i < subPlayItems; ++i)
|
|
{
|
|
int len3 = reader.getBits(16);
|
|
char clip_Information_file_name[6];
|
|
CLPIStreamInfo::readString(clip_Information_file_name, reader, 5);
|
|
m_mvcFiles.push_back(clip_Information_file_name);
|
|
reader.skipBits(32); // clip codec identifier
|
|
reader.skipBits(31); // reserved, condition
|
|
bool isMulticlip = reader.getBit();
|
|
reader.skipBits(8); // ref to stc id
|
|
reader.skipBits(32); // in time
|
|
reader.skipBits(32); // out time
|
|
reader.skipBits(16); // sync play item id
|
|
reader.skipBits(32); // sync start
|
|
if (isMulticlip)
|
|
{
|
|
int numberOfClipEntries = reader.getBits(8);
|
|
reader.skipBits(8); // reserved
|
|
for (int j = 1; j < numberOfClipEntries; ++j)
|
|
{
|
|
char clip_Information_file_name[6];
|
|
CLPIStreamInfo::readString(clip_Information_file_name, reader, 5);
|
|
m_mvcFiles.push_back(clip_Information_file_name);
|
|
reader.skipBits(32); // clip codec identifier
|
|
reader.skipBits(8); // ref to stc id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
void MPLSParser::parseExtensionData(uint8_t* data, uint8_t* dataEnd)
|
|
{
|
|
try
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(data, dataEnd);
|
|
uint32_t length = reader.getBits(32);
|
|
if (length == 0)
|
|
return;
|
|
|
|
int data_block_start_address = reader.getBits(32);
|
|
reader.skipBits(24);
|
|
int entries = reader.getBits(8);
|
|
for (int i = 0; i < entries; ++i)
|
|
{
|
|
uint32_t dataID = reader.getBits(32);
|
|
uint32_t dataAddress = reader.getBits(32);
|
|
uint32_t dataLength = reader.getBits(32);
|
|
|
|
if (dataAddress + dataLength > (uint32_t)(dataEnd - data))
|
|
{
|
|
LTRACE(LT_WARN, 2, "Invalid playlist extension entry skipped.");
|
|
continue;
|
|
}
|
|
|
|
if (dataAddress + dataLength > (uint32_t)(dataEnd - data))
|
|
continue; // invalid entry
|
|
|
|
switch (dataID)
|
|
{
|
|
case 0x00020001:
|
|
// stn table ss
|
|
isDependStreamExist = true;
|
|
parseStnTableSS(data + dataAddress, dataLength);
|
|
break;
|
|
case 0x00020002:
|
|
// stn table ss
|
|
isDependStreamExist = true;
|
|
parseSubPathEntryExtension(data + dataAddress, dataLength);
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
}
|
|
|
|
void MPLSParser::composeExtensionData(BitStreamWriter& writer, vector<ExtDataBlockInfo>& extDataBlockInfo)
|
|
{
|
|
vector<uint32_t*> extDataStartAddrPos;
|
|
extDataStartAddrPos.resize(extDataBlockInfo.size());
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // int length = reader.getBits(8); //8 uimsbf
|
|
int initPos = writer.getBitsCount() / 8;
|
|
if (extDataBlockInfo.size() > 0)
|
|
{
|
|
writer.putBits(32, 0); // data_block_start_address
|
|
writer.putBits(24, 0); // reserved_for_word_align
|
|
writer.putBits(8, extDataBlockInfo.size());
|
|
for (size_t i = 0; i < extDataBlockInfo.size(); ++i)
|
|
{
|
|
writer.putBits(16, extDataBlockInfo[i].id1);
|
|
writer.putBits(16, extDataBlockInfo[i].id2);
|
|
extDataStartAddrPos[i] = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // ext_data_start_address
|
|
writer.putBits(32, extDataBlockInfo[i].data.size()); // ext_data_length
|
|
}
|
|
while ((writer.getBitsCount() / 8 - initPos) % 4 != 0) writer.putBits(16, 0);
|
|
*(lengthPos + 1) = my_htonl(writer.getBitsCount() / 8 - initPos + 4); // data_block_start_address
|
|
for (size_t i = 0; i < extDataBlockInfo.size(); ++i)
|
|
{
|
|
*(extDataStartAddrPos[i]) = my_htonl(writer.getBitsCount() / 8 - initPos + 4);
|
|
for (auto& j : extDataBlockInfo[i].data) writer.putBits(8, j);
|
|
}
|
|
}
|
|
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - initPos);
|
|
}
|
|
|
|
void MPLSParser::parsePlayItem(BitStreamReader& reader, int PlayItem_id)
|
|
{
|
|
MPLSPlayItem newItem;
|
|
int length = reader.getBits(16);
|
|
char clip_Information_file_name[6];
|
|
char clip_codec_identifier[5];
|
|
CLPIStreamInfo::readString(clip_Information_file_name, reader, 5);
|
|
newItem.fileName = clip_Information_file_name;
|
|
CLPIStreamInfo::readString(clip_codec_identifier, reader, 4);
|
|
reader.skipBits(11); // reserved_for_future_use 11 bslbf
|
|
is_multi_angle = reader.getBit();
|
|
newItem.connection_condition = reader.getBits(4); // 4 bslbf
|
|
ref_to_STC_id = reader.getBits(8); // 8 uimsbf
|
|
|
|
newItem.IN_time = reader.getBits(32); // 32 uimsbf
|
|
newItem.OUT_time = reader.getBits(32); // 32 uimsbf
|
|
m_playItems.push_back(newItem);
|
|
|
|
UO_mask_table(reader);
|
|
PlayItem_random_access_flag = reader.getBit(); // 1 bslbf
|
|
reader.skipBits(7); // reserved_for_future_use 7 bslbf
|
|
uint8_t still_mode = reader.getBits(8); // 8 bslbf
|
|
if (still_mode == 0x01)
|
|
{
|
|
int still_time = reader.getBits(16); // 16 uimsbf
|
|
}
|
|
else
|
|
{
|
|
reader.skipBits(16); // reserved_for_future_use 16 bslbf
|
|
}
|
|
if (is_multi_angle == 1)
|
|
{
|
|
number_of_angles = reader.getBits(8); // uimsbf
|
|
reader.skipBits(6); // reserved_for_future_use 6 bslbf
|
|
is_different_audios = reader.getBit(); // 1 bslbf
|
|
is_seamless_angle_change = reader.getBit(); // 1 bslbf
|
|
for (int angle_id = 1; // angles except angle_id=0
|
|
angle_id < number_of_angles; angle_id++)
|
|
{
|
|
CLPIStreamInfo::readString(clip_Information_file_name, reader, 5); // 8*5 bslbf
|
|
CLPIStreamInfo::readString(clip_codec_identifier, reader, 4); // 8*4 bslbf
|
|
ref_to_STC_id = reader.getBits(8); // 8 uimsbf
|
|
}
|
|
}
|
|
STN_table(reader, PlayItem_id);
|
|
}
|
|
|
|
void MPLSParser::composePlayItem(BitStreamWriter& writer, int playItemNum, std::vector<PMTIndex>& pmtIndexList)
|
|
{
|
|
uint16_t* lengthPos = (uint16_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(16, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
int fileNum = playItemNum;
|
|
if (isDependStreamExist)
|
|
fileNum *= 2;
|
|
std::string clip_Information_file_name = strPadLeft(int32ToStr(fileNum + m_m2tsOffset), 5, '0');
|
|
CLPIStreamInfo::writeString(clip_Information_file_name.c_str(), writer, 5);
|
|
char clip_codec_identifier[] = "M2TS";
|
|
CLPIStreamInfo::writeString(clip_codec_identifier, writer, 4);
|
|
writer.putBits(11, 0); // reserved_for_future_use 11 bslbf
|
|
writer.putBit(0); // is_multi_angle
|
|
int connection_condition = playItemNum == 0 ? 1 : 6;
|
|
writer.putBits(4, connection_condition); // 4 bslbf
|
|
writer.putBits(8, ref_to_STC_id); // 8 uimsbf
|
|
if (playItemNum == 0)
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
else if (pmtIndexList[playItemNum - 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[playItemNum].begin()->first / 2);
|
|
else
|
|
writer.putBits(32, IN_time); // 32 uimsbf
|
|
|
|
if (playItemNum == pmtIndexList.size() - 1)
|
|
writer.putBits(32, OUT_time); // 32 uimsbf
|
|
else if (pmtIndexList[playItemNum + 1].size() > 0)
|
|
writer.putBits(32, pmtIndexList[playItemNum + 1].begin()->first / 2); // 32 uimsbf
|
|
else
|
|
writer.putBits(32, OUT_time); // 32 uimsbf
|
|
|
|
writer.putBits(28, 0); // UO_mask_table;
|
|
writer.putBits(4, isV3() ? 15 : 0); // UO_mask_table;
|
|
writer.putBit(0); // reserved
|
|
writer.putBit(isV3() ? 1 : 0); // UO_mask_table: SecondaryPGStreamNumberChange
|
|
writer.putBits(30, 0); // UO_mask_table cont;
|
|
|
|
writer.putBit(PlayItem_random_access_flag); // 1 bslbf
|
|
writer.putBits(7, 0); // reserved_for_future_use 7 bslbf
|
|
writer.putBits(8, 0); // still_mode
|
|
writer.putBits(16, 0); // reserved after stillMode != 0x01
|
|
composeSTN_table(writer, playItemNum, false);
|
|
*lengthPos = my_htons(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void MPLSParser::parsePlayListMark(uint8_t* buffer, int len)
|
|
{
|
|
BitStreamReader reader;
|
|
reader.setBuffer(buffer, buffer + len);
|
|
uint32_t length = reader.getBits(32); //
|
|
int number_of_PlayList_marks = reader.getBits(16); // 16 uimsbf
|
|
for (int PL_mark_id = 0; PL_mark_id < number_of_PlayList_marks; PL_mark_id++)
|
|
{
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
int mark_type = reader.getBits(8); // 8 bslbf
|
|
int ref_to_PlayItem_id = reader.getBits(16);
|
|
uint32_t mark_time_stamp = reader.getBits(32); // 32 uimsbf
|
|
int entry_ES_PID = reader.getBits(16); // 16 uimsbf
|
|
uint32_t duration = reader.getBits(32); // 32 uimsbf
|
|
if (mark_type == 1) // mark_type 0x01 = Chapter search
|
|
m_marks.push_back(PlayListMark(ref_to_PlayItem_id, mark_time_stamp));
|
|
}
|
|
}
|
|
|
|
int MPLSParser::calcPlayItemID(MPLSStreamInfo& streamInfo, uint32_t pts)
|
|
{
|
|
for (size_t i = 0; i < streamInfo.m_index.size(); i++)
|
|
{
|
|
if (streamInfo.m_index[i].size() > 0)
|
|
{
|
|
if (streamInfo.m_index[i].begin()->first > pts)
|
|
return FFMAX(i, 1) - 1;
|
|
}
|
|
}
|
|
return streamInfo.m_index.size() - 1;
|
|
}
|
|
|
|
void MPLSParser::composePlayListMark(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(32, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
MPLSStreamInfo& streamInfo = MPLSParser::getMainStream();
|
|
if (m_marks.size() == 0)
|
|
{
|
|
if (m_chapterLen == 0)
|
|
m_marks.push_back(PlayListMark(-1, IN_time));
|
|
else
|
|
{
|
|
for (size_t i = IN_time; i < OUT_time; i += m_chapterLen * 45000) m_marks.push_back(PlayListMark(-1, i));
|
|
}
|
|
}
|
|
writer.putBits(16, m_marks.size()); // 16 uimsbf
|
|
for (auto& i : m_marks)
|
|
{
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
writer.putBits(8, 1); // mark_type 0x01 = Chapter search
|
|
if (i.m_playItemID >= 0)
|
|
writer.putBits(16, i.m_playItemID); // play item ID
|
|
else
|
|
writer.putBits(16, calcPlayItemID(streamInfo, i.m_markTime * 2)); // play item ID
|
|
writer.putBits(32, i.m_markTime); // 32 uimsbf
|
|
writer.putBits(16, 0xffff); // entry_ES_PID always 0xffff for mark_type 1
|
|
writer.putBits(32, 0); // duration always 0 for mark_type 1
|
|
}
|
|
*lengthPos = my_htonl(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
/*
|
|
void MPLSParser::composePlayListMark(BitStreamWriter& writer)
|
|
{
|
|
uint32_t* lengthPos = (uint32_t*) (writer.getBuffer() + writer.getBitsCount()/8);
|
|
writer.putBits(32,0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount()/8;
|
|
|
|
writer.putBits(16, 1); // number_of_PlayList_marks
|
|
//for(int PL_mark_id=0; PL_mark_id<number_of_PlayList_marks; PL_mark_id++)
|
|
//{
|
|
writer.putBits(8, 0 ); //reserved_for_future_use 8 bslbf
|
|
writer.putBits(8, 1); // mark_type
|
|
|
|
writer.putBits(16, 0); // ref_to_PlayItem_id
|
|
writer.putBits(32, IN_time); // mark_time_stamp
|
|
writer.putBits(16, 0xffff); // entry_ES_PID
|
|
writer.putBits(32, 0); // duration
|
|
|
|
//}
|
|
*lengthPos = my_ntohl(writer.getBitsCount()/8 - beforeCount);
|
|
}
|
|
*/
|
|
|
|
void MPLSParser::composeSTN_table(BitStreamWriter& writer, int PlayItem_id, bool isSSEx)
|
|
{
|
|
uint16_t* lengthPos = (uint16_t*)(writer.getBuffer() + writer.getBitsCount() / 8);
|
|
writer.putBits(16, 0); // skip lengthField
|
|
int beforeCount = writer.getBitsCount() / 8;
|
|
writer.putBit(0); // Fixed_offset_during_PopUp_flag for ext mode
|
|
writer.putBits(15, 0); // reserved_for_future_use 16 bslbf
|
|
|
|
number_of_primary_video_stream_entries = 0;
|
|
number_of_secondary_video_stream_entries = 0;
|
|
number_of_primary_audio_stream_entries = 0;
|
|
number_of_secondary_audio_stream_entries = 0;
|
|
number_of_PG_textST_stream_entries = 0;
|
|
number_of_DolbyVision_video_stream_entries = 0;
|
|
|
|
std::vector<MPLSStreamInfo>& streamInfo = isSSEx ? m_streamInfoMVC : m_streamInfo;
|
|
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
if (i.isSecondary)
|
|
number_of_secondary_video_stream_entries++;
|
|
else if (i.HDR == 4)
|
|
number_of_DolbyVision_video_stream_entries++;
|
|
else
|
|
number_of_primary_video_stream_entries++;
|
|
}
|
|
else if (isAudioStreamType(stream_coding_type))
|
|
{
|
|
if (!i.isSecondary)
|
|
number_of_primary_audio_stream_entries++;
|
|
else
|
|
number_of_secondary_audio_stream_entries++;
|
|
}
|
|
else if (stream_coding_type == 0x90)
|
|
number_of_PG_textST_stream_entries++;
|
|
else
|
|
THROW(ERR_COMMON, "Unsupported media type " << stream_coding_type << " for AVCHD/Blu-ray muxing");
|
|
}
|
|
|
|
if (!isSSEx)
|
|
{
|
|
writer.putBits(8, number_of_primary_video_stream_entries); // 8 uimsbf // test subpath
|
|
writer.putBits(8, number_of_primary_audio_stream_entries);
|
|
writer.putBits(8, number_of_PG_textST_stream_entries);
|
|
writer.putBits(8, 0); // int number_of_IG_stream_entries = reader.getBits(8); //8 uimsbf
|
|
writer.putBits(8, number_of_secondary_audio_stream_entries);
|
|
writer.putBits(8, number_of_secondary_video_stream_entries); // int number_of_secondary_video_stream_entries.
|
|
// Now subpath used for second video stream
|
|
writer.putBits(8, 0); // number_of_PiP_PG_textST_stream_entries_plus
|
|
writer.putBits(8, number_of_DolbyVision_video_stream_entries);
|
|
writer.putBits(32, 0); // reserved_for_future_use 32 bslbf
|
|
}
|
|
|
|
// if (number_of_SubPaths == 0)
|
|
|
|
// video
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(stream_coding_type) && !i.isSecondary && i.HDR != 4)
|
|
{
|
|
i.composeStreamEntry(writer, PlayItem_id);
|
|
i.composeStreamAttributes(writer);
|
|
if (stream_coding_type == 0x20) // MVC
|
|
{
|
|
writer.putBits(10, 0); // reserved_for_future_use
|
|
writer.putBits(6, FFMAX(1, i.number_of_offset_sequences));
|
|
}
|
|
}
|
|
}
|
|
|
|
// primary audio
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isAudioStreamType(stream_coding_type) && !i.isSecondary)
|
|
{
|
|
i.composeStreamEntry(writer, PlayItem_id);
|
|
i.composeStreamAttributes(writer);
|
|
}
|
|
}
|
|
|
|
// PG
|
|
for (auto& i : m_streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (stream_coding_type == 0x90)
|
|
{
|
|
if (isSSEx)
|
|
{
|
|
i.composePGS_SS_StreamEntry(writer, PlayItem_id);
|
|
}
|
|
else
|
|
{
|
|
i.composeStreamEntry(writer, PlayItem_id);
|
|
i.composeStreamAttributes(writer);
|
|
}
|
|
}
|
|
}
|
|
|
|
// secondary audio
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isAudioStreamType(stream_coding_type) && i.isSecondary)
|
|
{
|
|
i.composeStreamEntry(writer, PlayItem_id);
|
|
i.composeStreamAttributes(writer);
|
|
if (number_of_secondary_video_stream_entries == 0)
|
|
{
|
|
writer.putBits(8, number_of_primary_audio_stream_entries); // number_of_primary_audio_ref_entries
|
|
// allowed to join with this stream
|
|
writer.putBits(8, 0); // word align
|
|
uint8_t primaryAudioNum = 0;
|
|
for (auto& j : streamInfo)
|
|
{
|
|
int stream_coding_type = j.stream_coding_type;
|
|
if (isAudioStreamType(stream_coding_type) && !j.isSecondary)
|
|
writer.putBits(8, primaryAudioNum++);
|
|
}
|
|
if (number_of_primary_audio_stream_entries % 2 == 1)
|
|
writer.putBits(8, 0); // word align
|
|
}
|
|
else
|
|
{
|
|
writer.putBits(16, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// secondary video
|
|
int secondaryVNum = 0;
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (i.isSecondary && isVideoStreamType(stream_coding_type))
|
|
{
|
|
i.type = 3;
|
|
i.composeStreamEntry(writer, PlayItem_id, secondaryVNum);
|
|
i.composeStreamAttributes(writer);
|
|
// comb_info_Secondary_video_Secondary_audio() {
|
|
int useSecondaryAudio = number_of_secondary_audio_stream_entries > secondaryVNum ? 1 : 0;
|
|
writer.putBits(8, useSecondaryAudio);
|
|
writer.putBits(8, 0); // reserved_for_word_align
|
|
if (useSecondaryAudio)
|
|
{
|
|
writer.putBits(8, secondaryVNum);
|
|
writer.putBits(8, 0); // word align
|
|
}
|
|
//}
|
|
|
|
// comb_info_Secondary_video_PiP_PG_textST(){
|
|
writer.putBits(8, 0); // number_of_PiP_PG_textST_ref_entries
|
|
writer.putBits(8, 0); // reserved_for_word_align
|
|
//}
|
|
secondaryVNum++;
|
|
}
|
|
}
|
|
|
|
// DV video
|
|
for (auto& i : streamInfo)
|
|
{
|
|
int stream_coding_type = i.stream_coding_type;
|
|
if (isVideoStreamType(stream_coding_type) && i.HDR == 4)
|
|
{
|
|
i.type = 4;
|
|
i.composeStreamEntry(writer, PlayItem_id);
|
|
i.composeStreamAttributes(writer);
|
|
}
|
|
}
|
|
|
|
if (isSSEx && writer.getBitsCount() % 32 != 0)
|
|
writer.putBits(16, 0);
|
|
|
|
*lengthPos = my_htons(writer.getBitsCount() / 8 - beforeCount);
|
|
}
|
|
|
|
void MPLSParser::STN_table(BitStreamReader& reader, int PlayItem_id)
|
|
{
|
|
int length = reader.getBits(16); // 16 uimsbf
|
|
reader.skipBits(16); // reserved_for_future_use 16 bslbf
|
|
number_of_primary_video_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_primary_audio_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_PG_textST_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_IG_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_secondary_audio_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_secondary_video_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
number_of_PiP_PG_textST_stream_entries_plus = reader.getBits(8); // 8 uimsbf
|
|
number_of_DolbyVision_video_stream_entries = reader.getBits(8); // 8 uimsbf
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
|
|
for (int primary_video_stream_id = 0; primary_video_stream_id < number_of_primary_video_stream_entries;
|
|
primary_video_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
}
|
|
for (int primary_audio_stream_id = 0; primary_audio_stream_id < number_of_primary_audio_stream_entries;
|
|
primary_audio_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
}
|
|
|
|
for (int PG_textST_stream_id = 0;
|
|
PG_textST_stream_id < number_of_PG_textST_stream_entries + number_of_PiP_PG_textST_stream_entries_plus;
|
|
PG_textST_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
}
|
|
|
|
for (int IG_stream_id = 0; IG_stream_id < number_of_IG_stream_entries; IG_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
}
|
|
|
|
for (int secondary_audio_stream_id = 0; secondary_audio_stream_id < number_of_secondary_audio_stream_entries;
|
|
secondary_audio_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.isSecondary = true;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
|
|
// comb_info_Secondary_audio_Primary_audio(){
|
|
int number_of_primary_audio_ref_entries = reader.getBits(8); // 8 uimsbf
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
for (int i = 0; i < number_of_primary_audio_ref_entries; i++)
|
|
{
|
|
int primary_audio_stream_id_ref = reader.getBits(8); // 8 uimsbf
|
|
}
|
|
if (number_of_primary_audio_ref_entries % 2 == 1)
|
|
{
|
|
reader.skipBits(8); // reserved_for_word_align
|
|
}
|
|
//}
|
|
}
|
|
for (int secondary_video_stream_id = 0; secondary_video_stream_id < number_of_secondary_video_stream_entries;
|
|
secondary_video_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.isSecondary = true;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
|
|
// comb_info_Secondary_video_Secondary_audio(){
|
|
int number_of_secondary_audio_ref_entries = reader.getBits(8); // 8 uimsbf
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
for (int i = 0; i < number_of_secondary_audio_ref_entries; i++)
|
|
{
|
|
int secondary_audio_stream_id_ref = reader.getBits(8); // 8 uimsbf
|
|
}
|
|
if (number_of_secondary_audio_ref_entries % 2 == 1)
|
|
{
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
}
|
|
//}
|
|
// comb_info_Secondary_video_PiP_PG_textST(){
|
|
int number_of_PiP_PG_textST_ref_entries = reader.getBits(8); // 8 uimsbf
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
for (int i = 0; i < number_of_PiP_PG_textST_ref_entries; i++)
|
|
{
|
|
int PiP_PG_textST_stream_id_ref = reader.getBits(8); // 8 uimsbf
|
|
}
|
|
if (number_of_PiP_PG_textST_ref_entries % 2 == 1)
|
|
{
|
|
reader.skipBits(8); // reserved_for_word_align 8 bslbf
|
|
}
|
|
//}
|
|
}
|
|
for (int DV_video_stream_id = 0; DV_video_stream_id < number_of_DolbyVision_video_stream_entries;
|
|
DV_video_stream_id++)
|
|
{
|
|
MPLSStreamInfo streamInfo;
|
|
streamInfo.parseStreamEntry(reader);
|
|
streamInfo.parseStreamAttributes(reader);
|
|
if (PlayItem_id == 0)
|
|
m_streamInfo.push_back(streamInfo);
|
|
}
|
|
}
|
|
|
|
// ------------- M2TSStreamInfo -----------------------
|
|
|
|
void M2TSStreamInfo::blurayStreamParams(double fps, bool interlaced, int width, int height, float ar, int* video_format,
|
|
int* frame_rate_index, int* aspect_ratio_index)
|
|
{
|
|
*video_format = 0;
|
|
*frame_rate_index = 0;
|
|
*aspect_ratio_index = 3; // 16:9; 2 = 4:3
|
|
|
|
bool isNtsc = width <= 854 && height <= 480 && (fabs(25 - fps) >= 0.5 && fabs(50 - fps) >= 0.5);
|
|
bool isPal = width <= 1024 && height <= 576 && (fabs(25 - fps) < 0.5 || fabs(50 - fps) < 0.5);
|
|
if (isNtsc)
|
|
*video_format = interlaced ? 1 : 3;
|
|
else if (isPal)
|
|
*video_format = interlaced ? 2 : 7;
|
|
else if (width >= 2600)
|
|
*video_format = 8;
|
|
else if (width >= 1300)
|
|
*video_format = interlaced ? 4 : 6; // as 1920x1080
|
|
else
|
|
*video_format = 5; // as 1280x720
|
|
|
|
if (width < 1080 && isV3())
|
|
LTRACE(LT_WARN, 2, "Warning: video height < 1080 is not standard for V3 Blu-ray.");
|
|
if (interlaced && isV3())
|
|
LTRACE(LT_WARN, 2, "Warning: interlaced video is not standard for V3 Blu-ray.");
|
|
|
|
if (fabs(fps - 23.976) < 1e-4)
|
|
*frame_rate_index = 1;
|
|
else if (fabs(fps - 24.0) < 1e-4)
|
|
*frame_rate_index = 2;
|
|
else if (fabs(fps - 25.0) < 1e-4)
|
|
*frame_rate_index = 3;
|
|
else if (fabs(fps - 29.97) < 1e-4)
|
|
*frame_rate_index = 4;
|
|
else if (fabs(fps - 50.0) < 1e-4)
|
|
*frame_rate_index = 6;
|
|
else if (fabs(fps - 59.94) < 1e-4)
|
|
*frame_rate_index = 7;
|
|
|
|
if (ar == AR_3_4 || ar == AR_VGA)
|
|
*aspect_ratio_index = 2; // 4x3
|
|
}
|
|
|
|
M2TSStreamInfo::M2TSStreamInfo(const PMTStreamInfo& pmtStreamInfo)
|
|
{
|
|
streamPID = pmtStreamInfo.m_pid;
|
|
stream_coding_type = pmtStreamInfo.m_streamType;
|
|
m_index = pmtStreamInfo.m_index;
|
|
isSecondary = pmtStreamInfo.isSecondary;
|
|
memset(&language_code, 0, 4);
|
|
memcpy(language_code, pmtStreamInfo.m_lang, 3);
|
|
|
|
character_code = 0;
|
|
video_format = 0;
|
|
frame_rate_index = 0;
|
|
number_of_offset_sequences = 0;
|
|
audio_presentation_type = 0;
|
|
sampling_frequency_index = 0;
|
|
aspect_ratio_index = 3; // 16:9; 2 = 4:3
|
|
// memcpy(language_code, "eng", 3); // todo: delete this line
|
|
if (pmtStreamInfo.m_codecReader != 0)
|
|
{
|
|
MPEGStreamReader* vStream = dynamic_cast<MPEGStreamReader*>(pmtStreamInfo.m_codecReader);
|
|
if (vStream)
|
|
{
|
|
width = vStream->getStreamWidth();
|
|
height = vStream->getStreamHeight();
|
|
HDR = vStream->getStreamHDR();
|
|
VideoAspectRatio ar = vStream->getStreamAR();
|
|
blurayStreamParams(vStream->getFPS(), vStream->getInterlaced(), width, height, ar, &video_format,
|
|
&frame_rate_index, &aspect_ratio_index);
|
|
if (ar == AR_3_4)
|
|
width = height * 4 / 3;
|
|
else if (ar == AR_16_9)
|
|
width = height * 16 / 9;
|
|
else if (ar == AR_221_100)
|
|
width = height * 221 / 100;
|
|
}
|
|
H264StreamReader* h264Stream = dynamic_cast<H264StreamReader*>(pmtStreamInfo.m_codecReader);
|
|
if (h264Stream)
|
|
number_of_offset_sequences = h264Stream->getOffsetSeqCnt();
|
|
|
|
SimplePacketizerReader* aStream = dynamic_cast<SimplePacketizerReader*>(pmtStreamInfo.m_codecReader);
|
|
if (aStream)
|
|
{
|
|
audio_presentation_type = aStream->getChannels();
|
|
if (audio_presentation_type == 2)
|
|
audio_presentation_type = 3;
|
|
else if (audio_presentation_type > 3)
|
|
audio_presentation_type = 6;
|
|
int freq = aStream->getFreq();
|
|
// todo: add index 12 and 14. 12: 48Khz core, 192Khz mpl, 14: 48Khz core, 96Khz mlp
|
|
switch (freq)
|
|
{
|
|
case 48000:
|
|
if (aStream->getAltFreq() == 96000)
|
|
sampling_frequency_index = 14;
|
|
else if (aStream->getAltFreq() == 192000)
|
|
sampling_frequency_index = 12;
|
|
else
|
|
sampling_frequency_index = 1;
|
|
break;
|
|
case 96000:
|
|
if (aStream->getAltFreq() == 192000)
|
|
sampling_frequency_index = 12;
|
|
else
|
|
sampling_frequency_index = 4;
|
|
break;
|
|
case 192000:
|
|
sampling_frequency_index = 5;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
M2TSStreamInfo::M2TSStreamInfo(const M2TSStreamInfo& other)
|
|
{
|
|
streamPID = other.streamPID;
|
|
stream_coding_type = other.stream_coding_type;
|
|
video_format = other.video_format;
|
|
frame_rate_index = other.frame_rate_index;
|
|
number_of_offset_sequences = other.number_of_offset_sequences;
|
|
width = other.width;
|
|
height = other.height;
|
|
HDR = other.HDR;
|
|
aspect_ratio_index = other.aspect_ratio_index;
|
|
audio_presentation_type = other.audio_presentation_type;
|
|
sampling_frequency_index = other.sampling_frequency_index;
|
|
character_code = other.character_code;
|
|
memcpy(language_code, other.language_code, sizeof(language_code));
|
|
isSecondary = other.isSecondary;
|
|
m_index = other.m_index;
|
|
}
|
|
|
|
MPLSStreamInfo::MPLSStreamInfo(const MPLSStreamInfo& other) : M2TSStreamInfo(other)
|
|
{
|
|
type = other.type;
|
|
character_code = other.character_code;
|
|
offsetId = other.offsetId;
|
|
pipParams = other.pipParams;
|
|
isSSPG = other.isSSPG;
|
|
SS_PG_offset_sequence_id = other.SS_PG_offset_sequence_id;
|
|
leftEye = 0;
|
|
rightEye = 0;
|
|
if (other.leftEye)
|
|
leftEye = new MPLSStreamInfo(*other.leftEye);
|
|
if (other.rightEye)
|
|
rightEye = new MPLSStreamInfo(*other.rightEye);
|
|
}
|
|
|
|
// -------------- MPLSStreamInfo -----------------------
|
|
|
|
MPLSStreamInfo::MPLSStreamInfo()
|
|
: M2TSStreamInfo(), type(0), offsetId(0xff), isSSPG(false), SS_PG_offset_sequence_id(0xff), leftEye(0), rightEye(0)
|
|
{
|
|
}
|
|
|
|
MPLSStreamInfo::MPLSStreamInfo(const PMTStreamInfo& pmtStreamInfo)
|
|
: M2TSStreamInfo(pmtStreamInfo),
|
|
type(1),
|
|
offsetId(0xff),
|
|
isSSPG(false),
|
|
SS_PG_offset_sequence_id(0xff),
|
|
leftEye(0),
|
|
rightEye(0)
|
|
{
|
|
pipParams = pmtStreamInfo.m_codecReader->getPipParams();
|
|
}
|
|
|
|
MPLSStreamInfo::~MPLSStreamInfo()
|
|
{
|
|
delete leftEye;
|
|
delete rightEye;
|
|
}
|
|
|
|
void MPLSStreamInfo::parseStreamEntry(BitStreamReader& reader)
|
|
{
|
|
int length = reader.getBits(8); // 8 uimsbf
|
|
type = reader.getBits(8); // 8 bslbf
|
|
if (type == 1)
|
|
{
|
|
streamPID = reader.getBits(16); // 16 uimsbf
|
|
reader.skipBits(32); // reserved_for_future_use 48 bslbf
|
|
reader.skipBits(16);
|
|
}
|
|
else if (type == 2)
|
|
{
|
|
int ref_to_SubPath_id = reader.getBits(8); // 8 uimsbf
|
|
int ref_to_subClip_entry_id = reader.getBits(8); // 8 uimsbf
|
|
streamPID = reader.getBits(16); // 16 uimsbf
|
|
reader.skipBits(32); // reserved_for_future_use 32 bslbf
|
|
}
|
|
else if (type == 3)
|
|
{
|
|
int ref_to_SubPath_id = reader.getBits(8); // 8 uimsbf
|
|
streamPID = reader.getBits(16); // 16 uimsbf
|
|
reader.skipBits(32); // reserved_for_future_use 40 bslbf
|
|
reader.skipBits(8);
|
|
}
|
|
else if (type == 4)
|
|
{
|
|
reader.skipBits(8);
|
|
streamPID = reader.getBits(16); // 16 uimsbf
|
|
reader.skipBits(32); // reserved_for_future_use 40 bslbf
|
|
reader.skipBits(8);
|
|
}
|
|
}
|
|
|
|
void MPLSStreamInfo::composePGS_SS_StreamEntry(BitStreamWriter& writer, int entryNum)
|
|
{
|
|
writer.putBits(8, offsetId);
|
|
writer.putBits(4, 0); // reserved
|
|
writer.putBit(0); // dialog region offset valid
|
|
writer.putBit(isSSPG); // is SS PG
|
|
writer.putBit(0); // top AS PG
|
|
writer.putBit(0); // bottom AS PG
|
|
if (isSSPG)
|
|
{
|
|
leftEye->composeStreamEntry(writer, entryNum);
|
|
rightEye->composeStreamEntry(writer, entryNum);
|
|
writer.putBits(8, 0); // reserved
|
|
writer.putBits(8, SS_PG_offset_sequence_id);
|
|
}
|
|
}
|
|
|
|
void MPLSStreamInfo::composeStreamEntry(BitStreamWriter& writer, int entryNum, int subPathID)
|
|
{
|
|
uint8_t* lengthPos = writer.getBuffer() + writer.getBitsCount() / 8;
|
|
writer.putBits(8, 0); // int length = reader.getBits(8); //8 uimsbf
|
|
int initPos = writer.getBitsCount() / 8;
|
|
writer.putBits(8, type); // 8 bslbf
|
|
if (type == 1)
|
|
{
|
|
writer.putBits(16, streamPID); // 16 uimsbf
|
|
writer.putBits(32, 0); // reserved_for_future_use 48 bslbf
|
|
writer.putBits(16, 0);
|
|
}
|
|
else if (type == 2)
|
|
{
|
|
writer.putBits(8, 0); // ref_to_SubPath_id
|
|
writer.putBits(8, 0); // entryNum - ref_to_subClip_entry_id
|
|
writer.putBits(16, streamPID);
|
|
writer.putBits(32, 0); // reserved
|
|
}
|
|
else if (type == 3)
|
|
{
|
|
writer.putBits(8, subPathID); // ref_to_SubPath_id. constant subPathID == 0
|
|
writer.putBits(16, streamPID); // 16 uimsbf
|
|
writer.putBits(32, 0); // reserved_for_future_use 40 bslbf
|
|
writer.putBits(8, 0);
|
|
}
|
|
else if (type == 4)
|
|
{
|
|
writer.putBits(8, 0);
|
|
writer.putBits(16, streamPID); // 16 uimsbf
|
|
writer.putBits(32, 0); // reserved_for_future_use 40 bslbf
|
|
writer.putBits(8, 0);
|
|
}
|
|
else
|
|
THROW(ERR_COMMON, "Unsupported media type for AVCHD/Blu-ray muxing");
|
|
*lengthPos = writer.getBitsCount() / 8 - initPos;
|
|
}
|
|
|
|
void MPLSStreamInfo::parseStreamAttributes(BitStreamReader& reader)
|
|
{
|
|
int length = reader.getBits(8); // 8 uimsbf
|
|
stream_coding_type = reader.getBits(8); // 8 bslbf
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
video_format = reader.getBits(4); // 4 bslbf
|
|
frame_rate_index = reader.getBits(4); // 4 bslbf
|
|
reader.skipBits(24); // reserved_for_future_use 24 bslbf
|
|
}
|
|
else if (isAudioStreamType(stream_coding_type))
|
|
{
|
|
audio_presentation_type = reader.getBits(4); // 4 bslbf
|
|
int sampling_frequency_index = reader.getBits(4); // 4 bslbf
|
|
CLPIStreamInfo::readString(language_code, reader, 3);
|
|
}
|
|
else if (stream_coding_type == 0x90)
|
|
{
|
|
// Presentation Graphics stream
|
|
CLPIStreamInfo::readString(language_code, reader, 3);
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x91)
|
|
{
|
|
// Interactive Graphics stream
|
|
CLPIStreamInfo::readString(language_code, reader, 3);
|
|
reader.skipBits(8); // reserved_for_future_use 8 bslbf
|
|
}
|
|
else if (stream_coding_type == 0x92)
|
|
{
|
|
// Text subtitle stream
|
|
character_code = reader.getBits(8); // 8 bslbf
|
|
CLPIStreamInfo::readString(language_code, reader, 3);
|
|
}
|
|
}
|
|
|
|
void MPLSStreamInfo::composeStreamAttributes(BitStreamWriter& writer)
|
|
{
|
|
uint8_t* lengthPos = writer.getBuffer() + writer.getBitsCount() / 8;
|
|
writer.putBits(8, 0); // int length = reader.getBits(8); //8 uimsbf
|
|
int initPos = writer.getBitsCount() / 8;
|
|
|
|
writer.putBits(8, stream_coding_type); // 8 bslbf
|
|
if (isVideoStreamType(stream_coding_type))
|
|
{
|
|
writer.putBits(4, video_format); // 4 bslbf
|
|
writer.putBits(4, frame_rate_index); // 4 bslbf
|
|
if (HDR & 18)
|
|
writer.putBits(8, 0x12); // HDR10 or HDR10plus
|
|
else if (HDR == 4)
|
|
writer.putBits(8, 0x22); // DV
|
|
else
|
|
writer.putBits(8, 0);
|
|
if (HDR == 16)
|
|
writer.putBits(8, 0x40); // HDR10plus
|
|
else
|
|
writer.putBits(8, 0);
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
}
|
|
else if (isAudioStreamType(stream_coding_type))
|
|
{
|
|
writer.putBits(4, audio_presentation_type); // 4 bslbf
|
|
writer.putBits(4, sampling_frequency_index); // 4 bslbf
|
|
CLPIStreamInfo::writeString(language_code, writer, 3);
|
|
}
|
|
else if (stream_coding_type == 0x90)
|
|
{
|
|
// Presentation Graphics stream
|
|
CLPIStreamInfo::writeString(language_code, writer, 3);
|
|
writer.putBits(8, 0); // reserved_for_future_use 8 bslbf
|
|
}
|
|
else
|
|
THROW(ERR_COMMON, "Unsupported media type for AVCHD/Blu-ray muxing");
|
|
*lengthPos = writer.getBitsCount() / 8 - initPos;
|
|
}
|