音视频封装demo:将h264数据和aac数据封装(mux)成TS文件(纯手工,不依赖第三方开源库)
1、README
a. demo使用
$ make clean && make DEBUG=1 $ $ ./ts_mux_h264_aac Usage: ./ts_mux_h264_aac avfile/test1_856x480_24fps.h264 24 avfile/test1_44100_stereo.aac out1.ts ./ts_mux_h264_aac avfile/test2_720x480_30fps.h264 60 avfile/test2_48000_stereo.aac out2.ts ./ts_mux_h264_aac avfile/test3_1280x720_20fps.h264 20 avfile/test1_44100_stereo.aac out3.ts
(注:目前合成的out1.ts在使用potPlayer播放时进度条有点问题,待修复。)
(图片来源网络,侵删)
目前合成视频的现象:
(out2.ts含有除SPS、PPS、IDR、PSLICE以外的其他NALU,所以主要看out1.ts和out3.ts)
- out1.ts:
- 电影和电视、ACG播放器
- 播放不流畅(含有B帧,而程序只填pts没填dts导致,对比out3.ts可知,无关紧要);
- 正常播放进度条时长正常(29s),拖动后有声音但画面卡住一会才动、音视频同步;
- VLC
- 播放不流畅(含有B帧,而程序只填pts没填dts导致,对比out3.ts可知,无关紧要);
- 正常播放进度条时长正常(29s),拖动后有声音但画面会灰屏有画面在动过一会才恢复、音视频同步;
- potplayer
- 播放流畅;
- 正常播放时进度条时长不正常(21s),拖动画面不会卡住、但音视频有时不同步;
- out3.ts
- 电影和电视、ACG播放器
- 播放流畅;
- 正常播放进度条时长正常(30s),拖动后有声音但画面卡住一会才动、音视频“同步”;
- VLC
- 播放流畅;
- 正常播放进度条时长正常(30s),拖动后有声音但画面会灰屏有画面在动过一会才恢复、音视频“同步”;
- potplayer
- 播放流畅;
- 正常播放时进度条时长不正常(8s),拖动画面和声音都会卡住一会才动;
b. 参考文章
-
【科普】TS文件格式:什么是TS?如何打开和转换它?_都叫兽软件
-
TS封装格式 - CrazyDiode - 博客园(推荐!!看这篇基本就够了!!!其余文章作为参考即可。)
-
TS文件格式_LaugustusJ的博客-CSDN博客_ts文件(没仔细看)
-
TS 文件格式解析_影音视频技术-CSDN博客_ts格式解析(没仔细看)
-
测试验证:h264实时流封装ts文件存储,完整实现 - jamin-snails的个人空间 - OSCHINA - 中文开源技术交流社区(没仔细看)
工具下载:
- SpecialFTS.exe(demo中的tools/SpecialFTS_1.1.7z)【源码下载地址】
参考源码:
- https://download.csdn.net/download/zhuweigangzwg/5605869?spm=1003.2166.3001.6637.7(推荐!!)
- https://github.com/Jsnails/MUX_TS(根据前者进行改动的,可以不用看)
c. demo目录架构
$ tree . ├── aac_adts.c ├── aac_adts.h ├── avfile │ ├── out1.ts │ ├── out2.ts │ ├── out3.ts │ ├── test1_44100_stereo.aac │ ├── test1_856x480_24fps.h264 │ ├── test2_48000_stereo.aac │ ├── test2_720x480_30fps.h264 │ └── test3_1280x720_20fps.h264 ├── crcLib.c ├── crcLib.h ├── docs │ ├── TS封装格式 - CrazyDiode - 博客园.mhtml │ ├── TS文件格式_LaugustusJ的博客-CSDN博客_ts文件.mhtml │ ├── TS 文件格式解析_影音视频技术-CSDN博客_ts格式解析.mhtml │ ├── 测试验证:h264实时流封装ts文件存储,完整实现 - jamin-snails的个人空间 - OSCHINA - 中文开源技术交流社区.mhtml │ └── 【科普】TS文件格式:什么是TS?如何打开和转换它?_都叫兽软件.mhtml ├── h264_nalu.c ├── h264_nalu.h ├── main.c ├── Makefile ├── README.md ├── reference_src │ ├── H264_AAC_TS_MUX.tar.bz2 │ └── MUX_TS-master.zip ├── tools │ └── SpecialFTS_1.1.7z ├── ts.c └── ts.h
2、主要代码片段
ts.h
#ifndef __TS_H__ #define __TS_H__ #include #include #include #include //#define ENABLE_DEBUG #ifdef ENABLE_DEBUG #define DEBUG(fmt, args...) printf(fmt, ##args) #else #define DEBUG(fmt, args...) #endif #define TS_HEADER_SYNC_BYTE 0x47 #define TS_PACKET_HEADER_SIZE 4 #define TS_PACKET_SIZE 188 /* PID: 13 bit */ #define TS_PID_PAT 0x0000 /* fixed */ #define TS_PID_PMT 0x0FFF /* custom */ #define TS_PID_H264 0x0007 /* custom */ #define TS_PID_AAC 0x0008 /* custom */ #define TS_STREAM_ID_VIDEO 0xE0 /* always */ #define TS_STREAM_ID_AUDIO 0xC0 /* always */ #define PMT_STREAM_TYPE_H264 0x1B /* fixed */ #define PMT_STREAM_TYPE_AAC 0x0F /* fixed */ #define PMT_STREAM_TYPE_MP3 0x03 /* fixed */ /* ts layer header element member. * [Note: It is not stored as defined type size!!!] */ typedef struct tsHeader { uint8_t sync_byte; // 8b, must be 0x47 uint8_t transport_error_indicator; // 1b, transport error flag, always '0'. '1': after the adapt field of the ts head is a useless byte, this byte is in the adaptive area length. uint8_t payload_unit_start_indicator; // 1b, the load unit starts the identifier, and a complete packet begins with a tag of '1'. uint8_t transport_priority; // 1b, always '0'. uint16_t pid; // 13b, packet id. uint8_t transport_scrambling_control; // 2b, always '00'. transfer disturbance control. uint8_t adaptation_field_control; // 2b, whether has adaption field. '00': reserved, '01': playload, '10': adaption_field, '11': playload+adaption_field. uint8_t continuity_counter; // 4b, increasing counter, 0~f, the starting value is not necessarily 0, but it must be continuous. }T_TsHeader, *PT_TsHeader; /* adaptationField element member. * [Note: It is not stored as defined type size!!!] */ typedef struct adaptationField { uint8_t adaptation_field_length; // 1B uint8_t discontinuty_indicator; // 1b, the continuous state of the current transfer. uint8_t random_access_indicator; // 1b, the next pes group with the same pid should contain the PTS fields and a raw flow access point. uint8_t elementary_stream_priority_indicator; // 1b, priority /* 5 flags for optional fields */ uint8_t pcr_flag; // 1b uint8_t opcr_flag; // 1b uint8_t splicing_point_flag; // 1b uint8_t transport_private_data_flag; // 1b uint8_t adaptation_field_extension_flag; // 1b /* optional fields */ uint64_t pcr; // 42b, Program Clock Reference uint64_t opcr; // 42b uint8_t splice_countdown; // 1B uint8_t transport_private_data_len; // 1B uint8_t transport_private_data[256]; }T_AdaptationField, *PT_AdaptationField; /* pts and dts element member. * [Note: It is not stored as defined type size!!!] */ typedef struct tsPtsDts { uint8_t reserved_1; // 4b uint8_t pts_32_30; // 3b uint8_t marker_bit1; // 1b uint32_t pts_29_15; // 15b uint8_t marker_bit2; // 1b uint32_t pts_14_0; // 15b uint8_t marker_bit3; // 1b uint8_t reserved_2; // 4b uint8_t dts_32_30; // 3b uint8_t marker_bit4; // 1b uint32_t dts_29_15; // 15b uint8_t marker_bit5; // 1b uint32_t dts_14_0; // 15b uint8_t marker_bit6; // 3b }TsPtsDts; /* PAT element member. * [Note: It is not stored as defined type size!!!] */ typedef struct pat { uint8_t table_id; // 8b, PAT is fixed 0x00. uint8_t section_syntax_indicator; // 1b, fixed '1'. uint8_t zero; // 1b, fixed '0'. uint8_t reserved_1; // 2b, fixed '11'. uint16_t section_length; // 12b, length of data behind, comtain "crc32 code". uint16_t transport_stream_id; // 16b, fixed 0x0001. uint8_t reserved_2; // 2b, fixed '11'. uint8_t version_number; // 5b, fixed '00000'. uint8_t current_next_indicator; // 1b, fixed '1', the "pat" is available, '0' is wait for next "pat". uint8_t section_number; // 8b, fixed 0x00. uint8_t last_section_number; // 8b, fixed 0x00. /* loop start */ uint32_t program_number; // 16b, 0x0000: NIT, 0x0001: PMT. uint8_t reserved_3; // 3b, fixed '111'. uint32_t pid; // 13b, program_number pid /* loop end */ uint32_t crc_32; // 32b, the crc32 check code for the previous data }T_Pat, *PT_Pat; /* PMT element member. * [Note: It is not stored as defined type size!!!] */ typedef struct pmt { uint8_t table_id; // 8b, PMT is fixed 0x02. uint8_t section_syntax_indicator; // 1b, fixed '1'. uint8_t zero; // 1b, fixed '0'. uint8_t reserved_1; // 2b, fixed '11'. uint32_t section_length; // 12b, length of data behind, comtain "crc32 code". uint32_t program_number; // 16b, pid of applicable program. uint8_t reserved_2; // 2b, fixed '11'. uint8_t version_number; // 5b, fixed '00000', if pat variable will be '00001'. uint8_t current_next_indicator; // 1b, fixed '1'. uint8_t section_number; // 8b, fixed 0x00. uint8_t last_section_number; // 8b, fixed 0x00. uint8_t reserved_3; // 3b, fixed '111'. uint32_t pcr_pid; // 13b, The pcr is grouped by the ts, which is designated as the video pid. uint8_t reserved_4; // 4b, fixed '1111'. uint32_t program_info_length; // 12b, program describes, 0x000 for no. /* loop start */ /* program 1 */ uint8_t stream_type_video; // 8b, stream type. 0x1b: h264 0x0f: aac 0x03: mp3 uint8_t reserved_5_video; // 3b, fixed '111' uint32_t elementary_pid_video; // 13b, pid of "stream type" uint8_t reserved_6_video; // 4b, fixed '1111' uint32_t es_info_length_video; // 12b, program describes, 0x000 for no. /* program 2 */ uint8_t stream_type_audio; uint8_t reserved_5_audio; uint32_t elementary_pid_audio; uint8_t reserved_6_audio; uint32_t es_info_length_audio; /* loop end */ uint32_t crc_32; // 32b, the crc32 check code for the previous data }T_Pmt, *PT_Pmt; /* custom "PES" structure, it includes "pes header" and "es data". * so the member's size is different in ts file. */ typedef struct { /* "pes header"(not contain pts/dts) : 9 Bytes */ uint32_t packet_start_code_prefix; // 3B, start code, must be 0x000001. uint8_t stream_id; // 1B, audio(0xc0~0xdf), always 0xc0; video(0xe0~0xef), always 0xe0. uint32_t pes_packet_length; // 2B, the length of the data behind, 0 indicates unlimited length. // 1B: always 0x80 in this Byte. uint8_t marker_bit; // 2b, must be '10'. uint8_t pes_scrambling_control; // 2b, pes packet scrambling control. uint8_t pes_priority; // 1b, pes packet priority. uint8_t data_alignment_indicator; // 1b, '1': the follows video or audio syncword is shown in the back of the pes package. uint8_t copyright; // 1b, copyright protection. uint8_t original_or_copy; // 1b, playload source. // 1B: 0x80: only pts; 0xc0: pts+dts uint8_t pts_dts_flags; // 2b, '10': PTS; '11': PTS+DTS, '00': none, '01': reserved. uint8_t escr_flag; // 1b, '1': escr_base+escr_expand; '0': none. uint8_t es_rate_flag; // 1b, es rate. uint8_t dsm_trick_mode_flag; // 1b, whether the 8 bitt connection field exists. uint8_t additional_copy_info_flag; // 1b, additional copy information. uint8_t pes_crc_flag; // 1b, whether there is a call. uint8_t pes_extension_flag; // 1b, extension. uint8_t pes_data_length; // 1B, 5 or 10, the length of the data(pts, dts) behind. TsPtsDts ts_pts_dts; // pts+dts structure. /* "es data": 1024*1024+4 Bytes max */ //uint8_t av_data[MAX_ONE_FRAME_SIZE]; // one frame audio or video data. uint8_t* av_data; // one frame audio or video data. uint8_t* av_data_cur_ptr; // position for current "av_data" uint32_t av_data_len; // length of data. }T_PesEsStruct, *PT_PesEsStruct; /************************************************************************ * function describe: Mux h264 and aac to TS file. * params: * [h264FileName]: h264 file.(in) * [vFps]: video fps.(in) * [aacFileName]: aac file file.(in) * [tsFileName]: ts file.(in) * return: 0-success other-error ************************************************************************/ int ts_mux_h264_aac(char *h264FileName, uint32_t vFps, char *aacFileName, char *tsFileName); #endifts.c
#include "h264_nalu.h" #include "aac_adts.h" #include "ts.h" #include "crcLib.h" static uint32_t endian_convert(uint32_t val) { return (((val >> 0) & 0xff) > 8) & 0xff) > 16) & 0xff) > 24) & 0xff) if(!ptAdaptationField) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } ptAdaptationField-adaptation_field_length = 7; /* pcr_flag = 1: has pcr */ ptAdaptationField->discontinuty_indicator = 0; ptAdaptationField->random_access_indicator = 0; /* is keyflame? always 0, you can set it such "iskeyflame?1:0" */ ptAdaptationField->elementary_stream_priority_indicator = 0; /* 5 flags for optional fields */ ptAdaptationField->pcr_flag = 1; ptAdaptationField->opcr_flag = 0; ptAdaptationField->splicing_point_flag = 0; ptAdaptationField->transport_private_data_flag = 0; ptAdaptationField->adaptation_field_extension_flag = 0; ptAdaptationField->pcr = pts * 300; ptAdaptationField->opcr = 0; ptAdaptationField->splice_countdown = 0; ptAdaptationField->transport_private_data_len = 0; return 0; } static int fixCommonAdaptionFieldStruct(T_AdaptationField *ptAdaptationField) { if(!ptAdaptationField) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } ptAdaptationField->adaptation_field_length = 1; /* 5 flags = 0 */ ptAdaptationField->discontinuty_indicator = 0; ptAdaptationField->random_access_indicator = 0; /* is keyflame? always 0, you can set it such "iskeyflame?1:0" */ ptAdaptationField->elementary_stream_priority_indicator = 0; /* 5 flags for optional fields */ ptAdaptationField->pcr_flag = 0; ptAdaptationField->opcr_flag = 0; ptAdaptationField->splicing_point_flag = 0; ptAdaptationField->transport_private_data_flag = 0; ptAdaptationField->adaptation_field_extension_flag = 0; ptAdaptationField->pcr = 0; ptAdaptationField->opcr = 0; ptAdaptationField->splice_countdown = 0; ptAdaptationField->transport_private_data_len = 0; return 0; } static int fixAdtsFrameToPesStruct(uint8_t *adtsData, uint32_t adtsDataLen, uint64_t pts, T_PesEsStruct *ptAdtsPesStu) { if(!ptAdtsPesStu || !adtsData || !adtsDataLen) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* pes header */ ptAdtsPesStu->packet_start_code_prefix = 0x000001; ptAdtsPesStu->stream_id = TS_STREAM_ID_AUDIO; ptAdtsPesStu->pes_packet_length = 0; ptAdtsPesStu->marker_bit = 0b10; ptAdtsPesStu->pes_scrambling_control = 0b00; ptAdtsPesStu->pes_priority = 0b0; ptAdtsPesStu->data_alignment_indicator = 0b0; ptAdtsPesStu->copyright = 0b0; ptAdtsPesStu->original_or_copy = 0b0; ptAdtsPesStu->pts_dts_flags = 0b10; // '10': audio(aac) only PTS ptAdtsPesStu->escr_flag = 0b0; ptAdtsPesStu->es_rate_flag = 0b0; ptAdtsPesStu->dsm_trick_mode_flag = 0b0; ptAdtsPesStu->additional_copy_info_flag = 0b0; ptAdtsPesStu->pes_crc_flag = 0b0; ptAdtsPesStu->pes_extension_flag = 0b0; ptAdtsPesStu->pes_data_length = 5; // pts_dts_flags = '10': audio(aac) only PTS ptAdtsPesStu->ts_pts_dts.reserved_1 = 0b1111; if(pts > 0x7FFFFFFF) { ptAdtsPesStu->ts_pts_dts.pts_32_30 = (pts >> 30) & 0x07; ptAdtsPesStu->ts_pts_dts.marker_bit1 = 0b1; } else { ptAdtsPesStu->ts_pts_dts.pts_32_30 = 0; ptAdtsPesStu->ts_pts_dts.marker_bit1 = 0b0; } if(pts > 0x7FFF) { ptAdtsPesStu->ts_pts_dts.pts_29_15 = (pts >> 15) & 0x007FFF; ptAdtsPesStu->ts_pts_dts.marker_bit2 = 0b1; } else { ptAdtsPesStu->ts_pts_dts.pts_29_15 = 0; ptAdtsPesStu->ts_pts_dts.marker_bit2 = 0b0; } ptAdtsPesStu->ts_pts_dts.pts_14_0 = pts & 0x007FFF; ptAdtsPesStu->ts_pts_dts.marker_bit3 = 0b1; /* es data */ ptAdtsPesStu->av_data = adtsData; ptAdtsPesStu->av_data_len = adtsDataLen; return 0; } int fixH264FrameToPesStruct(uint8_t *h264Data, uint32_t h264DataLen, uint64_t pts, T_PesEsStruct* ptH264PesStu) { if(!ptH264PesStu || !h264Data || !h264DataLen) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* pes header */ ptH264PesStu->packet_start_code_prefix = 0x000001; ptH264PesStu->stream_id = TS_STREAM_ID_VIDEO; ptH264PesStu->pes_packet_length = 0; ptH264PesStu->marker_bit = 0b10; ptH264PesStu->pes_scrambling_control = 0b00; ptH264PesStu->pes_priority = 0b0; ptH264PesStu->data_alignment_indicator = 0b0; ptH264PesStu->copyright = 0b0; ptH264PesStu->original_or_copy = 0b0; ptH264PesStu->pts_dts_flags = 0b10; // '10': only PTS ptH264PesStu->escr_flag = 0b0; ptH264PesStu->es_rate_flag = 0b0; ptH264PesStu->dsm_trick_mode_flag = 0b0; ptH264PesStu->additional_copy_info_flag = 0b0; ptH264PesStu->pes_crc_flag = 0b0; ptH264PesStu->pes_extension_flag = 0b0; ptH264PesStu->pes_data_length = 5; // pts_dts_flags = '10' ptH264PesStu->ts_pts_dts.reserved_1 = 0x03; if(pts > 0x7FFFFFFF) { ptH264PesStu->ts_pts_dts.pts_32_30 = (pts >> 30) & 0x07; ptH264PesStu->ts_pts_dts.marker_bit1 = 0b1; } else { ptH264PesStu->ts_pts_dts.pts_32_30 = 0; ptH264PesStu->ts_pts_dts.marker_bit1 = 0b0; } if(pts > 0x7FFF) { ptH264PesStu->ts_pts_dts.pts_29_15 = (pts >> 15) & 0x007FFF ; ptH264PesStu->ts_pts_dts.marker_bit2 = 0b1; } else { ptH264PesStu->ts_pts_dts.pts_29_15 = 0; ptH264PesStu->ts_pts_dts.marker_bit2 = 0b0; } ptH264PesStu->ts_pts_dts.pts_14_0 = pts & 0x007FFF; ptH264PesStu->ts_pts_dts.marker_bit3 = 0b1; /* es data */ ptH264PesStu->av_data = h264Data; ptH264PesStu->av_data_len = h264DataLen; return 0; } static int fixFirstPacketAdaptionField(T_AdaptationField *adaptationField, uint8_t adaptFieldLen, uint8_t *adaptBuf) { uint8_t adaptiveFlags = 0x00; uint8_t adaptivePos = 1; // offset if(!adaptationField || !adaptBuf) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } if(adaptationField->discontinuty_indicator) { adaptiveFlags |= 0x80; } if(adaptationField->random_access_indicator) { adaptiveFlags |= 0x40; } if(adaptationField->elementary_stream_priority_indicator) { adaptiveFlags |= 0x20; } if(adaptationField->pcr_flag) { adaptiveFlags |= 0x10; uint64_t pcr_base = adaptationField->pcr / 300; uint64_t pcr_ext = adaptationField->pcr % 300; adaptBuf[adaptivePos + 0] = (pcr_base >> 25) & 0xff; adaptBuf[adaptivePos + 1] = (pcr_base >> 17) & 0xff; adaptBuf[adaptivePos + 2] = (pcr_base >> 9) & 0xff; adaptBuf[adaptivePos + 3] = (pcr_base >> 1) & 0xff; adaptBuf[adaptivePos + 4] = pcr_base > 8 | 0x7e; adaptBuf[adaptivePos + 5] = (pcr_ext) & 0xff; adaptivePos += 6; } if(adaptationField->opcr_flag) { adaptiveFlags |= 0x08; uint64_t opcr_base = adaptationField->opcr / 300; uint64_t opcr_ext = adaptationField->opcr % 300; adaptBuf[adaptivePos + 0] = (opcr_base >> 25) & 0xff; adaptBuf[adaptivePos + 1] = (opcr_base >> 17) & 0xff; adaptBuf[adaptivePos + 2] = (opcr_base >> 9) & 0xff; adaptBuf[adaptivePos + 3] = (opcr_base >> 1) & 0xff; adaptBuf[adaptivePos + 4] = ((opcr_base > 8) & 0x01); adaptBuf[adaptivePos + 5] = (opcr_ext) & 0xff; adaptivePos += 6; } if(adaptationField->splicing_point_flag) { adaptiveFlags |= 0x04; adaptBuf[adaptivePos] = adaptationField->splice_countdown; adaptivePos += 1; } if(adaptationField->transport_private_data_flag) { adaptiveFlags |= 0x02; if(adaptationField->transport_private_data_len + 1 > adaptFieldLen - adaptivePos) return -2; else { adaptBuf[adaptivePos] = adaptationField->transport_private_data_len; memcpy(&adaptBuf[adaptivePos + 1], adaptationField->transport_private_data, adaptBuf[adaptivePos]); adaptivePos += (1 + adaptBuf[adaptivePos]); } } if(adaptationField->adaptation_field_extension_flag) { adaptiveFlags |= 0x01; adaptBuf[adaptivePos] = 1; adaptBuf[adaptivePos + 1] = 0; } adaptBuf[0] = adaptiveFlags; return 0; } static int generateTsHeaderData(uint16_t pid, uint8_t payloadStartInd, uint8_t adaptFieldControl, uint8_t *outTsHeaderData) { T_TsHeader tsHeader = {0}; static uint8_t continuity_counter_pat = 0; /* 0~f */ static uint8_t continuity_counter_pmt = 0; /* 0~f */ static uint8_t continuity_counter_audio = 0; /* 0~f */ static uint8_t continuity_counter_video = 0; /* 0~f */ if(!outTsHeaderData) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* fix */ tsHeader.sync_byte = TS_HEADER_SYNC_BYTE; tsHeader.transport_error_indicator = 0; tsHeader.payload_unit_start_indicator = payloadStartInd; tsHeader.transport_priority = 0; tsHeader.pid = pid; tsHeader.transport_scrambling_control = 0; tsHeader.adaptation_field_control = adaptFieldControl; switch(pid) { case TS_PID_PAT: tsHeader.continuity_counter = continuity_counter_pat++ % 0x10; break; case TS_PID_PMT: tsHeader.continuity_counter = continuity_counter_pmt++ % 0x10; break; case TS_PID_AAC: tsHeader.continuity_counter = continuity_counter_audio++ % 0x10; break; case TS_PID_H264: tsHeader.continuity_counter = continuity_counter_video++ % 0x10; break; default: return -2; } /* save */ outTsHeaderData[0] = tsHeader.sync_byte; outTsHeaderData[1] = tsHeader.transport_error_indicator T_Pat pat = {}; uint8_t tsBuf[TS_PACKET_SIZE] = {0}; uint32_t crc_32 = 0xFFFFFFFF; if(!fpTsFile) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* 1. fix params */ pat.table_id = 0x00; pat.section_syntax_indicator = 0b1; pat.zero = 0b0; pat.reserved_1 = 0b11; pat.section_length = 13; // 16 Bytes(pat total size) - 3 Bytes(sizeof table_id~section_length) pat.transport_stream_id = 0x0001; pat.reserved_2 = 0b11; pat.version_number = 0b00000; pat.current_next_indicator = 0b1; // available pat.section_number = 0x00; pat.last_section_number = 0x00; pat.program_number = 0x0001; // PMT pat.reserved_3 = 0b111; pat.pid = TS_PID_PMT; // program_number: 0x0001 - pmt's pid //pat.crc_32 = crc_32; // we'll calculate when write to buf. /* 2. fix ts data */ /* 2.1 fix ts data: ts header */ generateTsHeaderData(TS_PID_PAT, 0b1, 0b01, tsBuf); // 0b1: start, 0b01: playload. /* 2.2 fix ts data: adaptation field length */ tsBuf[4] = 0; /* no adaption field */ /* 2.3 fix ts data: pat data */ tsBuf[5] = pat.table_id; tsBuf[6] = pat.section_syntax_indicator 8) & 0x0F); tsBuf[16]= pat.pid & 0x00FF; crc_32 = endian_convert(crc32_mpeg_2(&tsBuf[5], 16 - 5 + 1)); memcpy(&tsBuf[17], (unsigned char *)&crc_32, 4); /* 2.4 fix ts data: clear with "0xff" */ memset(&tsBuf[21], 0xff, TS_PACKET_SIZE - 21); /* 3. write to file */ fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); return 0; } static int packPmt2TsAndWriteToFile(FILE *fpTsFile) { T_Pmt pmt; uint8_t tsBuf[TS_PACKET_SIZE] = {0}; uint32_t crc_32 = 0xFFFFFFFF; if(!fpTsFile) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* 1. fix params */ pmt.table_id = 0x02; pmt.section_syntax_indicator = 0b1; pmt.zero = 0b0; pmt.reserved_1 = 0b11; pmt.section_length = 23; // 16 + 5 + 5 - 3 pmt.program_number = 1; // only one program pmt.reserved_2 = 0b11; pmt.version_number = 0b0000; pmt.current_next_indicator = 0b1; // available pmt.section_number = 0x00; pmt.last_section_number = 0x00; pmt.reserved_3 = 0b111; pmt.pcr_pid = TS_PID_H264; pmt.reserved_4 = 0b1111; pmt.program_info_length = 0x000; pmt.stream_type_video = PMT_STREAM_TYPE_H264; // video type pmt.reserved_5_video = 0b111; pmt.elementary_pid_video = TS_PID_H264; // video pid pmt.reserved_6_video= 0b1111; pmt.es_info_length_video = 0x000; pmt.stream_type_audio = PMT_STREAM_TYPE_AAC; // audio type pmt.reserved_5_audio = 0b111; pmt.elementary_pid_audio = TS_PID_AAC; // audio pid pmt.reserved_6_audio = 0b1111; pmt.es_info_length_audio = 0x000; //pmt.crc_32 = crc_32; // we'll calculate when write to buf. /* 2. fix ts data */ /* 2.1 fix ts data: ts header */ generateTsHeaderData(TS_PID_PMT, 0b1, 0b01, tsBuf); // 0b1: start, 0b01: playload. /* 2.2 fix ts data: adaptation field length */ tsBuf[4] = 0; /* no adaption field */ /* 2.3 fix ts data: pmt data */ tsBuf[5] = pmt.table_id; tsBuf[6] = pmt.section_syntax_indicator > 8; tsBuf[9] = pmt.program_number & 0x00FF; tsBuf[10] = pmt.reserved_2 8) & 0x0F); tsBuf[16]= pmt.program_info_length & 0xFF; tsBuf[17]= pmt.stream_type_video; tsBuf[18]= pmt.reserved_5_video > 8 ) & 0x1F); tsBuf[19]= pmt.elementary_pid_video & 0x00FF; tsBuf[20]= pmt.reserved_6_video> 8) & 0x0F); tsBuf[21]= pmt.es_info_length_video & 0x0FF; tsBuf[22]= pmt.stream_type_audio; tsBuf[23]= pmt.reserved_5_audio> 8 ) & 0x1F); tsBuf[24]= pmt.elementary_pid_audio & 0x00FF; tsBuf[25]= pmt.reserved_6_audio > 8) & 0x0F); tsBuf[26]= pmt.es_info_length_audio & 0x0FF; crc_32 = endian_convert(crc32_mpeg_2(&tsBuf[5], 26 - 5 + 1)); memcpy(&tsBuf[27], (unsigned char *)&crc_32, 4); /* 2.4 fix ts data: clear with "0xff" */ memset(&tsBuf[31], 0xff, TS_PACKET_SIZE - 31); /* 3. write to file */ fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); return 0; } static int packPes2TsAndWriteToFile(T_PesEsStruct* ptPesStu, uint16_t pid, T_AdaptationField *adaptionField, uint64_t pts, FILE *fpTsFile) { uint8_t tsBuf[TS_PACKET_SIZE] = {0}; uint8_t tsBufPos = 0; uint8_t stuffingLen = 0; uint32_t firstPacketLoadLen = 0; if (!ptPesStu || !adaptionField || !fpTsFile) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* calcurate the first packet playload length */ firstPacketLoadLen = TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - adaptionField->adaptation_field_length - 1 /* sizeof adaptation_field_length */ - 9 /* pes header(not contain pts/dts) */ - 5; /* pts */ if(ptPesStu->av_data_len av_data_len - 1 /* sizeof adaptation_field_length */ - 9 /* pes header(not contain pts/dts) */ - 5; /* pts */ /* 2.2 adaptation field: discontinuty_indicator ~ adaptation_field_extension_flag. (0x00: no optional fields) */ tsBuf[tsBufPos + 1] = 0x00; tsBufPos += 2; /* 2.3 adaptation field: stuffing bytes */ stuffingLen = TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - ptPesStu->av_data_len - 2 /* adaptation_field_length ~ adaptation_field_extension_flag */ - 9 /* pes header(not contain pts/dts) */ - 5; /* pts */ memset(tsBuf + tsBufPos, 0xff, stuffingLen); tsBufPos += stuffingLen; /* 3. payload */ /* 3.1 payload: pes header. (not contain pts/dts) */ tsBuf[tsBufPos + 0] = (ptPesStu->packet_start_code_prefix >> 16) & 0xff; tsBuf[tsBufPos + 1] = (ptPesStu->packet_start_code_prefix >> 8) & 0xff; tsBuf[tsBufPos + 2] = ptPesStu->packet_start_code_prefix & 0xff; tsBuf[tsBufPos + 3] = ptPesStu->stream_id; tsBuf[tsBufPos + 4] = (ptPesStu->pes_packet_length >> 8) & 0xff; tsBuf[tsBufPos + 5] = ptPesStu->pes_packet_length & 0xff; tsBuf[tsBufPos + 6] = ptPesStu->marker_bit pes_scrambling_control pes_priority data_alignment_indicator copyright original_or_copy; tsBuf[tsBufPos + 7] = ptPesStu->pts_dts_flags escr_flag es_rate_flag dsm_trick_mode_flag additional_copy_info_flag pes_crc_flag pes_extension_flag; tsBuf[tsBufPos + 8] = ptPesStu->pes_data_length; tsBufPos += 9; /* 3.2 payload: pts */ tsBuf[tsBufPos + 0] = (((0x3 > 29) & 0x0E) | 0x01) & 0xff); tsBuf[tsBufPos + 1] = (((((pts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff); tsBuf[tsBufPos + 2] = ((((pts >> 14) & 0xfffe) | 0x01) & 0xff); tsBuf[tsBufPos + 3] = (((((pts > 8) & 0xff); tsBuf[tsBufPos + 4] = ((((pts av_data, ptPesStu->av_data_len); /* 4. write to file */ fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); return 0; } else { /* 1. ts header */ generateTsHeaderData(pid, 0b1, 0b11, tsBuf); // 0b1: start, 0b11: adaption_field + playload. tsBufPos += TS_PACKET_HEADER_SIZE; /* 2. adaptation field */ /* 2.1 adaptation field: adaptation field length */ tsBuf[tsBufPos] = adaptionField->adaptation_field_length; tsBufPos += 1; /* 2.2 adaptation field: discontinuty_indicator ~ adaptation_field_extension_flag. * whether the pcr_flag is equal to 1 in this demo, * and "adaptionField->adaptation_field_length = 1 or 7" */ fixFirstPacketAdaptionField(adaptionField, (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 1 - 9 - 5), &tsBuf[tsBufPos]); tsBufPos += adaptionField->adaptation_field_length; /* 3. payload*/ /* 3.1 payload: pes header. (not contain pts/dts) */ tsBuf[tsBufPos + 0] = (ptPesStu->packet_start_code_prefix >> 16) & 0xFF; tsBuf[tsBufPos + 1] = (ptPesStu->packet_start_code_prefix >> 8) & 0xFF; tsBuf[tsBufPos + 2] = ptPesStu->packet_start_code_prefix & 0xFF; tsBuf[tsBufPos + 3] = ptPesStu->stream_id; tsBuf[tsBufPos + 4] = ((ptPesStu->pes_packet_length) >> 8) & 0xFF; tsBuf[tsBufPos + 5] = (ptPesStu->pes_packet_length) & 0xFF; tsBuf[tsBufPos + 6] = ptPesStu->marker_bit pes_scrambling_control pes_priority data_alignment_indicator copyright original_or_copy; tsBuf[tsBufPos + 7] = ptPesStu->pts_dts_flags escr_flag es_rate_flag dsm_trick_mode_flag additional_copy_info_flag pes_crc_flag pes_extension_flag; tsBuf[tsBufPos + 8] = ptPesStu->pes_data_length; tsBufPos += 9; /* 3.2 payload: pts */ tsBuf[tsBufPos + 0] = (((0x3 > 29) & 0x0E) | 0x01) & 0xff); tsBuf[tsBufPos + 1] = (((((pts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff); tsBuf[tsBufPos + 2] = ((((pts >> 14) & 0xfffe) | 0x01) & 0xff); tsBuf[tsBufPos + 3] = (((((pts > 8) & 0xff); tsBuf[tsBufPos + 4] = ((((pts av_data_cur_ptr = ptPesStu->av_data; memcpy(tsBuf + tsBufPos, ptPesStu->av_data_cur_ptr, firstPacketLoadLen); /* 4. write to file */ fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); /* >> change the pointer to pack remain es(Elementary Stream) data! av_data_cur_ptr += firstPacketLoadLen; ptPesStu->av_data_len -= firstPacketLoadLen; /* >> pack remain es(Elementary Stream) data! av_data_len) { tsBufPos = 0; memset(tsBuf, 0, TS_PACKET_SIZE); if(ptPesStu->av_data_len >= (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE)) /* 184 */ { generateTsHeaderData(pid, 0b0, 0b01, tsBuf); // 0b0: It isn't the start, 0b01: only playload. tsBufPos += TS_PACKET_HEADER_SIZE; memcpy(tsBuf + tsBufPos, ptPesStu->av_data_cur_ptr, (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE)); fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); /* >> change the pointer to pack remain es(Elementary Stream) data! av_data_cur_ptr += (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE); ptPesStu->av_data_len -= (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE); } else if(ptPesStu->av_data_len == (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 1) ||\ ptPesStu->av_data_len == (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 2)) /* 183 or 182 */ { generateTsHeaderData(pid, 0b0, 0b11, tsBuf); // 0b0: It isn't the start, 0b11: adaption_field + playload. tsBufPos += TS_PACKET_HEADER_SIZE; tsBuf[tsBufPos + 0] = 0x01; tsBuf[tsBufPos + 1] = 0x00; tsBufPos += 2; memcpy(&tsBuf[tsBufPos], ptPesStu->av_data_cur_ptr, (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 2)); fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); /* >> change the pointer to pack remain es(Elementary Stream) data! av_data_cur_ptr += (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 2); ptPesStu->av_data_len -= (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - 2); } else { generateTsHeaderData(pid, 0b0, 0b11, tsBuf); // 0b0: It isn't the start, 0b11: adaption_field + playload. tsBufPos += TS_PACKET_HEADER_SIZE; tsBuf[tsBufPos + 0] = TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - ptPesStu->av_data_len - 1; tsBuf[tsBufPos + 1] = 0x00; tsBufPos += 2; memset(&tsBuf[tsBufPos], 0xff, (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - ptPesStu->av_data_len - 2)); tsBufPos += (TS_PACKET_SIZE - TS_PACKET_HEADER_SIZE - ptPesStu->av_data_len - 2); memcpy(&tsBuf[tsBufPos], ptPesStu->av_data_cur_ptr, ptPesStu->av_data_len); fwrite(tsBuf, 1, TS_PACKET_SIZE, fpTsFile); /* >> es(Elementary Stream) data empty! av_data_len = 0; } } } return 0; } int ts_mux_h264_aac(char *h264FileName, uint32_t vFps, char *aacFileName, char *tsFileName) { int ret = -1; uint64_t timeStamp_us_a = 0; uint64_t timeStamp_us_v = 0; uint32_t videoFps = vFps; uint32_t audioSampleRate = -1; FILE *fpH264 = NULL; FILE *fpAAC = NULL; FILE *fpTs = NULL; uint8_t *h264Buf = NULL; uint8_t *aacBuf = NULL; T_AdtsHeader adtsHeader = {}; T_NaluInfo naluInfo = {}; T_PesEsStruct pesStu = {}; T_AdaptationField adaptationField = {}; if (!h264FileName || !videoFps || !aacFileName || !tsFileName) { printf("[%s:%d] Params invalid!\n", __FUNCTION__, __LINE__); return -1; } /* open file */ fpH264 = fopen(h264FileName, "rb"); if (!fpH264) { DEBUG("[%s:%d] open %s error!\n", __FUNCTION__, __LINE__, h264FileName); return -2; } fpAAC = fopen(aacFileName, "rb"); if (!fpAAC) { DEBUG("[%s:%d] open %s error!\n", __FUNCTION__, __LINE__, aacFileName); return -3; } fpTs = fopen(tsFileName, "wb"); if (!fpTs) { DEBUG("[%s:%d] open %s error!\n", __FUNCTION__, __LINE__, tsFileName); return -4; } /* alloc tmp memory */ h264Buf = (uint8_t *)malloc(MAX_NALU_SIZE); if (!h264Buf) { DEBUG("[%s:%d] malloc error!\n", __FUNCTION__, __LINE__); return -5; } aacBuf = (uint8_t *)malloc(MAX_ADTS_SIZE); if (!aacBuf) { DEBUG("[%s:%d] malloc error!\n", __FUNCTION__, __LINE__); return -6; } /* parse AAC-ADTS */ ret = getAdtsFrame(fpAAC, aacBuf, &adtsHeader); if(!ret) { fseek(fpAAC, 0, SEEK_SET); // reset switch(adtsHeader.sampling_freq_index) { case SFI_96000: audioSampleRate = 96000; break; case SFI_88200: audioSampleRate = 88200; break; case SFI_64000: audioSampleRate = 64000; break; case SFI_48000: audioSampleRate = 48000; break; case SFI_44100: audioSampleRate = 44100; break; case SFI_32000: audioSampleRate = 32000; break; case SFI_24000: audioSampleRate = 24000; break; case SFI_22050: audioSampleRate = 22050; break; case SFI_16000: audioSampleRate = 16000; break; case SFI_12000: audioSampleRate = 12000; break; case SFI_11025: audioSampleRate = 11025; break; case SFI_8000: audioSampleRate = 8000; break; case SFI_7350: audioSampleRate = 7350; break; default: audioSampleRate = 0; break; } DEBUG("AAC Info:\n" "\t id: %d\n" "\t profile: %d\n" "\t freq index: %d\n" "\t sample rate: %d)\n" "\t channels: %d\n", adtsHeader.id, adtsHeader.profile, adtsHeader.sampling_freq_index, audioSampleRate, adtsHeader.channel_configuration); } #if 1 /* The PAT and PMT can show anywhere. */ ret = packPat2TsAndWriteToFile(fpTs); if(ret timeStamp_us_v) { ret = getOneH264Nalu(fpH264, h264Buf, &naluInfo); if (retmain.c
#include #include "ts.h" int main(int argc, char *argv[]) { if(argc == 1) { printf("Usage: \n" " %s avfile/test1_856x480_24fps.h264 24 avfile/test1_44100_stereo.aac out1.ts\n" " %s avfile/test2_720x480_30fps.h264 60 avfile/test2_48000_stereo.aac out2.ts\n" " %s avfile/test3_1280x720_20fps.h264 20 avfile/test1_44100_stereo.aac out3.ts\n", argv[0], argv[0], argv[0]); return -1; } if(0 == ts_mux_h264_aac(argv[1], atoi(argv[2]), argv[3], argv[4])) printf("\033[32mMux ts file success!\033[0m \n"); else printf("\033[31mMux ts file failed!\033[0m \n"); return 0; }3、demo下载地址(任选一个)
- https://download.csdn.net/download/weixin_44498318/89526734
- https://gitee.com/linriming/av_ts_mux_with_h264_aac.git
- https://github.com/linriming20/av_ts_mux_with_h264_aac.git
- SpecialFTS.exe(demo中的tools/SpecialFTS_1.1.7z)【源码下载地址】
-
- 电影和电视、ACG播放器
- 电影和电视、ACG播放器
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!
