PDA

View Full Version : Getting SCR values from a program stream


SMurf
16th July 2002, 16:50
Hi,

I've just made a very cheap little program that attempts to read the SCR value from the pack headers contained in an MPEG-2 program stream, but it doesn't seem to give the correct value :mad:. I did base it slightly on the algorithm used by BBinfo, but without the bit streaming code (I'm trying to do it with standard bit masking and shifting). Here's the important part of my C code:-

unsigned char *ptr; // points to data in buffer
long double SCR;
unsigned long temp; // for playing with the bits

ptr += 4; // skip pack header ID (0x000001??)
temp = (*ptr & 0x38) >> 3; // 3 bits
if (temp & 0x04) // a biggie...
SCR = 4294967296.0;
else
SCR = 0.0;

temp <<= 30; // shift the other 2 bits to the top
temp |= (*ptr++ & 0x03) << 28; // 2 bits
temp |= *ptr++ << 20; // 8 bits
temp |= (*ptr & 0xF8) << 12; // 5 bits
temp |= (*ptr++ & 0x03) << 13; // 2 bits
temp |= *ptr++ << 5; // 8 bits
temp |= (*ptr & 0xF8) >> 3; // 5 bits
SCR += (long double)temp; // add the quotient
SCR *= 300.0;
temp = (*ptr++ & 0x03) << 7; // 2 bits
temp |= (*ptr++ & 0xFE) >> 1; // 7 bits
SCR += (long double)temp; // add the remainder
printf("SCR = %Lf ms\n", SCR / 27000.0);

It seems to print fairly outlandish numbers (although they are incremental, at least :D), but I can't see where I'm going wrong here. Not the tidiest piece of code, but could anyone help?

mpucoder
16th July 2002, 17:12
edit: never mind, I got lost :)

EiGuscheMa
24th July 2002, 06:56
smurf, just my 2 cents:

if ptr points to unsigned char then *ptr will get unsigned char which when << 20 will always get 0.
so I would try:

temp |= ((unsigned long)*ptr++) << 20;
instead of
temp |= *ptr++ << 20;

for example
greetings

SMurf
12th August 2002, 17:01
Not neccessarily, as the |= operator equates to "temp = temp | *ptr++ << 20;", which would mean that the pointer is implicitly converted. Not very portable perhaps, but that's the way cr*p programmers think :D

Anyways, I figured out what my problem was ("ptr += 4;" went beyond where I wanted to be, changed to "ptr += 3"), and am now able to read SCR values. However, I have now made a little program that attempts to write the contents of a .wav file to an MPEG-2 Program Stream containing 1 stream of ID 0xBD, substream 0xA0 (LPCM). Currently I'm not writing any SCR values (they're all 0 in my packets), and although bbinfo seems content that my VOB is workable, PowerDVD disagrees ("Unknown file format.") Is it because the SCR is always 0, or something else? By how much should I increment the SCR every packet?

tateu
12th August 2002, 22:04
Have you verified that your multiplexor is adding all the other correct parameters, such as a System Header with a StreamID value for a Private Stream (0xBD), Video PTS and DTS, Audio PTS, etc? Is it outputting a file with the correct DVD Pack Size of 2048?

Maybe try mutlplexing your m2v and wav file once with your program and once with BBmpeg and then run BBinfo on both of them and see if there are any of those parameters that you are missing.

I have written my own mpeg analyzer but haven't ever looked into multiplexing so I am not sure how much to increment the SCR or Audio PTS values. I haven't found a steady increment value for SCR. I think the audio PTS values are related to frame size (1 frame of 48KHz audio = 24ms) but I am not 100% sure. I have only recently started to look into the file structure of mp2, AC3 and LPCM audio.

SMurf
12th August 2002, 23:34
My "multiplexor", if that's what you wanna call it (seeing as my program produces a Program Stream with only one substituent, the audio, I'd prefer to call it a "packetizer" :P), does output packets that are 2,048 bytes in size (I verified this with bbinfo). I copied the program_mux_rate (31833) from a sample stream I have, although I'm not sure what implications that has (* 50 bytes/second? Eh? :confused: ). The program does correctly create the streams (as I had said previously), although I have no PTS or DTS. I know you need PTS for an audio stream, but there was speculation as to the neccesity of it from the source I was reading the LPCM header information from. Should I put a PTS (0, of course :D) in the first packet?

I'm not doing video, this is purely to convert a .wav to a PS encoded with LPCM data from it. None of the standard multiplexors I can get my hands on support LPCM audio streams, so I can't test it.

tateu
13th August 2002, 00:18
Oh, audio only, I didn't understand that. And you're right about BBMpeg, for some reason I thought it handled LPCM audio.

I don't know if PTS values are required, but I don't think not having them would cause an invalid format error like you are getting. I'll have to play with a few things when I get home.

I am certainly no expert, but if you'd like to upload (or email) an LPCM mpg file somewhere I'd take a look and see if I can find any errors with it.

mpucoder
13th August 2002, 01:24
I don't know what that sample stream you have is, but the program mux rate is way too high for DVD. The standard (read that as ONLY) value used in DVD is 25200, which represents 1,260,000 bytes/second (the *50), or 10.08Mbps.
I'm assuming you are targeting DVD since LPCM is NOT part of the MPEG standard, but an extension of private streams for DVD (00 00 01 BD = private stream 1).

Nic
13th August 2002, 09:32
(Thanks for the above code SMurf, the SCR values are alot more accurate than the cr*ppy GOP way I was doing it)

-Nic

SMurf
13th August 2002, 10:25
Mmm, point mpucoder. Looking at my source DVD, the program_mux_rate is 25200, but when I put it through SmartRipper to make a VOB with just the LPCM stream in it, it seems to think 31833 is a better number...

Not that it seems to affect the way it plays, though. I think my problem at the moment is that a lot of my parameters are zeros at the moment, for instance the "number of frames" field, "FAU pointer" field and "audio frame number" field. The sample stream seems to set "number of frames" to either 6 or 7, but I don't know what the FAU's doing. I'm assuming the "audio frame number" field is 5 bits to act as a count to the number of 32ms frames played, but I don't know. If you're saying 1 frame of 48KHz audio is 24ms tateu, how many bytes is that?

mpucoder
13th August 2002, 14:06
Some of those fields, like frame number, depend on the presence of the NAV PACK (DSI/PCI) for a DVD. This marks the beginning of a VOBU, which is what the frame numbers are in reference to. All my sources say an AC3 frame is 32ms. But that's AC3, LPCM doesn't need a frame structure. The first audio unit is the frame number within a VOBU that the audio DTS pertains to. It is usually, but not always, the first full frame of audio.

I think your main problem might well be that you are trying to produce a rogue structure, that is a DVD specific extension without the rest of the DVD structure.

SMurf
13th August 2002, 17:04
Well, I've learnt a lot today :D

After some furious playing with parameters all over the place, I have found out that:-
"Number of audio frames" cannot be zero
"FAU pointer" cannot be zero
I was using an invalid value for sampling frequency (0=48KHz, 1=96KHz) as after reading the MPlayer (Movie Player for linux) mailing list they said that 2=44KHz (What I was using), 3=32KHz. I must ask them about that...

It does appear to work now (Although all I can hear is static, I need to work out how to use the two parameters above properly :p). I really would like 44KHz support though, but apparently the DVD Book doesn't have it, so I'm outta luck :(.

Edit: Apparently the extra frequencies only apply to DVB. Bum :(

mpucoder
13th August 2002, 17:41
Well, that's progress! If all you heard was static try reversing the byte order in the samples. Hopefully you are using 16-bit samples, 24-bit are very strangely packed.

tateu
13th August 2002, 19:42
mpucoder could be on to something with the byte order. I always forget which is which between Little Endian and Big Endian but the multiplexor (C-Cube) for my capture card (Dazzle DVC-II) requires the audio in Big Endian format but when capturing to separate m2v and wav files it requires the audio in Little Endian format. I wrote my own capture program for it and so I am able to force the incorrect byte order and I remember getting nothing but static when I tried a multiplexed capture with Little Endian format.

I don't know how to change formats, though. Maybe mpucoder can shed some light on that if you don't know how to do it. Are you supposed to reverse the order byte by byte: 0x80 (BIN 1000 0000) becomes 0x01 (Bin 0000 0001)? Or in pairs of bytes: 0x80AA becomes 0xAA80? Or in 4 bytes: 0x80AABB01 becomes 0x01BBAA80? Or something else?

Also, the 24 ms frame size applies to 48KHz mpeg-1 Layer 2 audio. Admittedly I have not done a lot with audio analysis (mostly just video) and even less with LPCM audio. I believe mpucoder is right about the 32ms frame size for 48KHz AC3. An analysis of a BBMpeg multiplexed AC3 mpg has audio PTS values that are 64ms and 96ms apart, both of which are multiples of 32. The files I have analyzed with mpeg audio have values that are 24, 48, 72 and 96ms apart. The mpeg audio files seem to have a frame size of 672 bytes and the AC3 files seem to be 768 bytes. the LPCM multiplexed files (from my captur card) have audio PTS files that are 10, 11.66, 11.69, 10.01 and 9.99ms apart. I am not sure why the numbers are not even integers. It could be something strange going on with my capture card. I haven't looked at an LPCM VOB yet.

Oh, and I'm curious, why exactly do you need Packetized mpeg files with LPCM audio only?

tateu
13th August 2002, 23:35
BlackSun gave me this link in another thread (about writing a DirectShow AC3 encoder, which I've never had much time to look into yet) but I completely forgot about it until today:
http://members.freemail.absa.co.za/ginggs/dvd/

I haven't tried it, but I think it's almost exactly what you are looking for. It's a PCM to VOB converter with full VC++ source code. It requires a raw PCM file, which again points to mpucoder's point about trying to reverse the byte order.

Update:

I just tried it. It doesn't work with the Ligos mpeg Splitter, it crashes, but if I use "mpg2splt.ax" (which I think is a Microsoft mpeg splitter) then it works. When I fed a wav file to PCM2VOB the output produced nothing but static. I then used a program, Sox, to convert my wav file to raw PCM (reversed byte order) and the file played back without problems. I then compared them in a hex editor and the reversal of byte order is done by pairs (0x80AA becomes 0xAA80).

So it looks like reversing the byte order could be your key to success

mpucoder
14th August 2002, 00:32
Interesting stuff at that site. Looks like I was wrong about LPCM being framed. Very short frame rate, 600 per second. But I guess that's to avoid a nasty problem that AC3 has with seamless branching - audio and video framerates not having a rational (in the mathematical sense) relationship.

SMurf
14th August 2002, 17:29
Well, if a frame is 150 90KHz clock ticks long, then it is 150/90000, or 1/600th of a second (1.6666666666 ms). Do a rough approximation and it turns out 1 frame is 160 bytes of PCM data at 48KHz, 16-bit mono (double for stereo). Hence if your packet only holds 2008 bytes of said data, it can hold 2008 / 320 = 6.275 frames of stereo data.(Round that up to 7, push the remaining data into the next packet, change the FAU pointer to where the next set of frames start and you're away :sly: )

My program does all that now, supporting mono and stereo data, and it also calculates the number of frames that can fit in each packet, so it should fit the standard now :D.
The only things I have to deal with is analyzing the source WAV format and converting 8- to 16-bit, resampling, etc. :cool:

Oh, and there's still the issue of SCR and PTS incrementation. I noticed that when SCR = 0.0 and PTS is also 0.0, bbinfo reports an "underflow". Does the PTS have to be greater than the SCR value?

mpucoder
14th August 2002, 18:51
Yes! PTS >= DTS > SCR
Reason: SCR is the current time stamp (actually of the last bit of the SCR)
DTS is the time at which the decoder should receive the data (therefore it must be no sooner than the last bit of the data, or approximately the SCR of the next pack)
PTS is the time to present the decoded data.

Nic
15th August 2002, 12:41
@mpucoder:
Sorry to bother you, but what is the most accurate way of finding the number of frames in a MPEG2 File (not caring about speed)
Ive tried counting the frames in each GOP, finding that first SCR & last SCR & multiplying by the frame_count & finding the first PTS of the 0xe0 stream & the last & multiplying by the frame count.

All are pretty accurate, but not the same as outputted by MPEG2DEC.DLL...

Could you give me any suggestions as to which/other more accurate ways :)

Thanks for any insight :) ,
-Nic

Koepi
15th August 2002, 12:50
Hm.
Maybe you should take the last time stamp and read the FPS value, that should be accurate... :)

Regards,
Koepi

Nic
15th August 2002, 13:16
Ive tried that :) (you also have to read the first time stamp, as the first time stamp is almost never 0:0:0)...

-Nic

Koepi
15th August 2002, 13:19
Oh, then scratch that.

Damn. There must be a way to figure that out correctly..

Regards,
Koepi

tateu
15th August 2002, 17:58
Nic,

Well, if you don't care about speed the most accurate way is obviously to count each and every frame. How far off from mpeg2dec.dll is your count when using GOP and PTS values? A quick test I did with a 743MB NTSC Telecined SVCD encoded with TMPGEnc came to:

mpeg2dec.dll = 60333
Frame By Frame = 60336
PTS = 60400
GOP = 60338
SCR = 60398
BitRate Viewer = 60336 (frame by frame)
Mpeg2VCR = 60338 (seems to use GOP)
Windows Media Player = about 60400 (seems to use PTS or SCR)

Frame By Frame, PTS, GOP and SCR were all calculated using a (not quite finished) mpeg analysis program I have been working on. The Frame By Frame count took about 1 minute to calculate so there isn't even a lot of time involved. I think I've done upwards of a 5 GB file in 6 to 7 minutes. If you don't have any code to count frames, you could take bbinfo form the author of BBMpeg and modify it to keep count of how many frames it analyzed.

I trust the frame by frame count to be the most acurate. I believe I remember reading somewhere that DVD2Avi cuts off a few frames so that is probably why mpeg2dec.dll reports 3 frames less than my frame by frame. The GOP count, in this case, is the next closest. Normally a PTS count is a lot closer, atleast on the majority of mpegs I analyze (29.97 DF NTSC from a DVC-II).

mpucoder
15th August 2002, 18:10
ReMPEG2 also analyzes the video and reports the encoded and displayed frames (which is great for NTSC people).
Tateu: what values do you get from it for your sample?

Nic
15th August 2002, 18:33
Thanks tateu,

Hmmm, Ill have to look into doing it frame by frame, if I remember correcly bbinfo's code is very messy, so I guess ill have to start from scratch...

-Nic

tateu
15th August 2002, 18:59
mpucoder,

ReMpeg v1.52 crashes when loading the above mentioned multiplexed file. It reports 60336 pictures (based on 24 fps) and 75420 frames (based on Telecined 29.97 fps) on the demultiplexed m2v file, though. And it does a better job on the GOP structure then my program. I still have some problems with my code when trying to read information (such as Picute Header info) that is spanned across multiple Packs.

Nic,

I started with bbinfo as a reference and then ended up writing almost all of my own code (though my code is far messier than bbinfo: it was my first C++ program and only my third program in any language). I can't seem to find the source code for bbinfo, at the moment, but if you didn't want to modify very much of it, I think all you'd need to do was add a global variable, such as int PictureCount, then PictureCount++ in a few places and print it out at the end.

Nic
16th August 2002, 09:55
Thanks tateu (I thought Id replied to this, but it cant have sent correctly :) )

Ive flicked thru the MPEG-2 spec again & Im guessing you refer to the picture header (0x00000100) as "frame by frame". So ill go count them... :)

also Ive noticed why mmy code was sometimes more out than it should be (Read: why im so stupid ;) )

while (true)
{
... find info...

// read more
readin = read(in, buffer, READINSIZE);
if ( readin < READINSIZE )
break;
}

Spot the bug?? :D

Cheers,
-Nic

ps
Just tried it on a 500meg vob file. im one frame out from what MPEG2DEC.DLL reports... :D

pps
Grrrr...it still can be out by around 10 frames on some MPEG-2 files ive got...

Start codes are specific bit patterns that do not otherwise occur in the video stream.
(from the spec)

Thats not right for a start as just searching for 0x00000100 will not result in a picture header everytime....grrr.

vlad59
16th August 2002, 13:03
@Nic

If it can help you, I noticed when I worked in Dvd2avi that Mpeg2dec.dll always (or very often) report one frame more than when you save your avi with Dvd2avi directly (without avisynth).

There is maybe a bug with mpeg2dec.dll but I had no time to check this out.

hope this helps

mpucoder
16th August 2002, 13:52
Start codes are unique, however when searching for codes within the video stream, make sure you're in the video stream! You must still process pack headers and pes headers. Actually, one easy way (DVD specific) is to look for 00 00 01 e0 at byte offset 00e of the pack. Then look at byte 016, it contains the size of the extension. Resume your scan at 017 + contents of 016.

A more general method means looking for any system header (00 00 01 BA - 00 00 01 FF) as an end to the video stream, and processing it. Processing can be very simple, for pack header (01 BA) skip 14 bytes, for pes headers other than 01 e0 the length is given just after the header, so skip ahead by that much.

Nic
16th August 2002, 15:53
Thanks for that mpucoder, ill give that a try :)

-Nic

SMurf
18th August 2002, 01:52
Isn't this my thread? :p

Anyways, I seem to be having some problem with my streams still:- it appears to play ever so slighty too quickly, and there seems to be distortion in the sound produced. The only things I can find different between my streams and other (working :D) streams is that my SCR and PTS values do not change (Will fix that v. soon) and that I do not specify a P-STD buffer scale. The reason is because I don't know what one is and how it affects the stream. Proper streams appear to set it to 1024 bytes. Suggestions?

mpucoder
18th August 2002, 03:15
Did you specify a P-STD buffer size? According to 13818 audio should be specified using the 128 byte scale, but that is MPEG audio.

The example I have of LPCM, 48K, 2 ch, 16-bit uses 1024 byte scale, P-STD buffer size = 58. (You can see this file, as it is the burner test file used a while back. http://mpucoder.kewlhair.com/burner_tests.html )

SMurf
18th August 2002, 17:47
Hmm, if I now feed it stereo PCM data, it sounds perfect, but if its mono, it plays at the right speed, but it's high pitched, for some reason (grrrrrr :angry: ). The DVD standard allows for mono data, I know that, but I've had problems in the past with mono MPEG Layer-2 data and PowerDVD, so perhaps it's the player.

Reading the stream information during playback does give the correct values (1 channel, 768Kbps), but I wonder if you must keep the frame size the same as for stereo in order for it to work... :confused: