int mpeg2ts_program_register_pid_processor(mpeg2ts_program_t *m2p, uint32_t PID, demux_pid_handler_t *handler) {
	if (m2p == NULL || m2p->pmt == NULL || handler == NULL)
		return 0;

	// if some handler already registered for this PID before, unregister it
	mpeg2ts_program_unregister_pid_processor(m2p, PID);

	pid_info_t *pid = pid_info_new();
	elementary_stream_info_t *esi = NULL;
	for (int i=0; i < vqarray_length(m2p->pmt->es_info); i++) {
		elementary_stream_info_t *tmp = vqarray_get(m2p->pmt->es_info, i);
		if (tmp != NULL && tmp->elementary_PID == PID) {
			esi = tmp;
			break;
		}
	}
	if (esi == NULL) {
		LOG_ERROR_ARGS("Elementary stream with PID 0x%02X not found in PMT of program %d", PID, m2p->program_number);
		SAFE_REPORT_TS_ERR(-71);
		return -1;
	}
	pid->es_info = esi;
	pid->demux_handler = handler;
	vqarray_add(m2p->pids, pid);

	return 0;
}
int mpeg2ts_stream_read_ts_packet(mpeg2ts_stream_t *m2s, ts_packet_t *ts) {
	int ret = 0;

	if (m2s == NULL || ts == NULL)
		return 0;
	if ((m2s->pat == NULL) && (ts->header.PID != PAT_PID))
		return 0; // we don't have any PSI, nothing we can do.

	pid_info_t *pi = NULL;
	for (int i = 0; i < vqarray_length(m2s->programs) && (pi == NULL); i++) {
		mpeg2ts_program_t *m2p = vqarray_get(m2s->programs, i);
		if (m2p == NULL)
			continue;

		if (m2p->PID == ts->header.PID) { // got a PMT
			// FIXME: this hack works in 90% of cases
			// however, it won't work when PAT is
			// in 2+ packets. we need generic section reader for this

			program_map_section_t *pms = program_map_section_new();
			program_map_section_read(pms, ts->payload.bytes + 1, ts->payload.len - 1);
			if (pms->program_number != m2p->program_number) {
				LOG_ERROR_ARGS("Invalid program number in PMT: expecting %d, encountered %d",
					m2p->program_number, pms->program_number);
				SAFE_REPORT_TS_ERR(-70);
				return -1;
			}

			if (m2p->pmt != NULL) {
				// FIXME: compare sections, drop the older one
				if ( pms->version_number != m2p->pmt->version_number )
					LOG_WARN("Changing PMT section versions not supported yet");
				program_map_section_free(pms);

				continue;
			} else {
				m2p->pmt = pms;
				for (int es_idx=0; es_idx < vqarray_length(m2p->pmt->es_info) ; es_idx++) {
					elementary_stream_info_t *es = vqarray_get(m2p->pmt->es_info, es_idx);
					if (es == NULL)
						continue;
					pi = pid_info_new();
					pi->es_info = es;
					// FIXME add default stream handlers via stream type
					vqarray_add(m2p->pids, pi);
					pi = NULL;
				}
			}
		} else { // same elementary PID may be owned by multiple programs
			pi = mpeg2ts_program_get_pid_info(m2p, ts->header.PID);
			if ((pi != NULL) && (pi->demux_handler != NULL) && (pi->demux_handler->process_ts_packet != NULL)) {
				ret = pi->demux_handler->process_ts_packet(ts, pi->es_info, pi->demux_handler->arg);
				if (ret != 0)
					return ret;
			}
		}
	}

	return ret;
}
// "factory methods"
int read_descriptor_loop(vqarray_t *desc_list, bs_t *b, int length) {
	int desc_start = bs_pos(b);

	while (length > bs_pos(b) - desc_start) {
		descriptor_t *desc = descriptor_new();
		desc = descriptor_read(desc, b);
		vqarray_add(desc_list, desc);
	}

	return bs_pos(b) - desc_start;
}
Пример #4
0
int program_map_section_read(program_map_section_t *pms, uint8_t *buf, size_t buf_size, uint32_t payload_unit_start_indicator,
   psi_table_buffer_t *pmtBuffer) 
{ 
   LOG_DEBUG ("program_map_section_read -- entering");
   if (pms == NULL || buf == NULL) 
   {
      SAFE_REPORT_TS_ERR(-1); 
      return 0;
   }

   bs_t *b = NULL;

   if (!payload_unit_start_indicator &&  pmtBuffer->buffer == NULL)
   {
      // this TS packet is not start of table, and we have no cached table data
      LOG_WARN ("program_map_section_read: payload_unit_start_indicator not set and no cached data");
      return 0;
   }

   if (payload_unit_start_indicator)
   {
      uint8_t payloadStartPtr = buf[0];
      buf += (payloadStartPtr + 1);
      buf_size -= (payloadStartPtr + 1);
      LOG_DEBUG_ARGS ("program_map_section_read: payloadStartPtr = %d", payloadStartPtr);
   }

   // check for pmt spanning multiple TS packets
   if (pmtBuffer->buffer != NULL)
   {
      LOG_DEBUG_ARGS ("program_map_section_read: pmtBuffer detected: pmtBufferAllocSz = %d, pmtBufferUsedSz = %d", pmtBuffer->bufferAllocSz, pmtBuffer->bufferUsedSz);
      size_t numBytesToCopy = buf_size;
      if (buf_size > (pmtBuffer->bufferAllocSz - pmtBuffer->bufferUsedSz))
      {
         numBytesToCopy = pmtBuffer->bufferAllocSz - pmtBuffer->bufferUsedSz;
      }
         
      LOG_DEBUG_ARGS ("program_map_section_read: copying %d bytes to pmtBuffer", numBytesToCopy);
      memcpy (pmtBuffer->buffer + pmtBuffer->bufferUsedSz, buf, numBytesToCopy);
      pmtBuffer->bufferUsedSz += numBytesToCopy;
      
      if (pmtBuffer->bufferUsedSz < pmtBuffer->bufferAllocSz)
      {
         LOG_DEBUG ("program_map_section_read: pmtBuffer not yet full -- returning");
         return 0;
      }

      b = bs_new(pmtBuffer->buffer, pmtBuffer->bufferUsedSz);
   }
   else
   {
      b = bs_new(buf, buf_size);
   }
      
   pms->table_id = bs_read_u8(b); 
   if (pms->table_id != TS_program_map_section) 
   {
      LOG_ERROR_ARGS("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section); 
      reportAddErrorLogArgs("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section); 
      SAFE_REPORT_TS_ERR(-40);
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }

   pms->section_syntax_indicator = bs_read_u1(b); 
   if (!pms->section_syntax_indicator) 
   {
      LOG_ERROR("section_syntax_indicator not set in PMT"); 
      reportAddErrorLog("section_syntax_indicator not set in PMT"); 
      SAFE_REPORT_TS_ERR(-41); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }
   
   bs_skip_u(b, 3); 

   pms->section_length = bs_read_u(b, 12); 
   if (pms->section_length > MAX_SECTION_LEN) 
   {
      LOG_ERROR_ARGS("PMT section length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pms->section_length, MAX_SECTION_LEN); 
      reportAddErrorLogArgs("PMT section length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pms->section_length, MAX_SECTION_LEN); 
      SAFE_REPORT_TS_ERR(-42); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }

   if (pms->section_length > bs_bytes_left(b))
   {
      LOG_DEBUG ("program_map_section_read: Detected section spans more than one TS packet -- allocating buffer");

      if (pmtBuffer->buffer != NULL)
      {
         // should never get here
         LOG_ERROR ("program_map_section_read: unexpected pmtBufffer");
         reportAddErrorLog ("program_map_section_read: unexpected pmtBufffer");
         resetPSITableBuffer(pmtBuffer);
      }

      pmtBuffer->bufferAllocSz = pms->section_length + 3;
      pmtBuffer->buffer = (uint8_t *)calloc (pms->section_length + 3, 1);
      memcpy (pmtBuffer->buffer, buf, buf_size);
      pmtBuffer->bufferUsedSz = buf_size;

      bs_free (b);
      return 0;
   }

   int section_start = bs_pos(b); 
   
   // bytes 0,1
   pms->program_number = bs_read_u16(b); 
   
   // byte 2;
   bs_skip_u(b, 2); 
   pms->version_number = bs_read_u(b, 5); 
   pms->current_next_indicator = bs_read_u1(b); 
   if (!pms->current_next_indicator) LOG_WARN("This PMT is not yet applicable/n"); 
   
   // bytes 3,4
   pms->section_number = bs_read_u8(b); 
   pms->last_section_number = bs_read_u8(b); 
   if (pms->section_number != 0 || pms->last_section_number != 0) 
   {
      LOG_ERROR("Multi-section PMT is not allowed/n"); 
      reportAddErrorLog("Multi-section PMT is not allowed/n"); 
      SAFE_REPORT_TS_ERR(-43); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }
   
   bs_skip_u(b, 3); 
   pms->PCR_PID = bs_read_u(b, 13); 
   if (pms->PCR_PID < GENERAL_PURPOSE_PID_MIN || pms->PCR_PID > GENERAL_PURPOSE_PID_MAX) 
   {
      LOG_ERROR_ARGS("PCR PID has invalid value 0x%02X", pms->PCR_PID); 
      reportAddErrorLogArgs("PCR PID has invalid value 0x%02X", pms->PCR_PID); 
      SAFE_REPORT_TS_ERR(-44); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }
 //  printf ("PCR PID = %d\n", pms->PCR_PID);
   bs_skip_u(b, 4); 
   
   pms->program_info_length = bs_read_u(b, 12); 
   if (pms->program_info_length > MAX_PROGRAM_INFO_LEN) 
   {
      LOG_ERROR_ARGS("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pms->program_info_length, MAX_PROGRAM_INFO_LEN); 
      reportAddErrorLogArgs("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pms->program_info_length, MAX_PROGRAM_INFO_LEN); 
      SAFE_REPORT_TS_ERR(-45); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   }
   
   read_descriptor_loop(pms->descriptors, b, pms->program_info_length); 

   while (!bs_eof(b) && pms->section_length - (bs_pos(b) - section_start) > 4) // account for CRC
   {
      elementary_stream_info_t *es = es_info_new();
      es_info_read(es, b); 
      vqarray_add(pms->es_info, es);
   }
   
   pms->CRC_32 = bs_read_u32(b); 
   
   // check CRC
   crc_t pas_crc = crc_init(); 
   pas_crc = crc_update(pas_crc, b->start, bs_pos(b) - 4); 
   pas_crc = crc_finalize(pas_crc); 
   if (pas_crc != pms->CRC_32) 
   {
      LOG_ERROR_ARGS("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc); 
      reportAddErrorLogArgs("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc); 
      SAFE_REPORT_TS_ERR(-46); 
      resetPSITableBuffer(pmtBuffer);
      bs_free (b);
      return 0;
   } 
   else 
   {
      // LOG_DEBUG("PMT CRC_32 checked successfully");
   }
   
   int bytes_read = bs_pos(b); 
   bs_free(b); 

   resetPSITableBuffer(pmtBuffer);

   return bytes_read;
}
Пример #5
0
int program_association_section_read(program_association_section_t *pas, uint8_t *buf, size_t buf_len, uint32_t payload_unit_start_indicator,
                                     psi_table_buffer_t *patBuffer)
{ 
   vqarray_t *programs;
   int num_programs = 0;

   if (pas == NULL || buf == NULL) 
   {
      SAFE_REPORT_TS_ERR(-1); 
      return 0;
   }

   bs_t *b = NULL;

   if (!payload_unit_start_indicator &&  patBuffer->buffer == NULL)
   {
      // this TS packet is not start of table, and we have no cached table data
      LOG_WARN ("program_association_section_read: payload_unit_start_indicator not set and no cached data");
      return 0;
   }

   if (payload_unit_start_indicator)
   {
      uint8_t payloadStartPtr = buf[0];
      buf += (payloadStartPtr + 1);
      buf_len -= (payloadStartPtr + 1);
      LOG_DEBUG_ARGS ("program_association_section_read: payloadStartPtr = %d", payloadStartPtr);
   }


   // check for pat spanning multiple TS packets
   if (patBuffer->buffer != NULL)
   {
      LOG_DEBUG_ARGS ("program_association_section_read: patBuffer detected: patBufferAllocSz = %d, patBufferUsedSz = %d", 
         patBuffer->bufferAllocSz, patBuffer->bufferUsedSz);
      size_t numBytesToCopy = buf_len;
      if (buf_len > (patBuffer->bufferAllocSz - patBuffer->bufferUsedSz))
      {
         numBytesToCopy = patBuffer->bufferAllocSz - patBuffer->bufferUsedSz;
      }
         
      LOG_DEBUG_ARGS ("program_association_section_read: copying %d bytes to patBuffer", numBytesToCopy);
      memcpy (patBuffer->buffer + patBuffer->bufferUsedSz, buf, numBytesToCopy);
      patBuffer->bufferUsedSz += numBytesToCopy;
      
      if (patBuffer->bufferUsedSz < patBuffer->bufferAllocSz)
      {
         LOG_DEBUG ("program_association_section_read: patBuffer not yet full -- returning");
         return 0;
      }

      b = bs_new(patBuffer->buffer, patBuffer->bufferUsedSz);
   }
   else
   {
      b = bs_new(buf, buf_len);
   }
      

   pas->table_id = bs_read_u8(b); 
   if (pas->table_id != program_association_section) 
   {
      LOG_ERROR_ARGS("Table ID in PAT is 0x%02X instead of expected 0x%02X", 
                     pas->table_id, program_association_section); 
      reportAddErrorLogArgs("Table ID in PAT is 0x%02X instead of expected 0x%02X", 
                     pas->table_id, program_association_section); 
      SAFE_REPORT_TS_ERR(-30); 
      resetPSITableBuffer(patBuffer);
      bs_free (b);
      return 0;
   }
   
   // read byte 0
   
   pas->section_syntax_indicator = bs_read_u1(b); 
   if (!pas->section_syntax_indicator) 
   {
      LOG_ERROR("section_syntax_indicator not set in PAT"); 
      reportAddErrorLog("section_syntax_indicator not set in PAT"); 
      SAFE_REPORT_TS_ERR(-31); 
      resetPSITableBuffer(patBuffer);
      bs_free (b);
      return 0;
   }
   bs_skip_u(b, 3); // TODO read the zero bit, check it to be zero
   pas->section_length = bs_read_u(b, 12); 
   if (pas->section_length > MAX_SECTION_LEN) 
   {
      LOG_ERROR_ARGS("PAT section length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pas->section_length, MAX_SECTION_LEN); 
      reportAddErrorLogArgs("PAT section length is 0x%02X, larger than maximum allowed 0x%02X", 
                     pas->section_length, MAX_SECTION_LEN); 
      SAFE_REPORT_TS_ERR(-32); 
      resetPSITableBuffer(patBuffer);
      bs_free (b);
      return 0;
   }
   
   if (pas->section_length > bs_bytes_left(b))
   {
      LOG_DEBUG ("program_association_section_read: Detected section spans more than one TS packet -- allocating buffer");

      if (patBuffer->buffer != NULL)
      {
         // should never get here
         LOG_ERROR ("program_association_section_read: unexpected patBufffer");
         reportAddErrorLog ("program_association_section_read: unexpected patBufffer");
         resetPSITableBuffer(patBuffer);
      }

      patBuffer->bufferAllocSz = pas->section_length + 3;
      patBuffer->buffer = (uint8_t *)calloc (pas->section_length + 3, 1);
      memcpy (patBuffer->buffer, buf, buf_len);
      patBuffer->bufferUsedSz = buf_len;

      bs_free (b);
      return 0;
   }

   // read bytes 1,2
   
   pas->transport_stream_id = bs_read_u16(b); 
   
   // read bytes 3,4
   
   bs_skip_u(b, 2); 
   pas->version_number = bs_read_u(b, 5); 
   pas->current_next_indicator = bs_read_u1(b); 
   if (!pas->current_next_indicator) LOG_WARN("This PAT is not yet applicable/n"); 
   
   // read byte 5
   
   pas->section_number = bs_read_u8(b); 
   pas->last_section_number = bs_read_u8(b); 
   if (pas->section_number != 0 || pas->last_section_number != 0) LOG_WARN("Multi-section PAT is not supported yet/n"); 
   
   // read bytes 6,7
   
   num_programs = (pas->section_length - 5 - 4) / 4;  // Programs listed in the PAT
   // explanation: section_length gives us the length from the end of section_length
   // we used 5 bytes for the mandatory section fields, and will use another 4 bytes for CRC
   // the remaining bytes contain program information, which is 4 bytes per iteration
   // It's much shorter in C :-)

   // Read the program loop, but ignore the NIT PID "program"
   programs = vqarray_new();
   for (uint32_t i = 0; i < num_programs; i++)
   {
      program_info_t *prog = malloc(sizeof(program_info_t));
      prog->program_number = bs_read_u16(b);
      if (prog->program_number == 0) { // Skip the NIT PID program (not a real program)
         free(prog);
         bs_skip_u(b, 16);
         continue;
      }
      bs_skip_u(b, 3);
      prog->program_map_PID = bs_read_u(b, 13);
      vqarray_add(programs, (vqarray_elem_t*)prog);
   }

   // This is our true number of programs
   pas->_num_programs = vqarray_length(programs);
   
   if (pas->_num_programs > 1) LOG_WARN_ARGS("%zd programs found, but only SPTS is fully supported. Patches are welcome.", pas->_num_programs); 
   
   // Copy form our vqarray into the native array
   pas->programs = malloc(pas->_num_programs * sizeof(program_info_t)); 
   for (uint32_t i = 0; i < pas->_num_programs; i++) 
   {
      program_info_t* prog = (program_info_t*)vqarray_pop(programs);
      pas->programs[i] = *prog;
      free(prog);
   }
   vqarray_free(programs);
   
   pas->CRC_32 = bs_read_u32(b); 
   
   // check CRC
   crc_t pas_crc = crc_init(); 
   pas_crc = crc_update(pas_crc, buf, bs_pos(b) - 4); 
   pas_crc = crc_finalize(pas_crc); 
   if (pas_crc != pas->CRC_32) 
   {
      LOG_ERROR_ARGS("PAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pas->CRC_32, pas_crc); 
      reportAddErrorLogArgs("PAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pas->CRC_32, pas_crc); 
      SAFE_REPORT_TS_ERR(-33); 
      resetPSITableBuffer(patBuffer);
      bs_free (b);
      return 0;
   } 
   else 
   {
      // LOG_DEBUG("PAT CRC_32 checked successfully");
      // don't enable unless you want to see this every ~100ms
   }
   

   bs_free(b); 
            
   resetPSITableBuffer(patBuffer);

   return 1;
}
int program_map_section_read(program_map_section_t *pms, uint8_t *buf, size_t buf_size) {
	if (pms == NULL || buf == NULL) {
		SAFE_REPORT_TS_ERR(-1);
		return 0;
	}

	bs_t *b = bs_new(buf, buf_size);

	pms->table_id = bs_read_u8(b);
	if (pms->table_id != TS_program_map_section) {
		LOG_ERROR_ARGS("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section);
		SAFE_REPORT_TS_ERR(-40);
		return 0;
	}

	pms->section_syntax_indicator = bs_read_u1(b);
	if (!pms->section_syntax_indicator) {
		LOG_ERROR("section_syntax_indicator not set in PMT");
		SAFE_REPORT_TS_ERR(-41);
		return 0;
	}

	bs_skip_u(b, 3);
	pms->section_length = bs_read_u(b, 12);
	if (pms->section_length > MAX_SECTION_LEN) {
		LOG_ERROR_ARGS("PMT section length is 0x%02X, larger than maximum allowed 0x%02X",
			pms->section_length, MAX_SECTION_LEN);
		SAFE_REPORT_TS_ERR(-42);
		return 0;
	}

	int section_start = bs_pos(b);

	// bytes 0,1
	pms->program_number = bs_read_u16(b);

	// byte 2;
	bs_skip_u(b, 2);
	pms->version_number = bs_read_u(b, 5);
	pms->current_next_indicator = bs_read_u1(b);
	if (!pms->current_next_indicator)
		LOG_WARN("This PMT is not yet applicable/n");

	// bytes 3,4
	pms->section_number = bs_read_u8(b);
	pms->last_section_number = bs_read_u8(b);
	if (pms->section_number != 0 || pms->last_section_number != 0) {
		LOG_ERROR("Multi-section PMT is not allowed/n");
		SAFE_REPORT_TS_ERR(-43);
		return 0;
	}

	bs_skip_u(b, 3);
	pms->PCR_PID = bs_read_u(b, 13);
	if (pms->PCR_PID < GENERAL_PURPOSE_PID_MIN || pms->PCR_PID > GENERAL_PURPOSE_PID_MAX) {
		LOG_ERROR_ARGS("PCR PID has invalid value 0x%02X", pms->PCR_PID);
		SAFE_REPORT_TS_ERR(-44);
		return 0;
	}
	bs_skip_u(b, 4);

	pms->program_info_length = bs_read_u(b, 12);
	if (pms->program_info_length > MAX_PROGRAM_INFO_LEN) {
		LOG_ERROR_ARGS("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X",
			pms->program_info_length, MAX_PROGRAM_INFO_LEN);
		SAFE_REPORT_TS_ERR(-45);
		return 0;
	}

	read_descriptor_loop(pms->descriptors, b, pms->program_info_length);

	while (pms->section_length - (bs_pos(b) - section_start) > 4) { // account for CRC
		elementary_stream_info_t *es = es_info_new();
		es_info_read(es, b);
		vqarray_add(pms->es_info, es);
	}

	pms->CRC_32 = bs_read_u32(b);

	// check CRC
	crc_t pas_crc = crc_init();
	pas_crc = crc_update(pas_crc, buf, bs_pos(b) - 4);
	pas_crc = crc_finalize(pas_crc);
	if (pas_crc != pms->CRC_32) {
		LOG_ERROR_ARGS("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc);
		SAFE_REPORT_TS_ERR(-46);
		return 0;
	} else {
		LOG_DEBUG("PMT CRC_32 checked successfully");
	}

	int bytes_read = bs_pos(b);
	bs_free(b);

	return bytes_read;
}