Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

isthisonetaken

macrumors regular
Original poster
Jun 29, 2006
123
0
Hey all,

so I am trying to decode an mjpeg file and have found that the file is built like this:
-> first 5 bytes are the number of bytes for the first image
-> 5 bytes (after the initial 5 + # for the first image) are the number fo bytes for the second image
-> and so on until the file is done

I am using NSData to load the file into an object, and used a range and subDataWithRange to create another NSData object which holds the size for the first picture. I'm stuck at how to convert that second NSData object into an int number that can be used to traverse the mjpeg file.

Any thoughts?

Dan
 
Do you know the byte order of the 5 byte length? I presume this value is unsigned? I would probably copy this a byte at a time into a long int (which should be at least 8 bytes) in the right positions based on byte order. I'd do that all in a subroutine that returns the read value. You could pass in the NSData with a start byte or a char buffer that you filled in with getBytes:range:.

-Lee
 
I would probably copy this a byte at a time into a long int (which should be at least 8 bytes) in the right positions based on byte order.
-Lee

How would I go about getting the individual bytes and copying them into a long int? I figured if I could get the individual bytes, than I could multiply that number (0-9) by the place (10k, 1k, 100, 10, 1) add those together and return that, but I can't get individual bytes.

When using [NSDataObject bytes] you need to use const void* and it doesn't let me access the bytes?
 
I figured if I could get the individual bytes, than I could multiply that number (0-9) by the place (10k, 1k, 100, 10, 1) add those together and return that, but I can't get individual bytes.
Wouldn't that be powers of 256 instead of 10?

i.e.
Code:
(byte1*256*256*256*256)+(byte2*256*256*256)+(byte3*256*256)+(byte4*256)+byte5

I've often cheated in C and cast a pointer to a char array buffer to a pointer to the appropriate int or even struct, haven't tried that in Objective-C though.

EDIT: Won't [NSData bytes] give you access to the actual bytes? And is it binary or BCD?

B
 
Last edited:
Code:
char lenBytes[5];
int startPos = ...;
[myData getBytes:lenBytes range:NSMakeRange(startPos,startPos+4)];
long int imgLen = 0;
char *imgLenBytes = &imgLen;
if(isBigEndian) {
  int byteStart = sizeof(imgLen)-5-1;
  memcpy(&imgLenBytes[byteStart],lenBytes,5);
} else {
  for(int bPos = 0; bPos < 5; bPos++) {
    imgLenBytes[bPos] = lenBytes[5-bPos];
  }
}
NSLog(@"the next image is %ld bytes.",imgLen);
//now allocate a buffer for the image, and stick the bytes in there.

This is hard to do in this box on my phone, so this is untested and could do irreparable damage to your machine or psyche.

-Lee
 
Just use the method -bytes to get a pointer to the data and start scanning with the pointer

Code:
char *scanptr = [imageDataObject bytes];
long long img1size = 0;
int aCtr;
for ( aCtr = 0 ; aCtr < 5 ; aCtr++ )
   // this is little-endian
   img1size |= scanptr[aCtr] << ( 8 * aCtr );
// and so forth
 
Last edited:
I tried both codes from lee and Sydde. Lee's code gave me a huge number magnitudes larger than the 35 million bytes making up the file and Sydde's gave me 49. Forgive me if this is a dumb question Sydde, but I'm confused on what this line of code does exactly: img1size |= scanptr[aCtr] << ( 8 * aCtr );

I don't know what the |= means and a google search returns nothing. I think that scanptr[aCtr] is moving to the next byte in the for loop, but than I'm not sure what << ( 8 * aCtr) is doing, although I would hazard a guess it has something to do with bits?

Thanks for the help so far guys, a think a little bit of clarification in the above code and I might have it working!

Also, to clarify, the byte order is 10k, 1k, 100, 10, 1 and the final number should be around 50 thousand something and not over 80 000.
 
I tried both codes from lee and Sydde. Lee's code gave me a huge number magnitudes larger than the 35 million bytes making up the file and Sydde's gave me 49. Forgive me if this is a dumb question Sydde, but I'm confused on what this line of code does exactly: img1size |= scanptr[aCtr] << ( 8 * aCtr );

I don't know what the |= means and a google search returns nothing. I think that scanptr[aCtr] is moving to the next byte in the for loop, but than I'm not sure what << ( 8 * aCtr) is doing, although I would hazard a guess it has something to do with bits?

Thanks for the help so far guys, a think a little bit of clarification in the above code and I might have it working!

Also, to clarify, the byte order is 10k, 1k, 100, 10, 1 and the final number should be around 50 thousand something and not over 80 000.

Oh, I think I understand now. The first byte is 10k chunks, the second is 1k, etc. That changes things dramatically. I thought this was a 5-byte integer. So:
Code:
char lenBytes[5];
long int magnitudes[] = {10240,1024,100,10,1};
int startPos = ...;
[myData getBytes:lenBytes range:NSMakeRange(startPos,startPos+4)];
long int imgLen = 0;
for(int bPos = 0; bPos < 5; bPos++) {
  imgLen += lenBytes[bPos]*magnitudes[bPos];
}
NSLog(@"the next image is %ld bytes.",imgLen);
//now allocate a buffer for the image, and stick the bytes in there.

I think that would work, and thankfully removes the need for endian-specific handling.

-Lee
 
Sounds like the value is BCD encoded then.

Ah, makes sense. So then:
Code:
char lenBytes[5];
int startPos = ...;
[myData getBytes:lenBytes range:NSMakeRange(startPos,startPos+4)];
long int imgLen = 0;
long int magnitude = 1;
for(int bPos = 4; bPos >= 0; bPos++) {
  imgLen += lenBytes[bPos]*magnitude;
  magnitude *= 10;
}
NSLog(@"the next image is %ld bytes.",imgLen);
//now allocate a buffer for the image, and stick the bytes in there.

Unfamiliar with that scheme. Makes more sense than interpreting the k as kilobytes.

-Lee
 
After looking up what BCD is, that is exactly the format the numbers are in. I changed the code to what you suggested lee and it got me an answer of 80493 which I'm almost inclined to say is the right number, except I was told it would never be over 80 000, so it might not be exactly right. Also lee, why in the NSMakeRange are you only doing startPos+4? Wouldn't it be 5? I tried +5, but the answer changed to over 500 000 so that didn't work... When checking the numbers individually, I'm getting 53, 54, 49, 55, 2. I'm guessing that is why the value is off at the end. Each byte should only be a number from 0-9.

Is there anyway to see the binary representation of the bytes? Like can I print all 5 bytes (in 8 bit format) in a textfield so I can know exactly what the answer should be?
 
Last edited:
If you want to look at binary bytes in a file, use a program like Hex Fiend.
http://ridiculousfish.com/hexfiend/

Where by "binary" I really mean "hexadecimal".


How about posting the URL of an example mjpeg file, so others have something to test with.

Or post the URL of the mjpeg spec, so others can see what the file format is.


When checking the numbers individually, I'm getting 53, 54, 49, 55, 2.
Frankly, those look like ASCII digits, except for the 2.

The characters 53, 54, 49, 55 are the numeral string "5617".
 
Last edited:
After looking up what BCD is, that is exactly the format the numbers are in. I changed the code to what you suggested lee and it got me an answer of 80493 which I'm almost inclined to say is the right number, except I was told it would never be over 80 000, so it might not be exactly right. Also lee, why in the NSMakeRange are you only doing startPos+4? Wouldn't it be 5? I tried +5, but the answer changed to over 500 000 so that didn't work... When checking the numbers individually, I'm getting 53, 54, 49, 55, 2. I'm guessing that is why the value is off at the end. Each byte should only be a number from 0-9.

Is there anyway to see the binary representation of the bytes? Like can I print all 5 bytes (in 8 bit format) in a textfield so I can know exactly what the answer should be?

If you couldn't tell I've just been spit-balling based on what I've understood from your description. The +4 is wrong for a different reason. The second parameter to NSMakeRange should be 5 all the time, I had misremembered it as being the end of the range.

You can definitely print the bytes:
Code:
char lenBytes[5];
int startPos = 0;
[myData getBytes:lenBytes range:NSMakeRange(startPos,5)];
for(int bPos = 0; bPos < 5; bPos++) {
  NSLog(@"Byte %d's value is %hhu",bPos,lenBytes[bPos]);
}

-Lee
 
I appreciate the help lee. Here's what I got in the log:
Byte 0's value is 53
Byte 1's value is 54
Byte 2's value is 49
Byte 3's value is 55
Byte 4's value is 49

I was also playing around with some things and got this code
Code:
NSRange range = {0, 5};
NSData *picSize = [vid1 subdataWithRange:range];
const char *bytes = [picSize bytes];
long int size = atoi(bytes); // add this to turn the const char* into a long int!
NSRunAlertPanel(@"info", @"bytes: %s\nsize: %ld", NULL, NULL, NULL, bytes, size);

and it prints out 56171 which I would think is correct. Now I can't seem to access the individual digits of the pointer though (as you may have noticed, pointers are a weak point for me). Assuming that those are the correct numbers, if I can access them in a for loop individually, than I can use what lee had above with the modifier (except starting at 10000 and dividing by 10 each time) to get the above char into an int.

EDIT: As it turns out, I don't need to access each character, I found the c function atoi which takes a const char* and it worked like a charm!

Thanks for all the help you guys.

Dan
 
Last edited:
Don't use atoi(). Use strtol() or strtod() instead.
Any particular reasons why I should be using the other methods?
EDIT: I see now, strtol returns a long int rather than the int being returned by atoi. I'm guessing that's why you suggested those?

Just FWIW, this seems like a particular MJPEG implementation and not the more standard and documented Apple Quicktime MJPEG-A, MJPEG-B (http://developer.apple.com/library/m.../QTFF/qtff.pdf page 129) or MJPEG in an AVI formats that many cameras will use.

The limitation of frame size to < 80,000 bytes is particularly odd to me. Must be a relatively low resolution stream.

B
This is for an assignment to stream video from a server to client and we were given two files to work with. I had originally used ffmpeg to split the video file into pictures and was going to stream those, but my professor didn't like that and made me switch to doing it this way. I still appreciate all the suggestions and help everyone gave me yesterday to allow me to reach the solution so I can move onto the other parts of the assignment!

Dan
 
Last edited:
Any particular reasons why I should be using the other methods?
EDIT: I see now, strtol returns a long int rather than the int being returned by atoi. I'm guessing that's why you suggested those?

From the man page for atoi:

IMPLEMENTATION NOTES
The atoi() function is not thread-safe and also not async-cancel safe.

The atoi() function has been deprecated by strtol() and should not be
used in new code.


This is for an assignment to stream video from a server to client and we were given two files to work with. I had originally used ffmpeg to split the video file into pictures and was going to stream those, but my professor didn't like that and made me switch to doing it this way. I still appreciate all the suggestions and help everyone gave me yesterday to allow me to reach the solution so I can move onto the other parts of the assignment!

Dan

You probably should have mentioned it was homework at the start.
 
Just a note in using strtol, atoi, etc... those are expecting a null-terminated array of characters. I don't see you doing null-termination, so you may have gotten lucky, but you should just get a 6-char array, copy in the 5 bytes for the image you're working with, and always set [5] to ='\0'. Otherwise, things could go bad in terms of reading outside of memory you have permission to, etc. causing a crash.

-Lee
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.