Commit e2393869 authored by Zlu Zu's avatar Zlu Zu Committed by Jean-Paul Saman

BAT decoder and generator

Original patch was modified by Jean-Paul Saman <jpsaman@videolan.org>
- changed license to LGPLv2.1
- general cleanup
- fixed memleaks
- add missing prototypes to bat_private.h
- add static to functions in example/decode_bat.c
parent 897aa49f
......@@ -60,3 +60,7 @@ E: jpsaman@videolan.org
D: VBI Data Descriptor (0x45)
D: CUE Identifier Descriptor (0x8a)
D: Splice Information Table
N: Zlu Zu
E: zluzu@gmail.com
D: BAT decoder and generator
## Process this file with automake to produce Makefile.in
noinst_PROGRAMS = decode_pat decode_pmt get_pcr_pid decode_sdt decode_mpeg
noinst_PROGRAMS = decode_pat decode_pmt get_pcr_pid decode_sdt decode_mpeg decode_bat
decode_pat_SOURCES = decode_pat.c
decode_pat_LDFLAGS = -L../src -ldvbpsi
......@@ -20,3 +20,5 @@ decode_mpeg_SOURCES += connect.c connect.h
endif
decode_mpeg_LDFLAGS = -L../src -ldvbpsi -lm
decode_bat_SOURCES = decode_bat.c
decode_bat_LDFLAGS = -L../src -ldvbpsi
/*****************************************************************************
* decode_bat.c: BAT decoder example
*----------------------------------------------------------------------------
* Copyright (C) 2001-2010 VideoLAN
* $Id: decode_bat.c 01 2010-04-01 17:55:18 zhuzlu $
*
* Authors: Zhu zhenglu <zhuzlu@gmail.com>
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*----------------------------------------------------------------------------
*
*****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif
/* The libdvbpsi distribution defines DVBPSI_DIST */
#ifdef DVBPSI_DIST
#include "../src/dvbpsi.h"
#include "../src/psi.h"
#include "../src/demux.h"
#include "../src/descriptor.h"
#include "../src/tables/bat.h"
#else
#include <dvbpsi/dvbpsi.h>
#include <dvbpsi/psi.h>
#include <dvbpsi/demux.h>
#include <dvbpsi/descriptor.h>
#include <dvbpsi/bat.h>
#endif
/*****************************************************************************
* ReadPacket
*****************************************************************************/
static int ReadPacket(int i_fd, uint8_t* p_dst)
{
int i = 187;
int i_rc = 1;
p_dst[0] = 0;
while((p_dst[0] != 0x47) && (i_rc > 0))
{
i_rc = read(i_fd, p_dst, 1);
}
while((i != 0) && (i_rc > 0))
{
i_rc = read(i_fd, p_dst + 188 - i, i);
if(i_rc >= 0)
i -= i_rc;
}
return (i == 0) ? 1 : 0;
}
/*****************************************************************************
* DumpDescriptors
*****************************************************************************/
static void DumpDescriptors(const char* str, dvbpsi_descriptor_t* p_descriptor)
{
while(p_descriptor)
{
int i;
printf("%s 0x%02x : \"", str, p_descriptor->i_tag);
for(i = 0; i < p_descriptor->i_length; i++)
printf("%.2x", p_descriptor->p_data[i]);
printf("\"\n");
p_descriptor = p_descriptor->p_next;
}
};
/*****************************************************************************
* Print_0xb1
*****************************************************************************/
static void Print_DescTag_0xb1(uint16_t i_ts_id, dvbpsi_descriptor_t* p_descriptor)
{
int i;
uint8_t *pdata;
unsigned int sub_bouquet_id;
int num;
unsigned int formater;
pdata = p_descriptor->p_data;
num=(p_descriptor->i_length-2)/9;
sub_bouquet_id= (((unsigned int)pdata[0]&0xff)<<8)|pdata[1];
if(sub_bouquet_id!=0xffff)
{
printf("sub_bouquet_id!=0xffff\n");
return;
}
if(num*9!=p_descriptor->i_length-2)
{
printf("num of private_services error\n");
return;
}
pdata+=2;
printf("\nts_id: %d, service_num: %d, service_id list: \n",i_ts_id,num);
formater=0;
for(i = 0; i < num; i++)
{
uint16_t service_id=(((uint16_t)pdata[0]&0xff)<<8)|pdata[1];
printf("%.4x ", service_id);
formater++;
if(0 == formater%16)
{
printf("\n");
}
pdata+=9;
}
printf("\r\n");
}
/*****************************************************************************
* DumpDescriptors_verbose
*****************************************************************************/
static void DumpDescriptors_verbose(uint16_t i_ts_id, dvbpsi_descriptor_t* p_descriptor)
{
while(p_descriptor)
{
if(0xb1 == p_descriptor->i_tag)
{
Print_DescTag_0xb1(i_ts_id,p_descriptor);
}
p_descriptor = p_descriptor->p_next;
}
};
/*****************************************************************************
* DumpBAT_verbose
*****************************************************************************/
static void DumpBAT_verbose(void* p_zero, dvbpsi_bat_t* p_bat)
{
dvbpsi_bat_ts_t* p_ts = p_bat->p_first_ts;
while(p_ts)
{
DumpDescriptors_verbose(p_ts->i_ts_id, p_ts->p_first_descriptor);
p_ts = p_ts->p_next;
}
}
/*****************************************************************************
* DumpBAT
*****************************************************************************/
static void DumpBAT(void* p_zero, dvbpsi_bat_t* p_bat)
{
dvbpsi_bat_ts_t* p_ts = p_bat->p_first_ts;
{
printf( "\n");
printf( "New active BAT(binary dumped)\n");
printf( " bouquet_id : %d\n",
p_bat->i_bouquet_id);
printf( " version_number : %d\n",
p_bat->i_version);
printf( " | ts_id \n");
while(p_ts)
{
printf(" | 0x%02x \n",
p_ts->i_ts_id);
DumpDescriptors(" | ]", p_ts->p_first_descriptor);
p_ts = p_ts->p_next;
}
printf( "\n");
printf( "New active BAT(string dumped)\n");
DumpBAT_verbose(p_zero,p_bat);
printf("\n");
}
dvbpsi_DeleteBAT(p_bat);
}
/*****************************************************************************
* NewSubtable
*****************************************************************************/
static void NewSubtableBAT(void * p_zero, dvbpsi_handle h_dvbpsi,
uint8_t i_table_id, uint16_t i_extension)
{
if(i_table_id == 0x4a)
{
dvbpsi_AttachBAT(h_dvbpsi, i_table_id, i_extension, DumpBAT, NULL);
}
}
/*****************************************************************************
* main
*****************************************************************************/
int main(int i_argc, char* pa_argv[])
{
int i_fd;
uint8_t data[188];
dvbpsi_handle h_dvbpsi;
int b_ok;
if(i_argc != 2)
return 1;
i_fd = open(pa_argv[1], 0);
h_dvbpsi = dvbpsi_AttachDemux(NewSubtableBAT, NULL);
b_ok = ReadPacket(i_fd, data);
while(b_ok)
{
uint16_t i_pid = ((uint16_t)(data[1] & 0x1f) << 8) + data[2];
if(i_pid == 0x11)
dvbpsi_PushPacket(h_dvbpsi, data);
b_ok = ReadPacket(i_fd, data);
}
dvbpsi_DetachDemux(h_dvbpsi);
return 0;
}
......@@ -16,6 +16,7 @@ libdvbpsi_la_LDFLAGS = -version-info 7:0:0
pkginclude_HEADERS = dvbpsi.h psi.h descriptor.h demux.h \
tables/pat.h tables/pmt.h tables/sdt.h tables/eit.h \
tables/cat.h tables/nit.h tables/tot.h tables/sis.h \
tables/bat.h \
descriptors/dr_02.h \
descriptors/dr_03.h \
descriptors/dr_04.h \
......@@ -84,5 +85,6 @@ tables_src = tables/pat.c tables/pat_private.h \
tables/cat.c tables/cat_private.h \
tables/nit.c tables/nit_private.h \
tables/tot.c tables/tot_private.h \
tables/sis.c tables/sis_private.h
tables/sis.c tables/sis_private.h \
tables/bat.c tables/bat_private.h
/*****************************************************************************
* bat.c: BAT decoder/generator
*----------------------------------------------------------------------------
* Copyright (C) 2001-2010 VideoLAN
* $Id: bat.c 110 2010-04-01 12:52:02Z gbazin $
*
* Authors: Zhu zhenglu <zhuzlu@gmail.com>
* heavily based on nit.c which was written by
* Johann Hanne
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*----------------------------------------------------------------------------
*
*****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif
#include "../dvbpsi.h"
#include "../dvbpsi_private.h"
#include "../psi.h"
#include "../descriptor.h"
#include "../demux.h"
#include "bat.h"
#include "bat_private.h"
/*****************************************************************************
* dvbpsi_AttachBAT
*****************************************************************************
* Initialize a BAT subtable decoder.
*****************************************************************************/
int dvbpsi_AttachBAT(dvbpsi_decoder_t * p_psi_decoder, uint8_t i_table_id,
uint16_t i_extension, dvbpsi_bat_callback pf_callback, void* p_cb_data)
{
dvbpsi_demux_t* p_demux = (dvbpsi_demux_t*)p_psi_decoder->p_private_decoder;
dvbpsi_demux_subdec_t* p_subdec;
dvbpsi_bat_decoder_t* p_bat_decoder;
unsigned int i;
if(dvbpsi_demuxGetSubDec(p_demux, i_table_id, i_extension))
{
DVBPSI_ERROR_ARG("BAT decoder",
"Already a decoder for (table_id == 0x%02x,"
"extension == 0x%02x)",
i_table_id, i_extension);
return 1;
}
p_subdec = (dvbpsi_demux_subdec_t*)malloc(sizeof(dvbpsi_demux_subdec_t));
if(p_subdec == NULL)
{
return 1;
}
p_bat_decoder = (dvbpsi_bat_decoder_t*)malloc(sizeof(dvbpsi_bat_decoder_t));
if(p_bat_decoder == NULL)
{
free(p_subdec);
return 1;
}
/* subtable decoder configuration */
p_subdec->pf_callback = &dvbpsi_GatherBATSections;
p_subdec->p_cb_data = p_bat_decoder;
p_subdec->i_id = (uint32_t)i_table_id << 16 | (uint32_t)i_extension;
p_subdec->pf_detach = dvbpsi_DetachBAT;
/* Attach the subtable decoder to the demux */
p_subdec->p_next = p_demux->p_first_subdec;
p_demux->p_first_subdec = p_subdec;
/* BAT decoder information */
p_bat_decoder->pf_callback = pf_callback;
p_bat_decoder->p_cb_data = p_cb_data;
/* BAT decoder initial state */
p_bat_decoder->b_current_valid = 0;
p_bat_decoder->p_building_bat = NULL;
for(i = 0; i < 256; i++)
p_bat_decoder->ap_sections[i] = NULL;
return 0;
}
/*****************************************************************************
* dvbpsi_DetachBAT
*****************************************************************************
* Close a BAT decoder.
*****************************************************************************/
void dvbpsi_DetachBAT(dvbpsi_demux_t * p_demux, uint8_t i_table_id,
uint16_t i_extension)
{
dvbpsi_demux_subdec_t* p_subdec;
dvbpsi_demux_subdec_t** pp_prev_subdec;
dvbpsi_bat_decoder_t* p_bat_decoder;
unsigned int i;
p_subdec = dvbpsi_demuxGetSubDec(p_demux, i_table_id, i_extension);
if(p_subdec == NULL)
{
DVBPSI_ERROR_ARG("BAT Decoder",
"No such BAT decoder (table_id == 0x%02x,"
"extension == 0x%02x)",
i_table_id, i_extension);
return;
}
p_bat_decoder = (dvbpsi_bat_decoder_t*)p_subdec->p_cb_data;
free(p_bat_decoder->p_building_bat);
for(i = 0; i < 256; i++)
{
if(p_bat_decoder->ap_sections[i])
dvbpsi_DeletePSISections(p_bat_decoder->ap_sections[i]);
}
free(p_subdec->p_cb_data);
pp_prev_subdec = &p_demux->p_first_subdec;
while(*pp_prev_subdec != p_subdec)
pp_prev_subdec = &(*pp_prev_subdec)->p_next;
*pp_prev_subdec = p_subdec->p_next;
free(p_subdec);
}
/*****************************************************************************
* dvbpsi_InitBAT
*****************************************************************************
* Initialize a pre-allocated dvbpsi_bat_t structure.
*****************************************************************************/
void dvbpsi_InitBAT(dvbpsi_bat_t* p_bat, uint16_t i_bouquet_id, uint8_t i_version,
int b_current_next)
{
p_bat->i_bouquet_id = i_bouquet_id;
p_bat->i_version = i_version;
p_bat->b_current_next = b_current_next;
p_bat->p_first_ts = NULL;
p_bat->p_first_descriptor = NULL;
}
/*****************************************************************************
* dvbpsi_EmptyBAT
*****************************************************************************
* Clean a dvbpsi_bat_t structure.
*****************************************************************************/
void dvbpsi_EmptyBAT(dvbpsi_bat_t* p_bat)
{
dvbpsi_bat_ts_t* p_ts = p_bat->p_first_ts;
dvbpsi_DeleteDescriptors(p_bat->p_first_descriptor);
p_bat->p_first_descriptor = NULL;
while(p_ts != NULL)
{
dvbpsi_bat_ts_t* p_tmp = p_ts->p_next;
dvbpsi_DeleteDescriptors(p_ts->p_first_descriptor);
free(p_ts);
p_ts = p_tmp;
}
p_bat->p_first_ts = NULL;
}
/*****************************************************************************
* dvbpsi_BATAddTS
*****************************************************************************
* Add a TS description at the end of the BAT.
*****************************************************************************/
dvbpsi_bat_ts_t *dvbpsi_BATAddTS(dvbpsi_bat_t* p_bat,
uint16_t i_ts_id, uint16_t i_orig_network_id)
{
dvbpsi_bat_ts_t * p_ts
= (dvbpsi_bat_ts_t*)malloc(sizeof(dvbpsi_bat_ts_t));
if(p_ts)
{
p_ts->i_ts_id = i_ts_id;
p_ts->i_orig_network_id = i_orig_network_id;
p_ts->p_next = NULL;
p_ts->p_first_descriptor = NULL;
if(p_bat->p_first_ts == NULL)
{
p_bat->p_first_ts = p_ts;
}
else
{
dvbpsi_bat_ts_t * p_last_ts = p_bat->p_first_ts;
while(p_last_ts->p_next != NULL)
p_last_ts = p_last_ts->p_next;
p_last_ts->p_next = p_ts;
}
}
return p_ts;
}
/*****************************************************************************
* dvbpsi_BATBouquetAddDescriptor
*****************************************************************************
* Add a descriptor in the BAT Bouquet descriptors (the first loop description),
* which is in the first loop of BAT.
*****************************************************************************/
dvbpsi_descriptor_t *dvbpsi_BATBouquetAddDescriptor(
dvbpsi_bat_t *p_bat,
uint8_t i_tag, uint8_t i_length,
uint8_t *p_data)
{
dvbpsi_descriptor_t * p_descriptor
= dvbpsi_NewDescriptor(i_tag, i_length, p_data);
if(p_descriptor)
{
if(p_bat->p_first_descriptor == NULL)
{
p_bat->p_first_descriptor = p_descriptor;
}
else
{
dvbpsi_descriptor_t * p_last_descriptor = p_bat->p_first_descriptor;
while(p_last_descriptor->p_next != NULL)
p_last_descriptor = p_last_descriptor->p_next;
p_last_descriptor->p_next = p_descriptor;
}
}
return p_descriptor;
}
/*****************************************************************************
* dvbpsi_BATTSAddDescriptor
*****************************************************************************
* Add a descriptor in the BAT TS descriptors, which is in the second loop of BAT.
*****************************************************************************/
dvbpsi_descriptor_t *dvbpsi_BATTSAddDescriptor(
dvbpsi_bat_ts_t *p_ts,
uint8_t i_tag, uint8_t i_length,
uint8_t *p_data)
{
dvbpsi_descriptor_t * p_descriptor
= dvbpsi_NewDescriptor(i_tag, i_length, p_data);
if(p_descriptor)
{
if(p_ts->p_first_descriptor == NULL)
{
p_ts->p_first_descriptor = p_descriptor;
}
else
{
dvbpsi_descriptor_t *p_last_descriptor = p_ts->p_first_descriptor;
while(p_last_descriptor->p_next != NULL)
p_last_descriptor = p_last_descriptor->p_next;
p_last_descriptor->p_next = p_descriptor;
}
}
return p_descriptor;
}
/*****************************************************************************
* dvbpsi_GatherBATSections
*****************************************************************************
* Callback for the subtable demultiplexor.
*****************************************************************************/
void dvbpsi_GatherBATSections(dvbpsi_decoder_t * p_psi_decoder,
void * p_private_decoder,
dvbpsi_psi_section_t * p_section)
{
dvbpsi_bat_decoder_t * p_bat_decoder
= (dvbpsi_bat_decoder_t*)p_private_decoder;
int b_append = 1;
int b_reinit = 0;
unsigned int i;
DVBPSI_DEBUG_ARG("BAT decoder",
"Table version %2d, " "i_table_id %2d, " "i_extension %5d, "
"section %3d up to %3d, " "current %1d",
p_section->i_version, p_section->i_table_id,
p_section->i_extension,
p_section->i_number, p_section->i_last_number,
p_section->b_current_next);
if(!p_section->b_syntax_indicator)
{
/* Invalid section_syntax_indicator */
DVBPSI_ERROR("BAT decoder",
"invalid section (section_syntax_indicator == 0)");
b_append = 0;
}
/* Now if b_append is true then we have a valid BAT section */
if(b_append)
{
/* TS discontinuity check */
if(p_psi_decoder->b_discontinuity)
{
b_reinit = 1;
p_psi_decoder->b_discontinuity = 0;
}
else
{
/* Perform a few sanity checks */
if(p_bat_decoder->p_building_bat)
{
if(p_bat_decoder->p_building_bat->i_bouquet_id != p_section->i_extension)
{
/* bouquet_id */
DVBPSI_ERROR("BAT decoder",
"'bouquet_id' differs"
" whereas no TS discontinuity has occured");
b_reinit = 1;
}
else if(p_bat_decoder->p_building_bat->i_version
!= p_section->i_version)
{
/* version_number */
DVBPSI_ERROR("BAT decoder",
"'version_number' differs"
" whereas no discontinuity has occured");
b_reinit = 1;
}
else if(p_bat_decoder->i_last_section_number !=
p_section->i_last_number)
{
/* last_section_number */
DVBPSI_ERROR("BAT decoder",
"'last_section_number' differs"
" whereas no discontinuity has occured");
b_reinit = 1;
}
}
else
{
if( (p_bat_decoder->b_current_valid)
&& (p_bat_decoder->current_bat.i_version == p_section->i_version))
{
/* Signal a new BAT if the previous one wasn't active */
if( (!p_bat_decoder->current_bat.b_current_next)
&& (p_section->b_current_next))
{
dvbpsi_bat_t *p_bat = NULL;
p_bat_decoder->current_bat.b_current_next = 1;
*p_bat = p_bat_decoder->current_bat;
p_bat_decoder->pf_callback(p_bat_decoder->p_cb_data, p_bat);
}
/* Don't decode since this version is already decoded */
b_append = 0;
}
}
}
}
/* Reinit the decoder if wanted */
if(b_reinit)
{
/* Force redecoding */
p_bat_decoder->b_current_valid = 0;