This blog is only to remind myself of what I've learned (from others or by myself) and only for knowledge sharing. External sources will be clearly stated in [Reference] of each article. I hope this blog won't infringe any copyrights and that it can be useful for interested blog readers.

顯示具有 DVB-H 標籤的文章。 顯示所有文章
顯示具有 DVB-H 標籤的文章。 顯示所有文章

2008年7月5日 星期六

DirectShow a/v sync notes

Preface

Reference DVB-H CRTPSourceFilter source code

Direct Show

!!NOTES:

when checking a/v sync, there are three cases to be tested.

a) From no signal to having signal (e.g. Mute->unMute)

b) fast-backward/re-wind, fast-forward (DISCONTINUITY)

c) Device lost (ex: Ctrl-Alt-Del)

4) buffering mechansim

1) a/v PTS

Real a/v PTS should come from a source filter,

ex: FileSourceFilter, DVBHRTPSourceFilter

a/v PTS should be used in MediaSample::SetTime(..) and

MediaSample::SetSyncPoint(..) with 100ns-based unit.


Pseudo code:

HRESULT CRTPSourcePin::FillBuffer(IMediaSample *pSample) {

// Notes:

// (1) pSample may be audio or video samples.

// (2) m_rtTimeBase and pPacket->pts are micro-second based.

1 micro-sec = 1000 nano-sec.

rtStart = (m_pSourceFilter->m_rtTimeBase + pPacket->pts) * 10 ;
rtEnd = rtStart + 1 ;

pSample->SetTime( &rtStart, &rtEnd );
pSample->SetSyncPoint( TRUE ) ;

if ( pPacket->pts > m_pFilter->m_rtCurrMaxPTS) {
m_pFilter->m_rtCurrMaxPTS = pPacket->pts;
}

}

!!NOTES:

MediaSample PTS value has to always move upward.

Otherwise, there might be frame drops.

2) DirectShow System Clock

When running a DirectShow Graph, System clock will also move forward

together with MediaSample PTS.


There will be following different cases

a) When PTS of MediaSample is equal to or bigger than System Clock

Audio MedisaSample PTS

|----------------|-------------|----------------------

100 200 300 or 500 (meant to be assigned)

Video MediaSample PTS

|----------------|-------------|-----------------------

100 200 300 or 500 (meant to be assigned)

System clock

|----------------|-------------|-----------------------

1000 1100 1200

Result: a/v will be in sync as long as PTS for MediaSample is assigned synchronously for a/v.


b) The speed of MediaSample is slower than System Clock

Audio MedisaSample PTS

|----------------|-------------|----------------------

100 200 210 (meant to be assigned)

Video MediaSample PTS

|----------------|-------------|-----------------------

100 200 210 (meant to be assigned)

System clock

|----------------|-------------|-----------------------

1000 1100 1200

Result:

might have video frame drops even though a/v PTS is in sync

because System Clock runs faster


3) Discontinuity

When discontinuity happens, what should be done

a) Use the last MAX PTS as the next PTS base

(ps: this is to make sure PTS value is moving upward)

b) Calculate how many seconds are passed between discontinuity event and

the last packet PTS

(ps: this is to make sure the following MedisaSample PTS value still

follows System Clock speed)

Pseudo code:

s = GetMediaPacket(...);

if (s == REAL_PACKET) {

lastPacket_tickCount = GetTickCount();

} else if (s == DISCONTINUITY) {

m_pSourceFilter->m_rtTimeBase += pRTPSourceFilter->m_rtCurrMaxPTS;

// extra seconds to add

m_pSourceFilter->m_rtTimeBase += (GetTickCount() - lastPacket_tickCount ) *1000;
m_pSourceFilter->m_rtCurrMaxPTS = 0;

}

 

4) Buffering mechanism

When it comes to DVB-T, buffering may not be needed.

But when it comes to DVB-H burst streaming, deciding how many packets should be buffered

in advance is very important because it will affect the playback quality.

2008年2月20日 星期三

重點整理 -- converting to libpcap file format

Preface

Reference original web link


[lwlan-devel] converting to libpcap file format

parul singla parulsingla at hotmail.com
Wed Sep 3 01:35:53 EDT 2003
Hi,
Earlier I had written a tool in VC++ which composes 802.11 frames and stores
them as ".dat" files. I wish to read/parse/decompose them using Ethereal.
But, I have to first convert the ".dat" files into ethereal readable format
(one of them is libpcap).

Somewhere I read on this mailing list (which goes like this):

/************************************

libpcap format has a file header, followed by the packets, with each
packet consisting of a packet header followed immediately by the data in
the packet, with no padding between the file header and the first packet
header, the packet header and the packet data, or the packet data and
the header of the next packet, if any.

The file header consists of, in order:

a 32-bit "magic number";

a 16-bit major version number;

a 16-bit minor version number;

a 32-bit "time zone offset" field that's actually not used, so
you can (and probably should) just make it 0;

a 32-bit "time stamp accuracy" field that's not actually used,
so you can (and probably should) just make it 0;

a 32-bit "snapshot length" field;

a 32-bit "link layer type" field.

The magic number has the value hex a1b2c3d4. All the fields can be
written in either big-endian or little-endian format; the magic number
is one of those fields, so the program reading the file (tcpdump,
Ethereal, whatever) can infer from that fields value, when it reads it,
whether the file was written in the same byte order as the native byte
order of the machine reading the file or in the opposite byte order, and
can byte-swap the values if they're written in the opposite byte order
(both libpcap, the library tcpdump and many other programs use to read
those files, and the library Ethereal and the programs that come with it
use to read the file, do so).

All numbers in the headers are usually written in the byte order of the
processor on whatever device is saving the frames.

The major version number should have the value 2.

The minor version number should have the value 4.

The "time zone offset" and "time stamp accuracy" fields should both be
zero.

The "snapshot length" field should be the maximum number of bytes per
packet that will be captured. If the entire packet is captured, make it
65535; if you only capture, for example, the first 64 bytes of the
packet, make it 64.

The link-layer type depends on the type of link-layer header that the
packets in the capture file have:

0 BSD loopback devices, except for later OpenBSD
1 Ethernet, and Linux loopback devices
6 802.5 Token Ring
7 ARCnet
8 SLIP
9 PPP
10 FDDI
100 LLC/SNAP-encapsulated ATM
101 "raw IP", with no link
102 BSD/OS SLIP
103 BSD/OS PPP
104 Cisco HDLC
105 802.11
108 later OpenBSD loopback devices (with the AF_
value in network byte order)
113 special Linux "cooked" capture
114 LocalTalk

If you need a new type for a new link-layer header, send mail to
tcpdump-workers at tcpdump.org asking for one; do *not* pick one yourself,
as you may pick one that's already in use, or reserved for future use.

Immediately following that header are the actual frames.

Each frame consists of a frame header followed by the raw bytes of the
frame.

The frame header consists of:

a time stamp, consisting of:
    (!!NOTES: Mark: 4 bytes here)
a UNIX-format time-in-seconds when the packet was
captured, i.e. the number of seconds since January 1,
1970, 00:00:00 GMT (that's GMT, *NOT* local time!);
    (!!NOTES: Mark: 4 bytes here)
the number of microseconds since that second when the
packet was captured;

a 32-bit value giving the number of bytes of packet data that
were captured;

a 32-bit value giving the actual length of the packet, in bytes
(which may be greater than the previous number, if you're not
saving the entire packet).

All those numbers must be in the same byte order as the numbers in the
file header.

***************************************************************/

So my question is if I prepend my 802.11 frames which i composed earlier by
this file header and each frame by the header it mentions.. will ethereal be
able to decompose the frames?

If there is any better approach to do this, lemme know.

Thanks in advace,


=====================================================================


Example: convert IP packets to libpcap format file

!!NOTES: PCAP binary pseudo code => refer to TSParser
    // prepare pcap_header
unsigned char pcap_header[24];
pcap_header[0] = 0xD4; // magic number = 0xA1B2C3D4
pcap_header[1] = 0xC3;
pcap_header[2] = 0xB2;
pcap_header[3] = 0xA1;
pcap_header[4] = 0x02; // major version number, must be 2
pcap_header[5] = 0x00;
pcap_header[6] = 0x04; // minor version number, must be 4
pcap_header[7] = 0x00;
pcap_header[8] = 0x00; // time zone offset
pcap_header[9] = 0x00;
pcap_header[10] = 0x00;
pcap_header[11] = 0x00;
pcap_header[12] = 0x00; // time stamp accuracy
pcap_header[13] = 0x00;
pcap_header[14] = 0x00;
pcap_header[15] = 0x00;
pcap_header[16] = 0xFF; // snapshot field
pcap_header[17] = 0xFF;
pcap_header[18] = 0x00;
pcap_header[19] = 0x00;
pcap_header[20] = 0x01; // link layer type field, 0x01 => Ethernet
pcap_header[21] = 0x00;
pcap_header[22] = 0x00;
pcap_header[23] = 0x00;
    // prepare frame_header
unsigned char fake_frame_header[16];
fake_frame_header[0] = 0xB8; // 0x43D10BB8 seconds since 1970, 0:0:0 GMT
fake_frame_header[1] = 0x0B;
fake_frame_header[2] = 0xD1;
fake_frame_header[3] = 0x43;
fake_frame_header[4] = 0x95; // 0x000CCC95 microseconds when captured
fake_frame_header[5] = 0xCC;
fake_frame_header[6] = 0x0C;
fake_frame_header[7] = 0x00;
    // prepare ethernet_header
unsigned char fake_ethernet_header[14];
fake_ethernet_header[0] = 0x01; // destination
fake_ethernet_header[1] = 0x00;
fake_ethernet_header[2] = 0x5E;
fake_ethernet_header[3] = 0x06;
fake_ethernet_header[4] = 0x07;
fake_ethernet_header[5] = 0x0B;
fake_ethernet_header[6] = 0x00; // source
fake_ethernet_header[7] = 0x14;
fake_ethernet_header[8] = 0x22;
fake_ethernet_header[9] = 0xB0;
fake_ethernet_header[10] = 0xB1;
fake_ethernet_header[11] = 0x63;
    // Type
if (isIPV4) {
fake_ethernet_header[12] = 0x08; // 0x0800 -> ipv4
fake_ethernet_header[13] = 0x00;
} else if (isIPV6) {
fake_ethernet_header[12] = 0x86; // 0x86DD ->ipv6
fake_ethernet_header[13] = 0xDD;
}
    for (int i = 0; i < IP_PACKET_DATA_COUNT; ++i) {
        if (i == 0) {
// WRITE libpcap header first to file
fwrite(pcap_header, 1, 24, pFileToWrite);

}
         // add frame header ==> ethernet header length + ip:total_length
unsigned long frame_header_len = 14(ETHERNET-HEADER-LEN) + <IP-TOTAL-LENGTH>;
         // # of packet bytes
*((unsigned long*)(fake_frame_header + 8)) = frame_header_len;
// the actual length of packets in bytes
*((unsigned long*)(fake_frame_header + 12)) = frame_header_len;
         fwrite(fake_frame_header, 1, 16, pFileToWrite);
         // add ethernet header
fwrite(fake_ethernet_header, 1, 14, pFileToWrite);

// add ip header + payload
fwrite(<IP-PACKET-START-ADDRESS>, 1, IP-TOTAL-LENGTH, pFileToWrite);
    }
fclose(pFileToWrite);

DVB-H PID-to-IP mapping TS parsing -- part IV

Preface

Reference
  • ETSI_EN_301_192 (en_301192v010401p.pdf)
  • ETSI_EN_300_468 (en_300468v010601p.pdf)
  • ISO_IEC_138181-1
  • a079r1.tm3025r2.cbms1164r.IPDC_PSI-SI.pdf


1) datagram_section

2) How to get IP datagram from a certain PID

after PID-IP-mapping info is found (refer to partIII), we can use

"Bully method" to get all IP datagram from a certain PID.

a) Focus on a certain PID (say PID=1000)

b) get all TS packets with PID = 1000

c) try to assemble a section from (b)

d) check if the assembled section contains table_id = 0x3e, which means

datagram_section

e) if it isn't, drop it and repeat (b). If it is, continue with (f)

f) start parsing datagram_section

!!NOTES:

There might be several different IP datagram (IP:port) from a certain PID.

ex: PID=1000 may contain 225.1.1.1:5000, 225.1.1.1:5001, 225.1.1.1:5002, 225.1.1.1:5003

When parsing, it's better to save them in different files so as to easily identify

what those IP packet might be.

(ex: Flute packets for ESG bootstrap or ESG,
RTP_video, RTP_audio, RTCP_video, RTCP_audio)

DVB-H PID-to-IP mapping TS parsing -- part III

Preface

Reference
  • ETSI_EN_301_192 (en_301192v010401p.pdf)
  • ETSI_EN_300_468 (en_300468v010601p.pdf)
  • ISO_IEC_138181-1
  • a079r1.tm3025r2.cbms1164r.IPDC_PSI-SI.pdf


1) This is the main flow for DVB-H PID-IP parsing.

The following steps are based on this step.

Ref -- 8.4.2 of EN 301 192

2) Explanations

[NIT] -- Ref -8.4.2 of EN 301 192

1) linkage_descriptor (8.2.1 of EN 301 192) of NIT_FOR_LOOP_1contains (multiple) DVB-H platform info.

2) terrestrial_delivery_system_descriptor (6.2.13.3 of EN 301 192) of NIT for_LOOP_2contains (multiple) TS parameter

[PAT]

program_map_ID of PAT_FOR_LOOP_1 is used to search for PMT PIDs.

[PMT]

1) program_number of PMT is unique to identify PMT

2) If PMT_FOR_LOOP_2_FOR_LOOP_1 contains data_broadcast_id_descriptor(tag = 0x66)

and the data_broadcast_id of the data_broadcast_id_descriptor is 0xb

(which is IPMAC_NOTIFICATION_INFO), then an INT is existed.

This INT can be found in the PID equal to pPMT_FOR_LOOP_2->elementary_map_PID and

it belongs to the platform that has the same platform id equal to platform_id of IPMAC_notification_info_FOR_LOOP_1

[INT] -- Ref -- 8.4.2 of EN 301 192

1) Check each operational_descriptor of OPERATIONAL_DESCRIPTOR_LOOP_FOR_LOOP_1 from pINT_FOR_LOOP_1->p_operational_descriptor_loop->vec_ OPERATIONAL_DESCRIPTOR_LOOP_FOR_LOOP_1

1.a) if it is IPMAC_stream_location_descriptor (tag = 0x13),

the service id of IPMAC_stream_location_descriptor is used to map PMT_program_number in a certain PMT and

the component_tag of IPMAC_stream_location_descriptor is used to map component_tag of

stream_identifier_descriptor (tag = 0x52) from the PMT_FOR_LOOP_2_FOR_LOOP_1.

if both component_tags are matched, the target PID equal to PMT_FOR_LOOP_2->elementary_map_PID contains stream info (IP info)

All IP info belonging to the target PID can be found in target_IP_address_descriptor(tag=0x09), target_IP_slash_descriptor(0x0f),

target_IPv6_address_descriptor (0x0a) or target_IPv6_slash_descriptor(0x11) from the target_descriptor of TARGET_DESCRIPTOR_LOOP_FOR_LOOP_1 in

pINT_FOR_LOOP_1->p_target_descriptor_loop->vec_TARGET_DESCRIPTOR_LOOP_FOR_LOOP_1


1.b) if it is time_slice_fec_identifier_descriptor (tag = 0x77), there will be info such as mpe-fec, burst duration, etc.


DVB-H PID-to-IP mapping TS parsing -- part II

Preface

Reference
  • ETSI_EN_301_192 (en_301192v010401p.pdf)
  • ETSI_EN_300_468 (en_300468v010601p.pdf)
  • ISO_IEC_138181-1
  • a079r1.tm3025r2.cbms1164r.IPDC_PSI-SI.pdf


1) This is the main flow for DVB-H PID-IP parsing.

The following steps are based on this step.

Ref -- 8.4.2 of EN 301 192

2) NIT and related descriptors

Ref -- 8.4.2 of EN 301 192 -- NIT

Ref -- 8.2.1 of EN 301 192 -- linkage_descriptor

Ref -- 6.2.13.3 of EN 301 192 -- terrestrial_delivery_system_descriptor

3) PAT

Ref -- ISO_IEC_138181-1 -- table id

Ref -- ISO_IEC_138181-1 -- PAT

4) PMT and related descriptors

Ref -- ISO_IEC_138181-1 -- PMT

Ref -- 6.2.12 of ETSI_EN 300 468 -- data_broadcast_id_descriptor

Ref -- 8.3.1 of EN 300 192 - IPMAC_NOTIFICATION_INFO

5) INT and related descriptors

Ref -- 8.4.4 of EN 301 192 -- INT

Ref -- 8.4.4.4 of EN 301 192 -- operational_descriptor

Ref -- 8.4.5.14 of EN 301 192 -- IPMAC_stream_location_descriptor

Ref -- 8.4.5.8 of EN 301 192 -- target_IP_address_descriptor

Ref -- 8.4.5.9 of EN 301 192 -- target_IP_slash_descriptor

Ref -- 8.4.5.11 of EN 301 192 -- target_IPv6_address_descriptor

Ref -- 8.4.5.12 of EN 301 192 -- target_IPv6_slash_descriptor

Ref -- 9.5 of EN 301 192 -- time_slice_fec_identifier_descriptor

6) datagram_section

Ref -- 7.1 of EN 301 192 -- datagram_section





DVB-H PID-to-IP mapping TS parsing -- part I

 

Preface

Reference
  • ISO_IEC_138181-1 
  • a PPT slide from Korea

 

TS packet header figure