Find int from byte array

Discussion in 'Mac Programming' started by isthisonetaken, Feb 9, 2011.

  1. isthisonetaken macrumors regular

    Joined:
    Jun 29, 2006
    #1
    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
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    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
     
  3. isthisonetaken thread starter macrumors regular

    Joined:
    Jun 29, 2006
    #3
    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?
     
  4. balamw, Feb 9, 2011
    Last edited: Feb 9, 2011

    balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #4
    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
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    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
     
  6. Sydde, Feb 9, 2011
    Last edited: Feb 9, 2011

    Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #6
    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
    
     
  7. isthisonetaken thread starter macrumors regular

    Joined:
    Jun 29, 2006
    #7
    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.
     
  8. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #8
    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
     
  9. mfram macrumors 65816

    Joined:
    Jan 23, 2010
    Location:
    San Diego, CA USA
    #9
    Sounds like the value is BCD encoded then.
     
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    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
     
  11. isthisonetaken, Feb 9, 2011
    Last edited: Feb 9, 2011

    isthisonetaken thread starter macrumors regular

    Joined:
    Jun 29, 2006
    #11
    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?
     
  12. chown33, Feb 9, 2011
    Last edited: Feb 9, 2011

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #12
    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.


    Frankly, those look like ASCII digits, except for the 2.

    The characters 53, 54, 49, 55 are the numeral string "5617".
     
  13. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #13
    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
     
  14. isthisonetaken, Feb 9, 2011
    Last edited: Feb 9, 2011

    isthisonetaken thread starter macrumors regular

    Joined:
    Jun 29, 2006
    #14
    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
     
  15. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #15
    Don't use atoi(). Use strtol() or strtod() instead.
     
  16. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #16
    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/mac/#documentation/QuickTime/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
     
  17. isthisonetaken, Feb 10, 2011
    Last edited: Feb 10, 2011

    isthisonetaken thread starter macrumors regular

    Joined:
    Jun 29, 2006
    #17
    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?

    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
     
  18. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #18
    From the man page for atoi:


    You probably should have mentioned it was homework at the start.
     
  19. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #19
    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
     

Share This Page