kopia lustrzana https://github.com/F5OEO/tstools
509 wiersze
13 KiB
C
509 wiersze
13 KiB
C
/*
|
||
* Test the PES reading facilities
|
||
*
|
||
* ***** BEGIN LICENSE BLOCK *****
|
||
* Version: MPL 1.1
|
||
*
|
||
* The contents of this file are subject to the Mozilla Public License Version
|
||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
* the License. You may obtain a copy of the License at
|
||
* http://www.mozilla.org/MPL/
|
||
*
|
||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
* for the specific language governing rights and limitations under the
|
||
* License.
|
||
*
|
||
* The Original Code is the MPEG TS, PS and ES tools.
|
||
*
|
||
* The Initial Developer of the Original Code is Amino Communications Ltd.
|
||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||
* the Initial Developer. All Rights Reserved.
|
||
*
|
||
* Contributor(s):
|
||
* Amino Communications Ltd, Swavesey, Cambridge UK
|
||
*
|
||
* ***** END LICENSE BLOCK *****
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
|
||
#include "compat.h"
|
||
#include "pes_fns.h"
|
||
#include "pidint_fns.h"
|
||
#include "misc_fns.h"
|
||
#include "ps_fns.h"
|
||
#include "ts_fns.h"
|
||
#include "es_fns.h"
|
||
#include "h262_fns.h"
|
||
#include "tswrite_fns.h"
|
||
#include "version.h"
|
||
|
||
/*
|
||
* Write out TS program data based on the information we have
|
||
*/
|
||
static int write_program_data_A(PES_reader_p reader,
|
||
TS_writer_p output)
|
||
{
|
||
// We know we support at most two program streams for output
|
||
int num_progs = 0;
|
||
u_int32 prog_pids[2];
|
||
byte prog_type[2];
|
||
int err;
|
||
u_int32 pcr_pid, pmt_pid;
|
||
|
||
if (reader->is_TS)
|
||
{
|
||
// For TS, we can use the stream types from the PMT itself
|
||
int number;
|
||
|
||
if (reader->video_pid != 0)
|
||
{
|
||
pmt_stream_p stream = pid_stream_in_pmt(reader->program_map,
|
||
reader->video_pid);
|
||
if (stream == NULL)
|
||
{
|
||
fprintf(stderr,"### Cannot find video PID %04x in program map\n",
|
||
reader->video_pid);
|
||
return 1;
|
||
}
|
||
prog_pids[0] = reader->output_video_pid; // may not be the same
|
||
prog_type[0] = stream->stream_type;
|
||
num_progs = 1;
|
||
pcr_pid = reader->video_pid;
|
||
}
|
||
if (reader->audio_pid != 0)
|
||
{
|
||
pmt_stream_p stream = pid_stream_in_pmt(reader->program_map,
|
||
reader->audio_pid);
|
||
if (stream == NULL)
|
||
{
|
||
fprintf(stderr,"### Cannot find audio PID %04x in program map\n",
|
||
reader->audio_pid);
|
||
return 1;
|
||
}
|
||
prog_pids[num_progs] = reader->output_audio_pid; // may not be the same
|
||
prog_type[num_progs] = stream->stream_type;
|
||
num_progs++;
|
||
}
|
||
pmt_pid = reader->pmt_pid;
|
||
}
|
||
else
|
||
{
|
||
// For PS, we have to be given appropriate PIDs, and we need to
|
||
// deduce stream types from the stream ids. Which, unfortunately,
|
||
// we can't do.
|
||
|
||
// For now, avoid the whole issue and just force some values...
|
||
num_progs = 1;
|
||
prog_pids[0] = 0x68; // hard-wired for video
|
||
prog_type[0] = MPEG2_VIDEO_STREAM_TYPE; // hard-wired for now
|
||
pcr_pid = 0x68;
|
||
|
||
if (reader->audio_stream_id != 0)
|
||
{
|
||
prog_pids[1] = 0x67; // hard-wired again
|
||
prog_type[1] = MPEG2_AUDIO_STREAM_TYPE; // a random guess
|
||
num_progs = 2;
|
||
}
|
||
pmt_pid = 0x66;
|
||
}
|
||
|
||
err = write_TS_program_data2(output,
|
||
1, // transport stream id
|
||
reader->program_number,
|
||
pmt_pid,pcr_pid,
|
||
num_progs,prog_pids,prog_type);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Error writing out TS program data\n");
|
||
return 1;
|
||
}
|
||
return 0;
|
||
|
||
}
|
||
|
||
/*
|
||
* Read PES packets and write them out to the target
|
||
*
|
||
* Returns 0 if all went well, 1 if an error occurred.
|
||
*/
|
||
static int play_pes_packets(PES_reader_p reader,
|
||
TS_writer_p output)
|
||
{
|
||
int err;
|
||
int ii;
|
||
int pad_start = 8;
|
||
int index = 0;
|
||
|
||
ES_p es; // A view of our PES packets as ES units
|
||
|
||
// Start off our output with some null packets - this is in case the
|
||
// reader needs some time to work out its byte alignment before it starts
|
||
// looking for 0x47 bytes
|
||
for (ii=0; ii<pad_start; ii++)
|
||
{
|
||
err = write_TS_null_packet(output);
|
||
if (err) return 1;
|
||
}
|
||
|
||
// Wrap our PES stream up as an ES stream
|
||
err = build_elementary_stream_PES(reader,&es);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Error trying to build ES reader from PES reader\n");
|
||
return 1;
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
h262_item_p item;
|
||
|
||
if (index % 500 == 0)
|
||
{
|
||
// Write out program data as we come to know it
|
||
err = write_program_data_A(reader,output);
|
||
if (err) return 1;
|
||
}
|
||
|
||
// Iterate our count here so that the first item is numbered 1
|
||
index++;
|
||
|
||
err = find_next_h262_item(es,&item);
|
||
if (err == EOF)
|
||
break;
|
||
else if (err)
|
||
{
|
||
fprintf(stderr,"### Error copying NAL units\n");
|
||
return err;
|
||
}
|
||
|
||
err = write_ES_as_TS_PES_packet(output,item->unit.data,
|
||
item->unit.data_len,DEFAULT_VIDEO_PID,
|
||
DEFAULT_VIDEO_STREAM_ID);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Error writing MPEG2 item\n");
|
||
return err;
|
||
}
|
||
|
||
free_h262_item(&item);
|
||
}
|
||
|
||
close_elementary_stream(&es);
|
||
return 0;
|
||
}
|
||
|
||
static int test1(PES_reader_p reader,
|
||
int verbose)
|
||
{
|
||
PES_packet_data_p packet;
|
||
int ii;
|
||
int err;
|
||
byte *old_data;
|
||
u_int32 old_data_len;
|
||
|
||
if (verbose)
|
||
printf("-------------------------- Test 1 --------------------------\n");
|
||
for (ii = 0; ii < 10; ii++)
|
||
{
|
||
err = read_next_PES_packet(reader);
|
||
if (err == EOF)
|
||
{
|
||
if (reader->give_info) printf("EOF\n");
|
||
break;
|
||
}
|
||
else if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
|
||
return 1;
|
||
}
|
||
packet = reader->packet;
|
||
if (verbose)
|
||
{
|
||
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
|
||
packet->posn,packet->data[3]);
|
||
print_stream_id(stdout,packet->data[3]);
|
||
printf(")\n");
|
||
print_data(stdout," Data",packet->data,packet->data_len,20);
|
||
|
||
err = report_PES_data_array("",packet->data,packet->data_len,FALSE);
|
||
if (err) return 1;
|
||
}
|
||
}
|
||
|
||
err = read_next_PES_packet(reader);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
|
||
return 1;
|
||
}
|
||
packet = reader->packet;
|
||
if (verbose)
|
||
{
|
||
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
|
||
packet->posn,packet->data[3]);
|
||
print_stream_id(stdout,packet->data[3]);
|
||
printf(")\n");
|
||
print_data(stdout," Data",packet->data,packet->data_len,20);
|
||
}
|
||
|
||
old_data = malloc(packet->data_len);
|
||
if (old_data == NULL)
|
||
{
|
||
fprintf(stderr,"### Error allocating data array\n");
|
||
return 1;
|
||
}
|
||
memcpy(old_data,packet->data,packet->data_len);
|
||
old_data_len = packet->data_len;
|
||
|
||
if (verbose)
|
||
printf("\n** Rewinding to the start of said packet again\n");
|
||
err = set_PES_reader_position(reader,packet->posn);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error seeking to previous PES packet\n");
|
||
free(old_data);
|
||
return 1;
|
||
}
|
||
|
||
if (verbose)
|
||
printf("** Reading packet the second time\n");
|
||
err = read_next_PES_packet(reader);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
|
||
free(old_data);
|
||
return 1;
|
||
}
|
||
packet = reader->packet;
|
||
if (verbose)
|
||
{
|
||
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
|
||
packet->posn,packet->data[3]);
|
||
print_stream_id(stdout,packet->data[3]);
|
||
printf(")\n");
|
||
print_data(stdout," Data",packet->data,packet->data_len,20);
|
||
}
|
||
if (packet->data_len != old_data_len)
|
||
{
|
||
fprintf(stderr,
|
||
"### Test1: first packet length %d, second packet length %d\n",
|
||
old_data_len,packet->data_len);
|
||
free(old_data);
|
||
return 1;
|
||
}
|
||
else if (memcmp(packet->data,old_data,packet->data_len))
|
||
{
|
||
fprintf(stderr,"### Test1: packet data differs\n");
|
||
print_data(stderr," Packet 1",old_data,old_data_len,50);
|
||
print_data(stderr," Packet 2",packet->data,packet->data_len,50);
|
||
free(old_data);
|
||
return 1;
|
||
}
|
||
if (verbose)
|
||
printf("------------------------------------------------------------\n");
|
||
|
||
// Even in a test it's a good idea to tidy up
|
||
free(old_data);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void print_usage()
|
||
{
|
||
printf(
|
||
"Usage: test_pes <input-file> <host>[:<port>]\n"
|
||
"\n"
|
||
);
|
||
REPORT_VERSION("test_pes");
|
||
printf(
|
||
"\n"
|
||
" Test the PES reading facilities. <input-file> should be a TS\n"
|
||
" (Transport Stream) or PS (Program Stream) file.\n"
|
||
"\n"
|
||
"Input:\n"
|
||
" <input-file> An H.222.0 TS or PS file.\n"
|
||
" <host> The host to which to write TS packets, over\n"
|
||
" TCP/IP. If <port> is not specified, it defaults\n"
|
||
" to 88.\n"
|
||
"\n"
|
||
"Switches:\n"
|
||
" -quiet, -q Suppress informational and warning messages.\n"
|
||
" -verbose, -v Output additional diagnostic messages\n"
|
||
" -noaudio Ignore any audio data\n"
|
||
" -nohost Don't try to connect to the host\n"
|
||
);
|
||
}
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
char *input_name = NULL;
|
||
char *output_name = NULL;
|
||
int had_input_name = FALSE;
|
||
int had_output_name = FALSE;
|
||
int port = 88; // Useful default port number
|
||
PES_reader_p reader = NULL;
|
||
TS_writer_p output = NULL;
|
||
int quiet = FALSE;
|
||
int verbose = FALSE;
|
||
int video_only = FALSE;
|
||
int want_output = TRUE;
|
||
|
||
int err;
|
||
int ii = 1;
|
||
|
||
if (argc < 2)
|
||
{
|
||
print_usage();
|
||
return 1;
|
||
}
|
||
while (ii < argc)
|
||
{
|
||
if (argv[ii][0] == '-')
|
||
{
|
||
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
|
||
!strcmp("-help",argv[ii]))
|
||
{
|
||
print_usage();
|
||
return 0;
|
||
}
|
||
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
|
||
{
|
||
quiet = TRUE;
|
||
verbose = FALSE;
|
||
}
|
||
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
|
||
{
|
||
verbose = TRUE;
|
||
quiet = FALSE;
|
||
}
|
||
else if (!strcmp("-noaudio",argv[ii]))
|
||
{
|
||
video_only = TRUE;
|
||
}
|
||
else if (!strcmp("-nohost",argv[ii]))
|
||
{
|
||
want_output = FALSE;
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr,"### test_pes: "
|
||
"Unrecognised command line switch '%s'\n",argv[ii]);
|
||
return 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (had_input_name && (want_output && had_output_name))
|
||
{
|
||
fprintf(stderr,"### test_pes: Unexpected '%s'\n",argv[ii]);
|
||
return 1;
|
||
}
|
||
else if (had_input_name && want_output)
|
||
{
|
||
err = host_value("test_pes",NULL,argv[ii],&output_name,&port);
|
||
if (err) return 1;
|
||
had_output_name = TRUE; // more or less
|
||
ii++;
|
||
}
|
||
else
|
||
{
|
||
input_name = argv[ii];
|
||
had_input_name = TRUE;
|
||
}
|
||
}
|
||
ii++;
|
||
}
|
||
|
||
if (!had_input_name)
|
||
{
|
||
fprintf(stderr,"### test_pes: No input file specified\n");
|
||
return 1;
|
||
}
|
||
if (want_output && !had_output_name)
|
||
{
|
||
fprintf(stderr,"### test_pes: No target host specified\n");
|
||
return 1;
|
||
}
|
||
|
||
err = open_PES_reader(input_name,!quiet,!quiet,&reader);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error opening file %s\n",input_name);
|
||
return 1;
|
||
}
|
||
|
||
if (!quiet)
|
||
printf("Opened file %s (as %s)\n",input_name,(reader->is_TS?"TS":"PS"));
|
||
|
||
set_PES_reader_video_only(reader,video_only);
|
||
|
||
if (want_output)
|
||
{
|
||
err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&output);
|
||
if (err)
|
||
{
|
||
(void) close_PES_reader(&reader);
|
||
fprintf(stderr,"### test_pes: Unable to connect to %s\n",output_name);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
err = test1(reader,verbose);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Test 1 failed\n");
|
||
(void) close_PES_reader(&reader);
|
||
(void) tswrite_close(output,TRUE);
|
||
return 1;
|
||
}
|
||
if (!quiet)
|
||
printf("** Test 1 passed\n"
|
||
"** Rewinding\n");
|
||
err = set_PES_reader_position(reader,0);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error seeking to previous PES packet\n");
|
||
return 1;
|
||
}
|
||
|
||
if (want_output)
|
||
{
|
||
err = play_pes_packets(reader,output);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error playing PES packets\n");
|
||
(void) close_PES_reader(&reader);
|
||
(void) tswrite_close(output,TRUE);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
if (want_output)
|
||
{
|
||
err = tswrite_close(output,quiet);
|
||
if (err)
|
||
fprintf(stderr,"### test_pes: Error closing output %s: %s\n",output_name,
|
||
strerror(errno));
|
||
}
|
||
err = close_PES_reader(&reader);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### test_pes: Error closing file %s\n",input_name);
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// Local Variables:
|
||
// tab-width: 8
|
||
// indent-tabs-mode: nil
|
||
// c-basic-offset: 2
|
||
// End:
|
||
// vim: set tabstop=8 shiftwidth=2 expandtab:
|