/***************************************************************************** * dvb.c : DVB functions ***************************************************************************** * Copyright (C) 2010 Kieran Kunhya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. *****************************************************************************/ #include #include "../common.h" #include "dvb.h" /**** PMT Second Loop Descriptors ****/ void write_aac_descriptor( bs_t *s, ts_int_stream_t *stream ) { bs_write( s, 8, DVB_AAC_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 2 ); // descriptor_length bs_write( s, 8, stream->aac_profile ); // profile_and_level bs_write( s, 1, 0 ); // AAC_type_flag bs_write( s, 7, 0 ); // reserved } void write_adaptation_field_data_descriptor( bs_t *s, uint8_t identifier ) { bs_write( s, 8, DVB_ADAPTATION_FIELD_DATA_DESCRIPTOR ); // descriptor_tag bs_write( s, 8, 1 ); // descriptor_length bs_write( s, 8, identifier ); // adaptation_field_data_identifier } void write_dvb_subtitling_descriptor( bs_t *s, ts_int_stream_t *stream ) { ts_dvb_sub_t *subtitle; bs_write( s, 8, DVB_SUBTITLING_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, stream->num_dvb_sub * 8 ); // descriptor_length for( int i = 0; i < stream->num_dvb_sub; i++ ) { subtitle = &stream->dvb_sub_ctx[i]; for( int j = 0; j < 3; j++ ) bs_write( s, 8, subtitle->lang_code[j] ); // ISO_639_language_code bs_write( s, 8, subtitle->subtitling_type ); // subtitling_type bs_write( s, 16, subtitle->composition_page_id ); // composition_page_id bs_write( s, 16, subtitle->ancillary_page_id ); // ancillary_page_id } } void write_stream_identifier_descriptor( bs_t *s, uint8_t stream_identifier ) { bs_write( s, 8, DVB_STREAM_IDENTIFIER_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 1 ); // descriptor_length bs_write( s, 8, stream_identifier ); // component_tag } void write_teletext_descriptor( bs_t *s, ts_int_stream_t *stream, int vbi ) { ts_dvb_ttx_t *teletext; if( vbi ) bs_write( s, 8, DVB_VBI_TELETEXT_DESCRIPTOR_TAG ); // descriptor_tag else bs_write( s, 8, DVB_TELETEXT_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, stream->num_dvb_ttx * 5 ); // descriptor_length for( int i = 0; i < stream->num_dvb_ttx; i++ ) { teletext = &stream->dvb_ttx_ctx[i]; for( int j = 0; j < 3; j++ ) bs_write( s, 8, teletext->lang_code[j] ); // ISO_639_language_code bs_write( s, 5, teletext->teletext_type ); // teletext_type bs_write( s, 3, teletext->teletext_magazine_number == 8 ? 0 : teletext->teletext_magazine_number ); // teletext_magazine_number bs_write( s, 8, teletext->teletext_page_number ); // teletext_page_number } } void write_vbi_descriptor( bs_t *s, ts_int_stream_t *stream ) { bs_t q; uint8_t temp[1024]; ts_dvb_vbi_t *vbi; bs_init( &q, temp, 1024 ); bs_write( s, 8, DVB_VBI_DESCRIPTOR_TAG ); // descriptor_tag for( int i = 0; i < stream->num_dvb_vbi; i++ ) { vbi = &stream->dvb_vbi_ctx[i]; bs_write( &q, 8, vbi->data_service_id ); // data_service_id bs_write( &q, 8, vbi->num_lines ); // data_service_descriptor_length for( int j = 0; j < vbi->num_lines; j++ ) { bs_write( &q, 2, 0x3 ); // reserved bs_write( &q, 1, vbi->lines[j].field_parity ); // field_parity bs_write( &q, 5, vbi->lines[j].line_offset ); // line_offset } } bs_flush( &q ); bs_write( s, 8, bs_pos( &q ) >> 3 ); // descriptor_length write_bytes( s, temp, bs_pos( &q ) >> 3 ); } static void write_service_descriptor( bs_t *s, int service_type, char *provider_name, char *service_name ) { int provider_name_length = strlen( provider_name ); int service_name_length = strlen( service_name ); bs_write( s, 8, DVB_SERVICE_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, provider_name_length + service_name_length + 3 ); // descriptor_length bs_write( s, 8, service_type ); // service_type bs_write( s, 8, provider_name_length ); // service_provider_name_length while( *provider_name != '\0' ) bs_write( s, 8, (unsigned char)*provider_name++ ); bs_write( s, 8, service_name_length ); // service_name_length while( *service_name != '\0' ) bs_write( s, 8, (unsigned char)*service_name++ ); } /* DVB Service Information */ int write_nit( ts_writer_t *w ) { int start; bs_t *s = &w->out.bs; write_packet_header( w, s, 1, w->network_pid, PAYLOAD_ONLY, &w->nit->cc ); bs_write( s, 8, 0 ); // pointer field start = bs_pos( s ); bs_write( s, 8, NIT_TID ); // table_id = network_information_section bs_write1( s, 1 ); // section_syntax_indicator bs_write1( s, 1 ); // reserved_future_use bs_write( s, 2, 0x03 ); // reserved bs_write( s, 12, 0x13 ); // section_length bs_write( s, 16, w->network_id ); // network_id bs_write( s, 2, 0x02 ); // reserved bs_write( s, 5, 0 ); // version_number bs_write1( s, 1 ); // current_next_indicator bs_write(s, 8, 0 ); // section_number bs_write(s, 8, 0 ); // last_section_number bs_write(s, 4, 0xf ); // reserved_future_use bs_write(s, 12, 0 ); // network_descriptors_length // network descriptor(s) here bs_write(s, 4, 0xf ); // reserved_future_use bs_write(s, 12, 0 ); // transport_stream_loop_length bs_write( s, 16, w->ts_id ); // transport_stream_id bs_write( s, 16, w->network_id ); // original_network_id bs_write( s, 4, 0xf ); // reserved_future_use bs_write(s, 12, 0 ); // transport_descriptors_length // transport descriptor(s) here bs_flush( s ); write_crc( s, start ); // -40 to include header and pointer field write_padding( s, start - 40 ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } /* "The SDT contains data describing the services in the system e.g. names of services, the service provider, etc" */ int write_sdt( ts_writer_t *w ) { int start; uint8_t *sdt_buf = NULL, *sdt_buf2 = NULL; int buf_size = 0; int ret = 0; int section_length; bs_t *s = &w->out.bs; bs_t q, r; buf_size = 200; /* Estimate size of buffer */ for( int i = 0; i < w->num_programs; i++ ) { buf_size += strlen( w->programs[i]->sdt_ctx.provider_name ); buf_size += strlen( w->programs[i]->sdt_ctx.service_name ); } buf_size <<= 1; sdt_buf = malloc( buf_size ); if( !sdt_buf ) { fprintf( stderr, "malloc failed" ); goto end; } sdt_buf2 = malloc( buf_size ); if( !sdt_buf2 ) { fprintf( stderr, "malloc failed" ); goto end; } start = bs_pos( s ); write_packet_header( w, s, 1, SDT_PID, PAYLOAD_ONLY, &w->sdt->cc ); bs_write( s, 8, 0 ); // pointer field bs_init( &q, sdt_buf, buf_size ); bs_write( &q, 8, SDT_TID ); // table_id bs_write1( &q, 1 ); // section_syntax_indicator bs_write1( &q, 1 ); // reserved_future_use bs_write( &q, 2, 0x3 ); // reserved bs_init( &r, sdt_buf2, buf_size ); bs_write( &r, 16, w->ts_id ); // transport_stream_id bs_write( &r, 2, 0x3 ); // reserved bs_write( &r, 5, 0 ); // version_number bs_write1( &r, 1 ); // current_next_indicator bs_write( &r, 8, 0 ); // section_number bs_write( &r, 8, 0 ); // last_section_number bs_write( &r, 16, w->network_id ); // original_network_id bs_write( &r, 8, 0xff ); // reserved_future_use for( int i = 0; i < w->num_programs; i++ ) { sdt_program_ctx_t *sdt_ctx = &w->programs[i]->sdt_ctx; bs_write( &r, 16, w->programs[i]->program_num & 0xffff ); // service_id (equivalent to program_number) bs_write( &r, 6, 0x3f ); // reserved_future_use bs_write1( &r, 0 ); // EIT_schedule_flag bs_write1( &r, 0 ); // EIT_present_following_flag bs_write( &r, 3, 4 ); // running_status bs_write1( &r, 0 ); // free_CA_mode int provider_name_len = strlen( sdt_ctx->provider_name ); int service_name_len = strlen( sdt_ctx->service_name ); char *provider_name = sdt_ctx->provider_name; char *service_name = sdt_ctx->service_name; int descriptors_len = 5 + provider_name_len + service_name_len; bs_write( &r, 12, descriptors_len ); // descriptors_loop_length // service descriptor (mandatory for DVB) write_service_descriptor( &r, sdt_ctx->service_type, provider_name, service_name ); // other descriptor(s) here } /* section length includes crc */ section_length = (bs_pos( &r ) >> 3) + 4; bs_write( &q, 12, section_length & 0x3ff ); // section_length /* write main chunk into sdt array */ bs_flush( &r ); write_bytes( &q, sdt_buf2, bs_pos( &r ) >> 3 ); /* take crc of the whole service description section */ bs_flush( &q ); write_crc( &q, 0 ); int length = bs_pos( &q ) >> 3; int bytes_left = TS_PACKET_SIZE - (( bs_pos( s ) - start ) >> 3); bs_flush( &q ); write_bytes( s, sdt_buf, MIN( bytes_left, length ) ); bs_flush( s ); write_padding( s, start ); if( increase_pcr( w, 1, 0 ) < 0 ) goto end; int pos = MIN( bytes_left, length ); length -= pos; bytes_left = 184; /* keep writing SDT packets */ while( length > 0 ) { start = bs_pos( s ); write_packet_header( w, s, 0, SDT_PID, PAYLOAD_ONLY, &w->sdt->cc ); write_bytes( s, &sdt_buf[pos], MIN( bytes_left, length ) ); write_padding( s, start ); pos += MIN( bytes_left, length ); length -= MIN( bytes_left, length ); if( increase_pcr( w, 1, 0 ) < 0 ) goto end; } end: free( sdt_buf ); free( sdt_buf2 ); return ret; } #if 0 // FIXME // "the EIT contains data concerning events or programmes such as event name, start time, duration, etc.; " void write_eit( ts_writer_t *w ) { uint64_t start; bs_t *s = &w->out.bs; write_packet_header( w, s, 1, EIT_PID, PAYLOAD_ONLY, &w->eit->cc ); bs_write( s, 8, 0 ); // pointer field start = bs_pos( s ); bs_write( s, 8, EIT_TID ); // table_id bs_write1( s, 0 ); // section_syntax_indicator CHECKME bs_write1( s, 1 ); // reserved_future_use bs_write( s, 2, 0x03); // reserved bs_write( s, 12, len ); // section_length } #endif static void write_utc_time( bs_t *s ) { int l, mjd; time_t cur_time; struct tm *now; /* MJD conversions in Annex C of ETSI EN 300 468 */ cur_time = time( NULL ); now = gmtime( &cur_time ); l = ( ( now->tm_mon + 1 == 1 ) || ( now->tm_mon + 1 == 2 ) ) ? 1 : 0; mjd = 14956 + now->tm_mday + (int)((now->tm_year - l) * 365.25) + (int)((now->tm_mon + 1 + 1 + l * 12) * 30.6001); bs_write( s, 8, mjd >> 8 ); bs_write( s, 8, mjd & 0xff ); bs_write( s, 8, (now->tm_hour / 10) << 4 | (now->tm_hour % 10) ); // hours bs_write( s, 8, (now->tm_min / 10) << 4 | (now->tm_min % 10) ); // minutes bs_write( s, 8, (now->tm_sec / 10) << 4 | (now->tm_sec % 10) ); // seconds } int write_tdt( ts_writer_t *w ) { int start; bs_t *s = &w->out.bs; write_packet_header( w, s, 1, TDT_PID, PAYLOAD_ONLY, &w->tdt->cc ); bs_write( s, 8, 0 ); // pointer field start = bs_pos( s ); bs_write( s, 8, TDT_TID ); // table_id bs_write1( s, 0 ); // section_syntax_indicator bs_write1( s, 1 ); // reserved_future_use bs_write( s, 2, 0x03 ); // reserved bs_write( s, 12, 0x05 ); // section_length write_utc_time( s ); // -40 to include header and pointer field write_padding( s, start - 40 ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } // TODO TOT void write_dvb_au_information( bs_t *s, ts_int_pes_t *pes ) { bs_t q; uint8_t temp[128]; ts_int_stream_t *stream = pes->stream; bs_write( s, 8, AU_INFORMATION_DATA_FIELD ); // data_field_tag bs_init( &q, temp, 128 ); if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 ) bs_write( &q, 4, 1 ); // AU_coding_format else if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) bs_write( &q, 4, 0x2 ); // AU_coding_format bs_write( &q, 4, pes->frame_type ); // AU_coding_type_information bs_write( &q, 2, pes->ref_pic_idc ); // AU_ref_pic_idc if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 ) bs_write( &q, 2, pes->pic_struct ); // AU_pic_struct else if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) bs_write( &q, 2, 0 ); // AU_coding_format bs_write1( &q, 1 ); // AU_PTS_present_flag bs_write1( &q, 1 ); // AU_profile_info_present_flag bs_write1( &q, 1 ); // AU_stream_info_present_flag bs_write1( &q, 0 ); // AU_trick_mode_info_present_flag bs_write32( &q, (pes->pts * 300) & 0xffffffff ); // AU_PTS_32 bs_write( &q, 4, 0 ); // reserved bs_write( &q, 4, stream->dvb_au_frame_rate ); // AU_frame_rate_code bs_write( &q, 8, stream->mpegvideo_ctx->profile & 0xff ); // profile_idc if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) { bs_write1( &q, stream->mpegvideo_ctx->profile == AVC_BASELINE ); // constraint_set0_flag bs_write1( &q, stream->mpegvideo_ctx->profile <= AVC_MAIN ); // constraint_set1_flag bs_write1( &q, 0 ); // constraint_set2_flag if( stream->mpegvideo_ctx->level == 9 && stream->mpegvideo_ctx->profile <= AVC_MAIN ) // level 1b bs_write1( &q, 1 ); // constraint_set3_flag else if( stream->mpegvideo_ctx->profile == AVC_HIGH_10_INTRA || stream->mpegvideo_ctx->profile == AVC_CAVLC_444_INTRA || stream->mpegvideo_ctx->profile == AVC_HIGH_444_INTRA ) bs_write1( &q, 1 ); // constraint_set3_flag else bs_write1( &q, 0 ); // constraint_set3_flag bs_write1( &q, 0 ); // constraint_set4_flag bs_write1( &q, 0 ); // constraint_set5_flag } else bs_write( &q, 5, 0 ); bs_write( &q, 2, 0 ); // AU_AVC_compatible_flags bs_write( &q, 8, stream->mpegvideo_ctx->level & 0xff ); // level_idc if( pes->write_pulldown_info ) { bs_write1( &q, 1 ); // AU_Pulldown_info_present_flag bs_write( &q, 6, 0 ); // AU_reserved_zero bs_write1( &q, 0 ); // AU_flags_extension_1 bs_write( &q, 4, 0 ); // AU_reserved_zero bs_write( &q, 4, pes->pic_struct & 0xf ); // AU_Pulldown_info } /* reserved bytes */ bs_flush( &q ); bs_write( s, 8, bs_pos( &q ) >> 3 ); // data_field_length write_bytes( s, temp, bs_pos( &q ) >> 3 ); }