段老师的干货时间又到咯,下面代码实现的是将 AAC 和 H264 数据打包成 PS 包的流程,其中包括了 PES 头、PSI 表头、MPEG-TS 头、AAC/H264 数据打包等多个步骤。此外,还包含 CRC32 校验等校验码的计算。需要注意的是,此代码示例仅供参考,具体实现需要根据实际需求进行调整和修改。
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
// 同步字节
const unsigned char SYNC_BYTE = 0x47;
int main() {
ofstream out_file("output.ps", ios::out | ios::binary);
vector<unsigned short> pid_list;
// 打包 PAT(Program Association Table)
PSITableHeader pat_header = {0};
pat_header.m_table_id = 0x00;
pat_header.m_section_syntax_indicator = true;
pat_header.m_section_length = 13;
out_file.write((const char*)&SYNC_BYTE, 1);
out_file.write((const char*)&pat_header, sizeof(pat_header));
unsigned short transport_stream_id = 1;
unsigned short program_number = 1;
unsigned short program_map_PID = 100;
unsigned char section_number = 0;
unsigned char last_section_number = 0;
out_file.write((const char*)&transport_stream_id, 2);
out_file.write((const char*)§ion_number, 1);
out_file.write((const char*)&last_section_number, 1);
out_file.write((const char*)&program_number, 2);
out_file.write("", 3); // 预留为 "111"
out_file.write((const char*)&program_map_PID, 2);
out_file.write("", 4); // 预留为 CRC32 校验码
PATSection pat_section = {0};
pat_section.m_program_number = program_number;
pat_section.m_PID = program_map_PID;
// 打包 PMT(Program Map Table)
PSITableHeader pmt_header = {0};
pmt_header.m_table_id = 0x02;
pmt_header.m_section_syntax_indicator = true;
pmt_header.m_section_length = 13;
out_file.write((const char*)&SYNC_BYTE, 1);
out_file.write((const char*)&pmt_header, sizeof(pmt_header));
unsigned char pcr_pid = 100;
unsigned char program_info_length = 0;
section_number = 0;
last_section_number = 0;
out_file.write((const char*)&program_number, 2);
out_file.write("", 3); // 预留为 "111"
out_file.write((const char*)&pcr_pid, 2);
out_file.write("", 2); // 预留为 "1111"
out_file.write(&program_info_length, 1);
PMTSection pmt_section = {0};
pmt_section.m_program_number = program_number;
pmt_section.m_program_map_PID = program_map_PID;
pmt_section.m_stream_types.push_back(0x1B); // 音频类型
pmt_section.m_stream_types.push_back(0x1B); // 视频类型
pmt_section.m_elementary_PIDs.push_back(0xc0); // 音频 PID
pmt_section.m_elementary_PIDs.push_back(0xe0); // 视频 PID
// 按照顺序将 PES 包和 MPEG-TS 头打包成 PS 包
const int AAC_PACKET_SIZE = 188 - sizeof(TSHeader); // AAC 数据包大小
const int H264_PACKET_SIZE = 188 - sizeof(TSHeader); // H264 数据包大小
unsigned char aac_packet[AAC_PACKET_SIZE];
unsigned char h264_packet[H264_PACKET_SIZE];
int aac_packet_len = 0, h264_packet_len = 0;
ifstream aac_file("audio.aac", ios::in | ios::binary);
ifstream h264_file("video.h264", ios::in | ios::binary);
while (true) {
// 处理 AAC 数据
aac_file.read((char*)aac_packet, AAC_PACKET_SIZE);
aac_packet_len = aac_file.gcount();
if (aac_packet_len == 0) {
break;
}
PackAACData(aac_packet, aac_packet_len, out_file);
// 处理 H264 数据
h264_file.read((char*)h264_packet, H264_PACKET_SIZE);
h264_packet_len = h264_file.gcount();
if (h264_packet_len == 0) {
break;
}
PackH264Data(h264_packet, h264_packet_len, out_file, pid_list);
}
aac_file.close();
h264_file.close();
// 打包 PAT 表的 CRC32 校验码
out_file.seekp(sizeof(SYNC_BYTE) + sizeof(pat_header) + pat_header.m_section_length - 4, ios::beg);
unsigned int crc32 = 0xffffffff;
for (int i = 0; i < sizeof(pat_section); i++) {
crc32 ^= ((unsigned char*)(&pat_section))[i] << 24;
for (int j = 0; j < 8; j++) {
if (crc32 & 0x80000000) {
crc32 = (crc32 << 1) ^ 0x04c11db7;
} else {
crc32 <<= 1;
}
}
}
crc32 = ~crc32;
out_file.write((const char*)&crc32, 4);
// 打包 PMT 表的 CRC32 校验码
out_file.seekp(sizeof(SYNC_BYTE) + sizeof(pmt_header) + pmt_header.m_section_length - 4, ios::beg);
crc32 = 0xffffffff;
for (int i = 0; i < sizeof(pmt_section); i++) {
crc32 ^= ((unsigned char*)(&pmt_section))[i] << 24;
for (int j = 0; j < 8; j++) {
if (crc32 & 0x80000000) {
crc32 = (crc32 << 1) ^ 0x04c11db7;
} else {
crc32 <<= 1;
}
}
}
crc32 = ~crc32;
out_file.write((const char*)&crc32, 4);
// 打包 MPEG-TS 头和数据成 TS 包
const int TS_PACKET_SIZE = 188; // TS 数据包大小
unsigned char ts_packet[TS_PACKET_SIZE];
int ts_packet_len = 0;
ifstream in_file("output.ps", ios::in | ios::binary);
in_file.read((char*)ts_packet, 4); // 跳过前面的 SYNC_BYTE 和 PAT 表头
in_file.read((char*)ts_packet, TS_PACKET_SIZE);
while (in_file.gcount() == TS_PACKET_SIZE) {
ts_packet_len = TS_PACKET_SIZE;
for (int i = 0; i < pid_list.size(); i++) {
unsigned short pid = pid_list[i];
if (ts_packet[1] >> 4 == 0x01 && (ts_packet[1] & 0x0f) == pid) {
TSHeader* ts_header = (TSHeader*)ts_packet;
ts_header->m_pid = program_map_PID;
if (ts_header->m_adaptation_field_control == 0x02 || ts_header->m_adaptation_field_control == 0x03) {
int padding = TS_PACKET_SIZE - ts_packet_len - 1;
unsigned char* adaptation_field = ts_packet + ts_packet_len;
*adaptation_field++ = padding - 1;
memset(adaptation_field, 0xff, padding);
ts_packet_len += padding + 1;
}
out_file.write((const char*)ts_packet, ts_packet_len);
break;
}
}
in_file.read((char*)ts_packet, TS_PACKET_SIZE);
}
in_file.close();
out_file.close();
return 0;
}