I need to detect MPEG4 I-Frame in RTP packet. I know how to remove RTP header and get the MPEG4 frame in it, but I can't figure out how to identify the I-Frame.
Does it have a specific signature/header?
Ok so I figured it out for h264 stream.
How to detect I-Frame:
remove RTP header
check the value of the first byte in h264 payload
if the value is 124 (0x7C) it is an I-Frame
I cant figure it out for the MPEG4-ES stream... any suggestions?
EDIT: H264 IDR
This works for my h264 stream (fmtp:96 packetization-mode=1; profile-level-id=420029;). You just pass byte array that represents the h264 fragment received through RTP. If you want to pass whole RTP, just correct the RTPHeaderBytes value to skip RTP header. I always get the I-Frame, because it is the only frame that can be fragmented, see here. I use this (simplified) piece of code in my server, and it works like a charm!!!! If the I-Frame (IDR) is not fragmented, the fragment_type would be 5, so this code would return true for the fragmented and not fragmented IDRs.
public static bool isH264iFrame(byte[] paket)
{
int RTPHeaderBytes = 0;
int fragment_type = paket[RTPHeaderBytes + 0] & 0x1F;
int nal_type = paket[RTPHeaderBytes + 1] & 0x1F;
int start_bit = paket[RTPHeaderBytes + 1] & 0x80;
if (((fragment_type == 28 || fragment_type == 29) && nal_type == 5 && start_bit == 128) || fragment_type == 5)
{
return true;
}
return false;
}
Here's the table of NAL unit types:
Type Name
0 [unspecified]
1 Coded slice
2 Data Partition A
3 Data Partition B
4 Data Partition C
5 IDR (Instantaneous Decoding Refresh) Picture
6 SEI (Supplemental Enhancement Information)
7 SPS (Sequence Parameter Set)
8 PPS (Picture Parameter Set)
9 Access Unit Delimiter
10 EoS (End of Sequence)
11 EoS (End of Stream)
12 Filter Data
13-23 [extended]
24-31 [unspecified]
EDIT 2: MPEG4 I-VOP
I forgot to update this... Thanx to Che and ISO IEC 14496-2 document, I managed to work this out! Che was rite, but not so precise in his answer... so here is how to find I, P and B frames (I-VOP, P-VOP, B-VOP) in short:
VOP (Video Object Plane -- frame) starts with a code 000001B6(hex). It is the same for all MPEG4 frames (I,P,B)
Next follows many more info, that I am not going to describe here (see the IEC doc), but we only (as che said) need the higher 2 bits from the following byte (next two bits after the byte with the value B6). Those 2 bits tell you the VOP_CODING_TYPE, see the table:
VOP_CODING_TYPE (binary) Coding method
00 intra-coded (I)
01 predictive-coded (P)
10 bidirectionally-predictive-coded (B)
11 sprite (S)
So, to find I-Frame find the packet starting with four bytes 000001B6 and having the higher two bits of the next byte 00. This will find I frame in MPEG4 stream with a simple video object type (not sure for advanced simple).
For any other problems, you can check the document provided (ISO IEC 14496-2), there is all you want to know about MPEG4. :)
As far as I know, MPEG4-ES stream fragments in RTP payload usually start with MPEG4 startcode, which can be one of these:
0x000001b0: visual_object_sequence_start_code (probably keyframe)
0x000001b6: vop_start_code (keyframe, if the next two bits are zero)
0x000001b3: group_of_vop_start_code, which contains three bytes and then hopefully a vop_start_code that may or may not belong to a keyframe (see above)
0x00000120: video_object_layer_start_code (probably keyframe)
0x00000100-0x0000011f: video_object_start_code (those look like keyframes as well)
something else (probably not a keyframe)
I'm afraid that you'll need to parse the stream to be sure :-/
Actually, you was correct for h264 stream, if the NAL value (first byte) is 0x7C it means that the I-Frame is fragmented. No other frames (P and B) can be fragmented, so if there is packetization-mode=1 in SDP, then it means that the I-Frames are fragmented, and therefore if you read 0x7C as first byte, then it is I-Frame. Read more here: http://www.rfc-editor.org/rfc/rfc3984.txt.
This worked for me:
- Figure out the "payload type", for example: Payload type: DynamicRTP-Type-96 (96)
- Tell wireshark which stream is H264: File->preferences->protocols->H264. Enter 96 as payload type.
- Filter on slice_type:"h264.slice_type eq 7"
For H264:
Remove RTP header.
If chunk NAL type (in first byte) is SPS (7) or PPS (8) mark the frame as IFrame (many cameras not use SPS and PPS (Axis included)).
If chunk NAL type is #28 FU A (fragmentation unit A), check FU Header (next byte) if is NAL type IDR (5) (IDR (Instantaneous Decoding Refresh) Picture) is an IFrame.
Examples:
nal_ref_idc: 3, nal type: 7 (0x07) descripcion: 7 (SPS)<br>
00000000 24 00 00 2B 80 60 22 ED 96 57 3E 68 57 F3 22 B5 $..+.`"í.W>hWó"µ<br>
00000010 67 64 00 1E AD 84 01 0C 20 08 61 00 43 08 02 18 gd..... .a.C...
00000020 40 10 C2 00 84 2B 50 5A 09 34 DC 04 04 04 08 #.Â..+PZ.4Ü....<br>
nal_ref_idc: 3, nal type: 8 (0x08) descripcion: 8 (PPS)<br>
00000000 24 00 00 10 80 60 22 EE 96 57 3E 68 57 F3 22 B5 $....`"î.W>hWó"µ
00000010 68 EE 3C B0 hî<°
FU_A (fragmentation unit A)
nal_ref_idc: 3, nal type: 5 (0x05) descripcion: 5 (IDR (Instantaneous Decoding Refresh) Picture)
00000000 24 00 05 96 80 60 22 F1 96 57 3E 68 57 F3 22 B5 $....`"ñ.W>hWó"µ
00000010 7C 05 A0 AA 2F 81 92 AB CA FE 9E 34 D8 06 AD 74 |. ª/..«Êþ.4Ø.t
...
0x000001b6: vop_start_code (keyframe, if the next two bits are zero)
this is correct way for MPEG-4
Related
I'm trying to understand how a protocol works, it's from a TEC-Microsystem device (DX5100), it says:
CRC: Byte of the control sum CRC-8. It can be absent in some options
of the protocol. The control sum CRC-8 is calculated before the
stuffing for the entire packet, beginning with the byte FEND and
finishing with the last databyte. If a packet transmits an address,
when calculating the control sum, its true value is used, i.e. MSB=1
is not taken into account. For the calculation of the control sum the
polynomial is used. CRC = X8 + X5 + X4 + 1.
When I sniff the data being sent by their software, I see this data being transmitted:
0xC0 0x81 0x04 0x02 0x02 0x00 0x55
If a packet transmits an address,
when calculating the control sum, its true value is used, i.e. MSB=1
is not taken into account
This means that the data taken into account to compute the CRC is actually 0xC0 0x01 0x04 0x02 0x02 0x00 (second byte is 0x01 instead of 0x81).
According to what I could find on wikipedia, "CRC = X8 + X5 + X4 + 1" means they use "CRC-8-Dallas/Maxim".
However, when I use https://crccalc.com/, enter C00104020200 and hit "CALC-CRC-8" it reports 0x82 for "CRC-8/MAXIM", not 0x55. Am I missing something?
More examples from the sniffer:
C0 81 03 02 02 00 D3, so C0 01 03 02 02 00 CRC is D3
C0 81 05 02 02 00 DA, so C0 01 05 02 02 00 CRC is DA
With two examples, you can XOR them, which eliminates initial value and final xor, as if both were 00:
C0 01 03 02 02 00 CRC is D3
C0 01 05 02 02 00 CRC is DA
---------------------------
00 00 06 00 00 00 CRC is 09
This confirms that the CRC polynomial is 0x31 (reversed to 0x8C), input reflected, result reflected.
Using initial value 0xDE didn't work, so I tried reversing the bits to 0x7B which works for the three examples in the question. So initial value == 0x7B, the polynomial will also be bit reversed from 0x31 to 0x8C, but the online calculator uses the non-reversed polynomial, 0x31. If you click on "show reflected lookup table", calculate CRC, then look at row 8 byte 0, you will see the 0x8C.
I am trying to decode a 9-byte RS-485 packet that has a 3-byte CRC as shown below. Using a SW serial monitor I have decoded the parameter portion Using an online calculator.
I know can calculate Byte7 using 8-bit Checksum8 Modulo 256. My issue is
I cannot determine the last 2 Bytes of CRC.
I have attached my output from the reveng CRC decoder and it
finds a Model but not a name. I supplied 4 full 9 Byte packets.
reveng -w8 -sF 9BYTEPACKET1 9BYTEPACKET2 9BYTEPACKET3 9BYTEPACKET4
And as shown in the image link it returns:
width=8,poly=0x01,init=0x00,refin=false,refout=false,xorout=0x00,check=0x31,residue=0x00,name="(none)"
Sample Data:
00 32 2a ff ff cd 27 03 f1
00 20 03 ff ff cd ee 02 02
00 13 28 ff ff cd 06 03 f3
00 13 02 ff ff cd e0 02 3e
Byte1 = ID(Can Change)
Byte2 = Parameter(Can Change)
Byte3 = Parameter(Can Change)
Byte4 = 0xFF (Never changes)
Byte5 = 0xFF (Never changes)
Byte6 = 0xCD (Never changes)
Byte7 = Checksum8 Modulo 256 (I can calculate this from Byte1 to Byte6)
Byte8 = CRC (Changes with diff Parameter data)
Byte9 = CRC (Changes with diff Parameter data)
Any help would be appreciated.
Your Byte8 and Byte7 are simply the sum of the preceding bytes in little-endian order. Byte9 is then the exclusive-or of the preceding bytes.
There is no CRC.
My Protobuf message consists of 3 doubles
syntax = "proto3";
message TestMessage{
double input = 1;
double output = 2;
double info = 3;
}
When I set these values to
test.set_input(2.3456);
test.set_output(5.4321);
test.set_info(5.0);
the serialized message looks like
00000000 09 16 fb cb ee c9 c3 02 40 11 0a 68 22 6c 78 ba |........#..h"lx.|
00000010 15 40 19 |.#.|
00000013
when using test.serializeToArray and could not be deserialized successfully by a go program using the same protobuf message. When trying to read it from a c++ program I got a 0 as info, so the message seems to be corrupted.
When using test.serializeToOstream I got this message, which could be deserialized successfully by both go and c++ programs.
00000000 09 16 fb cb ee c9 c3 02 40 11 0a 68 22 6c 78 ba |........#..h"lx.|
00000010 15 40 19 00 00 00 00 00 00 14 40 |.#........#|
0000001b
When setting the values to
test.set_input(2.3456);
test.set_output(5.4321);
test.set_info(5.5678);
the serialized messages, both produced by test.serializeToArray and test.serializeToOstream look like
00000000 09 16 fb cb ee c9 c3 02 40 11 0a 68 22 6c 78 ba |........#..h"lx.|
00000010 15 40 19 da ac fa 5c 6d 45 16 40 |.#....\mE.#|
0000001b
and could be successfully read by my go and cpp program.
What am I missing here? Why is serializeToArray not working in the first case?
EDIT:
As it turns out, serializeToString works fine, too.
Here the code I used for the comparison:
file_a.open(FILEPATH_A);
file_b.open(FILEPATH_B);
test.set_input(2.3456);
test.set_output(5.4321);
test.set_info(5.0);
//serializeToArray
int size = test.ByteSize();
char *buffer = (char*) malloc(size);
test.SerializeToArray(buffer, size);
file_a << buffer;
//serializeToString
std::string buf;
test.SerializeToString(&buf);
file_b << buf;
file_a.close();
file_b.close();
Why does serializeToArray not work as expected?
EDIT2:
When using file_b << buf.data() instead of file_b << buf.data(), the data gets corrupted as well, but why?
I think the error you're making is treating binary as character data and using character data APIs. Many of those APIs stop at the first nil byte (0), but that is a totally valid value in protobuf binary.
You need to make sure you don't use any such APIs basically - stick purely to binary safe APIs.
Since you indicate that size is 27, this all fits.
Basically, the binary representation of 5.0 includes 0 bytes, but you could easily have seen the same problem for other values in time.
I'm attempting to mimic the function used for creating CRC's in PNG files, I'm using the autodin II polynomial and the source code from:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c
My tests have all been for the IHDR chunk, so my parameters have been:
crc - 0xffffffff and 0 (both have been suggested)
buff - the address of the IHDR Chunk's type.
length - the IHDR Chunk's length + 4 (the length of the chunk's data + the length of the type)
I printed the calculated CRC in binary, which I compared to the actual CRC of the chunk. I can see no similarities (little-big endian, reversed bits, XOR'd, etc).
This is the data for the IHDR chunk (hexadecimal format):
length(big endian): d0 00 00 00 (13)
type: 49 48 44 52
data: 00 00 01 77 00 00 01 68 08 06 00 00 00
existing CRC: b0 bb 40 ac
If anyone can tell me why my calculations are off, or give me a CRC32 function that will work I would greatly appreciate it.
Thank-you!
The CRC-32 algorithm used in PNG images is described here: http://www.w3.org/TR/PNG-Structure.html#CRC-algorithm (there's also a link to C code for doing test calculations).
But as #Jigsore pointed out, you won't get sensible results from the data you posted here. You've given us a 4-byte type identifier and what looks like 7.5 bytes of data to follow it. There should be a total of 13 bytes according to the length header.
EDIT:
This works using the function from w3.org:
int main() {
char input[] = { 0x49,0x48,0x44,0x52,0x00,0x00,0x01,0x77,0x00,
0x00,0x01,0x68,0x08,0x06,0x00,0x00,0x00 };
printf("%08lx\n",crc(input,17));
return 0;
}
Output:
ac40bbb0
In the registry there is one ( or more ) key depending how many monitors you have HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404C\{Some Unique ID}\Device Parameters\EDID which is a REG_BINARY key. In my case this is :
00 FF FF FF FF FF FF 00 10 AC 4C 40 53 43 34 42 34 14 01 03 0A 2F 1E 78 EE EE 95 A3 54
4C 99 26 0F 50 54 A5 4B 00 71 4F 81 80 B3 00 01 01 01 01 01 01 01 01 01 01 21 39 90 30
62 1A 27 40 68 B0 36 00 DA 28 11 00 00 1C 00 00 00 FF 00 34 57 31 4D 44 30 43 53 42 34
43 53 0A 00 00 00 FC 00 44 45 4C 4C 20 50 32 32 31 30 0A 20 20 00 00 00 FD 00 38 4B 1E
53 10 00 0A 20 20 20 20 20 20 00 FA
This reg_binary value contains information (such as Serial Number and Type) about the connected monitor. I only need these two values. My question is how can i read these values using C or C++?
I have a VB script which can do this:
'you can tell If the location contains a serial number If it starts with &H00 00 00 ff
strSerFind=Chr(&H00) & Chr(&H00) & Chr(&H00) & Chr(&HfF)
'or a model description If it starts with &H00 00 00 fc
strMdlFind=Chr(&H00) & Chr(&H00) & Chr(&H00) & Chr(&Hfc)
This link also contains information about EDID: http://en.wikipedia.org/wiki/Extended_display_identification_data
Could someone help me, how can i do this in C? I can find only VB script examples, but unfortunately i don't understand them, and also it would be very important for me.
You mention wanting the "serial number" and "type". There is no "type" but there is a manufacturer ID and a product ID. For the most part these aren't stored as meaningful strings in the information you get back...they are just numeric values. And they're all in the first 16 bytes.
I'll decode the beginning according to the spec you cite.
Bytes 0,1,2,3,4,5,6,7 - Header information
This should be the literal string "00h FFh FFh FFh FFh FFh FFh 00h", which serves as a sanity check that we're looking at a valid EDID block. Your data starts off with exactly what we expect:
00 FF FF FF FF FF FF 00
Bytes 8 and 9 - Manufacturer ID.
These IDs are assigned by Microsoft, and are three-letter codes. Oh sure, they could have "wasted" three whole bytes in ASCII for this. But that would have been too sensible. So they frittered away eight bytes on an extremely "non-magic" number for the header, and invented an "ingenious" way to encode those three letters into the sixteen bits held by two bytes. How'd they pull it off?
+--------+--------+
| Byte 8 | Byte 9 |
--------+--------+--------+
Bit # 76543210 76543210
-----------------=---------
Meaning 0αααααββ βββγγγγγ
So the highest-order bit of Byte 8 is always zero, and the remaining 15 bits are divided into three groups of 5 bits (which I've called α, β, and γ). Each is interpreted as a letter, where "00001=A"; "00010=B"; ... "11010=Z".
You've got:
10 AC
And hexadecimal 10AC expressed as 16 binary bits is 0001000010101100. So let's bring that table back again:
+--------+--------+
| Byte 8 | Byte 9 |
--------+--------+--------+
Bit # 76543210 76543210
-----------------=---------
Meaning 0αααααββ βββγγγγγ
-----------------=---------
Yours 00010000 10101100
So α = 00100 (decimal 4), β = 00101 (decimal 5), γ = 01100 (decimal 12). Using those decimal numbers as indexes into the English alphabet we get D-E-L. By this arcane sorcery we have determined that your monitor is most likely made by Dell. :)
Bytes 10 and 11 - Product ID Code
This is a two-byte number, assigned by the manufacturer, stored as "LSB first". This is to say that the first byte is the least significant place value. You have:
4C 40
Which we need to interpret as the hexadecimal number 404C.
Bytes 12,13,14,15 - Serial Number.
This is a 32-bit value assigned by the manufacturer which has no requirement for the format. It is "usually stored as LSB first", but doesn't have to be.
53 43 34 42
You can interpret that as 0x53433442, or 0x42344353, or whatever...so long as you're consistent in comparing one value against another.
So now you see it's just three letters and some numbers. Once you get the bytes into a buffer there are a lot of ways to extract the information. #freerider provided some information on that, I'll just throw in a bit more.
The EDID standard says that what you get back as a description is 128 bytes. That is the case with the registry key here, and you can probably assume that if there are not exactly 128 bytes it is corrupt. So using the code provided by #freerider, there'd be no need to pass in anything larger than that...you could technically go down to just 16 if that's the only part of the EDID you're interested in:
#define EDID_BUFFER_SIZE 128
// in idiomatic C++ it's better to say:
// const size_t edidBufferSize = 128;
BYTE edidBuffer[EDID_BUFFER_SIZE];
DWORD nLength = GetLocalMachineProfileBuffer( Buffer, EDID_BUFFER_SIZE );
if (nLength != EDID_BUFFER_SIZE) {
// handle error case, not a valid EDID block
} else {
// valid EDID block, do extraction:
// * manufacturer ID
// * product ID
// * serial number
}
(Note: I prefer to avoid using the sizeof on arrays like #freerider's sizeof( Buffer ) above. While it will technically work in this case, it doesn't return the number of elements in the array...rather the number of bytes the array occupies in memory. In this case the elements happen to actually be bytes, so it will work...but you quickly run into problems, like when you pass an array to another function by pointer and suddenly it starts reporting its size as the size of a pointer...)
Beyond that, your question of how to extract structural data out of a buffer of bytes is a very general one, and is so foundational to C-style programming that if you don't know where to start on it then you should probably work through simpler programs. Getting the three five bit segments out of the manufacturer name involves things like bit shifting, bit masking, or bit fields. Going through the array deals with addresses and how to index arrays and things like that.
The closest parallel question I could find offhand right now is this:
extract IP from a buffer of bytes
Lots of ways to do it, but an interesting one is that you can define the layout of a structure in memory and then tell the program "hey, this block of memory I found is laid out just like the structure I defined. So let me extract information from it as simply as if I'd defined the object in my program"...
But then you have to be sensitive to issues like data structure alignment. That's because the way your compiler will naturally put objects into memory doesn't necessarily match what you think it would do:
http://en.wikipedia.org/wiki/Data_structure_alignment
With the information above you should at least be able to make a shot at reading some tutorials and seeing what works. If you can't figure out one part of the problem then break that little part out as its own question, and show what you tried and why it didn't work...
This previous question explains how to get EDID with C/C++/C#. It's not through the registry, but as long it works...
Win32 code to get EDID in Windows XP/7
If you want to still read the registry, use RegQueryValueEx and friends.
DWORD GetLocalMachineProfileBuffer(BYTE* pBuffer, DWORD nMaxLength )
{
CString szSubKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\DEL404C{Some Unique ID}\Device Parameters\EDID";
DWORD rc;
DWORD dwType;
HKEY hOpenedKey;
if( ERROR_SUCCESS == RegOpenKeyEx (
HKEY_LOCAL_MACHINE, // handle of open key
szSubKey, // address of name of subkey to open
0, // reserved
KEY_READ, // security access mask
&hOpenedKey // address of handle of open key
) )
{
rc = RegQueryValueEx(
hOpenedKey,
(const char*)szValueName,
0,
&dwType,
(LPBYTE)pBuffer,
&nMaxLength );
if( rc != ERROR_SUCCESS )
{
return (DWORD)-1;
}
else
{
ASSERT( dwType == REG_BINARY );
}
RegCloseKey( hOpenedKey );
return nMaxLength;
}
else
{
return (DWORD)-1;
}
}
call it like this:
BYTE Buffer[20000];
DWORD nLength = GetLocalMachineProfileBuffer( Buffer, sizeof( Buffer ) );