📄 main.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) ENST 2000-200X * All rights reserved * * This file is part of GPAC / mp42ts application * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/media_tools.h>#include <gpac/constants.h>#include <gpac/ietf.h>#include "mp42ts.h"void usage() { fprintf(stderr, "usage: mp42ts [options] dst\n" "With options being: \n" "-prog=FILE specifies an input file used for a TS service\n" " * currently only supports ISO files and SDP files\n" " * option can be used several times, once for each program\n" "-rate=R specifies target rate in kbits/sec of the multiplex\n" " If not set, transport stream will be of variable bitrate\n" "\n" "dst can be a file, an RTP or a UDP destination (unicast/multicast)\n" );}static GFINLINE void m2ts_dump_time(M2TS_Time *time, char *name){ fprintf(stdout, "%s: %d%03d\n", name, time->sec, time->nanosec/1000000);}static GFINLINE Bool m2ts_time_less(M2TS_Time *a, M2TS_Time *b) { if (a->sec>b->sec) return 0; if (a->sec==b->sec) return (a->nanosec<b->nanosec) ? 1 : 0; return 1;}static GFINLINE Bool m2ts_time_less_or_equal(M2TS_Time *a, M2TS_Time *b) { if (a->sec>b->sec) return 0; if (a->sec==b->sec) return (a->nanosec>b->nanosec) ? 0 : 1; return 1;}static GFINLINE void m2ts_time_inc(M2TS_Time *time, u32 delta_inc_num, u32 delta_inc_den){ u64 n_sec; u32 sec; /*couldn't compute bitrate - we need to have more info*/ if (!delta_inc_den) return; sec = delta_inc_num / delta_inc_den; if (sec) { time->sec += sec; sec *= delta_inc_den; delta_inc_num = delta_inc_num % sec; } /*move to nanosec - 0x3B9ACA00 = 1000*1000*1000 */ n_sec = delta_inc_num; n_sec *= 0x3B9ACA00; n_sec /= delta_inc_den; time->nanosec += (u32) n_sec; while (time->nanosec >= 0x3B9ACA00) { time->nanosec -= 0x3B9ACA00; time->sec ++; }}/************************************ * Section-related functions ************************************/void m2ts_mux_table_update(M2TS_Mux_Stream *stream, u8 table_id, u16 table_id_extension, u8 *table_payload, u32 table_payload_length, Bool use_syntax_indicator, Bool private_indicator, Bool use_checksum) { u32 overhead_size; u32 offset; u32 section_number, nb_sections; M2TS_Mux_Table *table, *prev_table; u32 maxSectionLength; M2TS_Mux_Section *section, *prev_sec; GF_BitStream *bs; /* check if there is already a table with that id */ prev_table = NULL; table = stream->tables; while (table) { if (table->table_id == table_id) { /* if yes, we need to flush the table and increase the version number */ M2TS_Mux_Section *sec = table->section; while (sec) { M2TS_Mux_Section *sec2 = sec->next; free(sec->data); free(sec); sec = sec2; } table->version_number = (table->version_number + 1)%0x1F; break; } prev_table = table; table = table->next; } if (!table) { /* if no, the table is created */ GF_SAFEALLOC(table, M2TS_Mux_Table); table->table_id = table_id; if (prev_table) prev_table->next = table; else stream->tables = table; } if (!table_payload_length) return; switch (table_id) { case GF_M2TS_TABLE_ID_PMT: case GF_M2TS_TABLE_ID_PAT: case GF_M2TS_TABLE_ID_SDT_ACTUAL: case GF_M2TS_TABLE_ID_SDT_OTHER: case GF_M2TS_TABLE_ID_BAT: maxSectionLength = 1024; break; case GF_M2TS_TABLE_ID_MPEG4_BIFS: case GF_M2TS_TABLE_ID_MPEG4_OD: maxSectionLength = 4096; break; default: fprintf(stderr, "Cannot create sections for table with id %d\n", table_id); return; } overhead_size = SECTION_HEADER_LENGTH; if (use_syntax_indicator) overhead_size += SECTION_ADDITIONAL_HEADER_LENGTH + CRC_LENGTH; section_number = 0; nb_sections = 1; while (nb_sections*(maxSectionLength - overhead_size)<table_payload_length) nb_sections++; switch (table_id) { case GF_M2TS_TABLE_ID_PMT: if (nb_sections > 1) fprintf(stdout,"Warning: last section number for PMT shall be 0 !!\n"); break; default: break; } prev_sec = NULL; offset = 0; while (offset < table_payload_length) { u32 remain; GF_SAFEALLOC(section, M2TS_Mux_Section); remain = table_payload_length - offset; if (remain > maxSectionLength - overhead_size) { section->length = maxSectionLength; } else { section->length = remain + overhead_size; } bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); /* first header (not included in section length */ gf_bs_write_int(bs, table_id, 8); gf_bs_write_int(bs, use_syntax_indicator, 1); gf_bs_write_int(bs, private_indicator, 1); gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12); if (use_syntax_indicator) { /* second header */ gf_bs_write_int(bs, table_id_extension, 16); gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ gf_bs_write_int(bs, table->version_number, 5); gf_bs_write_int(bs, 1, 1); /* current_next_indicator = 1: we don't send version in advance */ gf_bs_write_int(bs, section_number, 8); section_number++; gf_bs_write_int(bs, nb_sections-1, 8); } gf_bs_write_data(bs, table_payload + offset, section->length - overhead_size); offset += section->length - overhead_size; if (use_syntax_indicator) { /* place holder for CRC */ gf_bs_write_u32(bs, 0); } gf_bs_get_content(bs, (char**) §ion->data, §ion->length); gf_bs_del(bs); if (use_syntax_indicator) { u32 CRC; CRC = gf_m2ts_crc32(section->data,section->length-CRC_LENGTH); section->data[section->length-4] = (CRC >> 24) & 0xFF; section->data[section->length-3] = (CRC >> 16) & 0xFF; section->data[section->length-2] = (CRC >> 8) & 0xFF; section->data[section->length-1] = CRC & 0xFF; } if (prev_sec) prev_sec->next = section; else table->section = section; prev_sec = section; } stream->current_table = stream->tables; stream->current_section = stream->current_table->section; stream->current_section_offset = 0;}void m2ts_mux_table_update_bitrate(M2TS_Mux *mux, M2TS_Mux_Stream *stream){ M2TS_Mux_Table *table; /*update PMT*/ if (stream->table_needs_update) stream->process(mux, stream); stream->bit_rate = 0; table = stream->tables; while (table) { M2TS_Mux_Section *section = table->section; while (section) { stream->bit_rate += section->length; section = section->next; } table = table->next; } stream->bit_rate *= 8; if (!stream->refresh_rate_ms) stream->refresh_rate_ms = 500; stream->bit_rate *= 1000; stream->bit_rate /= stream->refresh_rate_ms;}/* length of adaptation_field_length; */ #define ADAPTATION_LENGTH_LENGTH 1/* discontinuty flag, random access flag ... */#define ADAPTATION_FLAGS_LENGTH 1 /* length of encoded pcr */#define PCR_LENGTH 6static u32 m2ts_add_adaptation(GF_BitStream *bs, Bool has_pcr, u64 time, Bool is_rap, u32 padding_length){ u32 adaptation_length; adaptation_length = ADAPTATION_FLAGS_LENGTH + (has_pcr?PCR_LENGTH:0) + padding_length; gf_bs_write_int(bs, adaptation_length, 8); gf_bs_write_int(bs, 0, 1); // discontinuity indicator gf_bs_write_int(bs, is_rap, 1); // random access indicator gf_bs_write_int(bs, 0, 1); // es priority indicator gf_bs_write_int(bs, has_pcr, 1); // PCR_flag gf_bs_write_int(bs, 0, 1); // OPCR flag gf_bs_write_int(bs, 0, 1); // splicing point flag gf_bs_write_int(bs, 0, 1); // transport private data flag gf_bs_write_int(bs, 0, 1); // adaptation field extension flag if (has_pcr) { u64 PCR_base, PCR_ext; PCR_base = time; gf_bs_write_long_int(bs, PCR_base, 33); gf_bs_write_int(bs, 0, 6); // reserved PCR_ext = 0; gf_bs_write_long_int(bs, PCR_ext, 9); } while (padding_length) { gf_bs_write_u8(bs, 0xff); // stuffing byte padding_length--; } return adaptation_length + ADAPTATION_LENGTH_LENGTH;}void m2ts_mux_table_get_next_packet(M2TS_Mux_Stream *stream, u8 *packet){ GF_BitStream *bs; M2TS_Mux_Table *table; M2TS_Mux_Section *section; u32 payload_length, padding_length; u8 adaptation_field_control; table = stream->current_table; assert(table); section = stream->current_section; assert(section); bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE); gf_bs_write_int(bs, 0x47, 8); // sync gf_bs_write_int(bs, 0, 1); // error indicator if (stream->current_section_offset == 0) { gf_bs_write_int(bs, 1, 1); // payload start indicator } else { /* No section concatenation yet!!!*/ gf_bs_write_int(bs, 0, 1); // payload start indicator } if (!stream->current_section_offset) payload_length = 183; else payload_length = 184; if (section->length - stream->current_section_offset >= payload_length) { padding_length = 0; adaptation_field_control = M2TS_ADAPTATION_NONE; } else { /* in all the following cases, we write an adaptation field */ adaptation_field_control = M2TS_ADAPTATION_AND_PAYLOAD; /* we need at least 2 bytes for adaptation field headers (no pcr) */ payload_length -= 2; if (section->length - stream->current_section_offset >= payload_length) { padding_length = 0; } else { padding_length = payload_length - section->length - stream->current_section_offset; payload_length -= padding_length; } } assert(payload_length + stream->current_section_offset <= section->length); gf_bs_write_int(bs, 0, 1); // priority indicator gf_bs_write_int(bs, stream->pid, 13); // section pid gf_bs_write_int(bs, 0, 2); // scrambling indicator gf_bs_write_int(bs, adaptation_field_control, 2); // we do not use adaptation field for sections gf_bs_write_int(bs, stream->continuity_counter, 4); // continuity counter if (stream->continuity_counter < 15) stream->continuity_counter++; else stream->continuity_counter=0; if (adaptation_field_control != M2TS_ADAPTATION_NONE) m2ts_add_adaptation(bs, 0, 0, 0, padding_length); if (!stream->current_section_offset) { //write pointer field gf_bs_write_u8(bs, 0); /* no concatenations of sections in ts packets, so start address is 0 */ } gf_bs_del(bs); memcpy(packet+188-payload_length, section->data + stream->current_section_offset, payload_length); stream->current_section_offset += payload_length; //m2ts_time_inc(stream->time, 1504/*188*8*/, muxer->bit_rate); if (stream->current_section_offset == section->length) { stream->current_section_offset = 0; stream->current_section = stream->current_section->next; if (!stream->current_section) { stream->current_table = stream->current_table->next; if (!stream->current_table) { stream->current_table = stream->tables; /*update time*/ m2ts_time_inc(&stream->time, stream->refresh_rate_ms, 1000); } stream->current_section = stream->current_table->section; } }}Bool m2ts_stream_process_pat(M2TS_Mux *muxer, M2TS_Mux_Stream *stream){ if (stream->table_needs_update) { /* generate table payload */ M2TS_Mux_Program *prog; GF_BitStream *bs; u8 *payload; u32 size; bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); prog = muxer->programs; while (prog) { gf_bs_write_u16(bs, prog->number); gf_bs_write_int(bs, 0x7, 3); /*reserved*/ gf_bs_write_int(bs, prog->pmt->pid, 13); /*reserved*/ prog = prog->next; } gf_bs_get_content(bs, (char**)&payload, &size); gf_bs_del(bs); m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PAT, muxer->ts_id, payload, size, 1, 0, 0); stream->table_needs_update = 0; free(payload); } return 1;}Bool m2ts_stream_process_pmt(M2TS_Mux *muxer, M2TS_Mux_Stream *stream){ if (stream->table_needs_update) { /* generate table payload */ M2TS_Mux_Stream *es; u8 *payload; u32 length; GF_BitStream *bs; bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); gf_bs_write_int(bs, 0x7, 3); // reserved gf_bs_write_int(bs, stream->program->pcr->pid, 13); gf_bs_write_int(bs, 0xF, 4); // reserved if (1 /* !iod */) { gf_bs_write_int(bs, 0, 12); // program info length =0 } else {#if 0 u32 len; len = iod->length + 4; gf_bs_write_int(bs, len, 12); // program info length = iod len /*for (i =0; i< gf_list_count(program->desc); i++) { // program descriptors // IOD }*/ gf_bs_write_int(bs, 29, 8); // tag len = iod->length + 2; gf_bs_write_int(bs, len, 8); // length gf_bs_write_int(bs, 2, 8); // Scope_of_IOD_label : 0x10 iod unique a l'int閞ieur de programme // 0x11 iod unoque dans la flux ts gf_bs_write_int(bs, 2, 8); // IOD_label gf_bs_write_data(bs, iod->data, iod->length); // IOD#endif } es = stream->program->streams; while (es) { gf_bs_write_int(bs, es->mpeg2_stream_type, 8); gf_bs_write_int(bs, 0x7, 3); // reserved gf_bs_write_int(bs, es->pid, 13); gf_bs_write_int(bs, 0xF, 4); // reserved /* Second Loop Descriptor */ if (0 /*iod*/) {#if 0 gf_bs_write_int(bs, 4, 12); // ES info length = 4 :only SL Descriptor gf_bs_write_int(bs, 30, 8); // tag gf_bs_write_int(bs, 1, 8); // length gf_bs_write_int(bs, es->mpeg4_es_id, 16); // mpeg4_esid#endif } else { gf_bs_write_int(bs, 0, 12); } es = es->next; } gf_bs_get_content(bs, (char**)&payload, &length); gf_bs_del(bs); m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PMT, stream->program->number, payload, length, 1, 0, 0); stream->table_needs_update = 0; free(payload); } return 1;}Bool m2ts_stream_process_stream(M2TS_Mux *muxer, M2TS_Mux_Stream *stream){ if (stream->pck_offset == stream->pck.data_len) { if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) { if (stream->pck_offset) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_RELEASE, NULL); stream->pck_offset = 0; stream->pck.data_len = 0; /*EOF*/ if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) return 0; stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_PULL, &stream->pck); } else { M2TS_Packet *pck; /*flush input pipe*/ stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL); gf_mx_p(stream->mx); /*discard first packet*/ if (stream->pck_offset) { assert(stream->pck_first); pck = stream->pck_first; stream->pck_first = pck->next; free(pck->data); free(pck); } stream->pck_offset = 0; stream->pck.data_len = 0; /*fill pck*/ pck = stream->pck_first; if (!pck) { gf_mx_v(stream->mx); return 0; } stream->pck.cts = pck->cts; stream->pck.data = pck->data; stream->pck.data_len = pck->data_len; stream->pck.dts = pck->dts; stream->pck.flags = pck->flags; gf_mx_v(stream->mx); } /*!! watchout !!*/ if (stream->ts_scale) { stream->pck.cts = (u64) (stream->ts_scale * (s64) stream->pck.cts); stream->pck.dts = (u64) (stream->ts_scale * (s64) stream->pck.dts); } /*initializing the PCR*/ if (!stream->program->pcr_init) { if (stream==stream->program->pcr) { stream->program->pcr_init_ts_time = muxer->time; stream->program->pcr_init_time = stream->pck.dts; stream->program->pcr_init = 1; } else { /*don't send until PCR is initialized*/ return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -