You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

337 lines
12 KiB

/*****************************************************************************
* ts_pes.c: MPEG PES assembly tests
*****************************************************************************
* Copyright (C) 2020 VideoLabs, VLC authors and VideoLAN
*
* This program 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 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <vlc_common.h>
#include <vlc_block.h>
#include "../../../modules/demux/mpeg/ts_streams.h"
#include "../../../modules/demux/mpeg/ts_pid_fwd.h"
#include "../../../modules/demux/mpeg/ts_streams_private.h"
#include "../../../modules/demux/mpeg/ts_pes.h"
#include "../../libvlc/test.h"
static void Parse(vlc_object_t *obj, void *priv, block_t *data,
uint32_t i_flags, stime_t t)
{
VLC_UNUSED(obj);
VLC_UNUSED(t);
block_t **pp_append = (block_t **) priv;
fprintf(stderr, "recv: ");
data = block_ChainGather(data);
data->i_flags = i_flags;
for(size_t i=0; i<data->i_buffer; i++)
fprintf(stderr, "%2.2x ", data->p_buffer[i]);
fprintf(stderr, "\n");
block_ChainAppend(pp_append, data);
}
#define RESET do {\
block_ChainRelease(output);\
output = NULL;\
block_ChainRelease(pes.gather.p_data);\
memset(&pes, 0, sizeof(pes));\
pes.transport = TS_TRANSPORT_PES;\
pes.gather.pp_last = &pes.gather.p_data;\
} while(0)
#define ASSERT(a) do {\
if(!(a)) { RESET; \
fprintf(stderr, "failed line %d\n", __LINE__); \
return 1; } \
} while(0)
#define PKT_FROMSZ(a, b) do {\
pkt = block_Alloc(sizeof(a) + b);\
ASSERT(pkt);\
memcpy(pkt->p_buffer, a, sizeof(a));\
for(size_t i=1; i<1+b;i++)\
pkt->p_buffer[sizeof(a) + i] = i % 0xFF;\
} while(0)
#define PKT_FROM(a) PKT_FROMSZ(a, 0)
int main(void)
{
block_t *pkt;
block_t *output = NULL;
int outputcount = 0;
size_t outputsize = 0;
test_init();
ts_pes_parse_callback cb =
{
.p_obj = NULL,
.priv = &output,
.pf_parse = Parse
};
ts_stream_t pes;
memset(&pes, 0, sizeof(pes));
pes.transport = TS_TRANSPORT_PES;
pes.gather.pp_last = &pes.gather.p_data;
/* filler packet */
const uint8_t dummy0[] = {
0xFF,
};
/* General case, aligned payloads */
/* payload == 0 */
const uint8_t aligned0[] = {
0x00, 0x00, 0x01, 0xe0, 0x00, 0x03, 0x80, 0x00, 0x00,
};
PKT_FROM(aligned0);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == TS_PES_HEADER_SIZE+3);
ASSERT(!memcmp(aligned0, output->p_buffer, outputsize));
RESET;
/* no output if not unit start */
PKT_FROM(aligned0);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, false, true, 0));
ASSERT(!output);
RESET;
/* no output if not unit start */
PKT_FROM(aligned0);
pkt->i_buffer = 1;
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, false, true, 0));
ASSERT(!output);
RESET;
/* payload == 6 */
const uint8_t aligned1[] = {
0x00, 0x00, 0x01, 0xe0, 0x00, 0x09, 0x80, 0x00, 0x00,
0xAA, 0xBB, 0xAA, 0xBB, 0xAA, 0xBB,
};
PKT_FROM(aligned1);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == TS_PES_HEADER_SIZE+3+6);
ASSERT(!memcmp(aligned1, output->p_buffer, outputsize));
RESET;
/* no output if not unit start */
PKT_FROM(aligned1);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, false, true, 0));
ASSERT(!output);
RESET;
/* payload == 30, uncomplete */
PKT_FROM(aligned1);
SetWBE(&pkt->p_buffer[4], 30);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
RESET;
/* packets assembly, exact size, payload > 150 - TS_PES_HEADER_SIZE - 4 */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 250);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 256);
PKT_FROMSZ(dummy0, 106-sizeof(dummy0));
ASSERT(ts_pes_Gather(&cb, &pes, pkt, false, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == 256);
RESET;
/* packets assembly, incorrect size, overflow by %15 pkt loss or size field corruption */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 250);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 256);
PKT_FROMSZ(dummy0, 150-sizeof(dummy0));
ASSERT(ts_pes_Gather(&cb, &pes, pkt, false, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == 300);
RESET;
/* no packets assembly from unit start, early termination by fixed size */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 250);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 256);
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 2);
RESET;
/* no packets assembly from unit start, early termination by undef size */
PKT_FROMSZ(aligned1, 188-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 250);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 256);
PKT_FROMSZ(aligned1, 188-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 0);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == 188);
RESET;
/* packets assembly, payload undef, use next sync code from another payload undef */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 0);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 0);
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 0);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == 150);
RESET;
/* packets assembly, payload undef, use next sync code from fixed size */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 0);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 0);
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 2); /* secondary */
RESET;
/* packets assembly, payload undef, use next sync code from fixed size but uncomplete */
PKT_FROMSZ(aligned1, 150-sizeof(aligned1));
SetWBE(&pkt->p_buffer[4], 0);
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(!output);
ASSERT(pes.gather.i_data_size == 0);
PKT_FROM(aligned1);
pkt->i_buffer = 6;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1); /* can't output */
PKT_FROM(aligned1);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, false, true, 0)); /* add data for last output */
ASSERT(output); /* output */
RESET;
const uint8_t aligned2[] = {
0x00, 0x00, 0x01, 0xe0, 0x00, 0x03, 0x80, 0x00, 0x00,
0x00, 0x00, 0x01, 0xe0, 0x00, 0x07, 0x80, 0x00, 0x00, /* PES 0xdb header */
0x00, 0x01, 0x02, 0x03, /* PES payload */
0x00, 0x00, 0x01, 0xe0, 0x00, 0x07, 0x80, 0x00, 0x00, /* PES 0xdb header */
0xAA, 0xBB, 0xCC, 0xDD, /* PES payload */
};
/* If the payload_unit_start_indicator is set to '1', then one and only one
* PES packet starts in this transport stream packet. */
PKT_FROM(aligned2);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
RESET;
/* Broken PUSI tests */
pes.b_broken_PUSI_conformance = true;
PKT_FROM(aligned2);
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 3);
RESET;
pes.b_broken_PUSI_conformance = true;
PKT_FROM(aligned2);
pkt->p_buffer[0] = 0xFF;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 2);
RESET;
for(int split=12; split>9; split--)
{
pes.b_broken_PUSI_conformance = true;
PKT_FROM(aligned2);
pkt->i_buffer = split;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
PKT_FROM(aligned2);
pkt->p_buffer += split;
pkt->i_buffer -= split;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
RESET;
}
/* PKT FLAGS */
PKT_FROM(aligned1);
pkt->i_buffer -= 1;
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
PKT_FROM(aligned1);
pkt->i_flags |= BLOCK_FLAG_DISCONTINUITY;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 2);
ASSERT(outputsize == sizeof(aligned1) * 2 - 1);
ASSERT(output->i_flags & BLOCK_FLAG_CORRUPTED); /* First block is corrupted 15/16 bytes */
ASSERT((output->i_flags & BLOCK_FLAG_DISCONTINUITY) == 0);
ASSERT(output->p_next->i_flags == BLOCK_FLAG_DISCONTINUITY); /* Next block resumes as discont */
RESET;
PKT_FROM(aligned1);
SetWBE(&pkt->p_buffer[4], 0); /* Set 0 sized to prevent immediate output */
ASSERT(!ts_pes_Gather(&cb, &pes, pkt, true, true, 0));
PKT_FROM(aligned1);
SetWBE(&pkt->p_buffer[4], 0);
pkt->i_flags |= BLOCK_FLAG_DISCONTINUITY;
ASSERT(ts_pes_Gather(&cb, &pes, pkt, true, true, 0)); /* should output on sync||discont */
ASSERT(output);
block_ChainProperties(output, &outputcount, &outputsize, NULL);
ASSERT(outputcount == 1);
ASSERT(outputsize == sizeof(aligned1));
//ASSERT(output->i_flags == BLOCK_FLAG_CORRUPTED); /* First might be corrupted but we don't really know */
RESET;
return 0;
}