Search code examples
androidimagemp3bitmapfactoryid3

Why is BitmapFactory.decodeByteArray returning Null?


Bitmap.Factory will not decode a byte array with png image data to a bitmap. The png data is collected from an id3 tag of an mp3.

     byte[] imageBytes = getImageFrameData("APIC", imageType, _id3TagByteArray);
    //!convert frame data bytes to image
    if(imageBytes != null) {
        image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
    }

Am I doing it wrong? Bitmap factory always returns null.

getImageFrameData

     private byte[] getImageFrameData(String frameID, byte imageType, byte[] ID3tag){
    /////////////////
     /*
    variable position is initialized to 10 in order to skip ID3 header
    (this must be improved in order to account for potential extended id3 header)
     */
    //!check for extended id3 header (to be implemented)
    final int FRAME_ID_LENGTH = 4;
    final int DATA_LENGTH = 4;
    final int FLAG_LENGTH = 2;
    final int HEADER_LENGTH = FRAME_ID_LENGTH + DATA_LENGTH + FLAG_LENGTH;
    final int TEXT_ENC_LENGTH = 1;
    final int PICT_TYPE_LENGTH = 1;
    List<Byte> fMimeType = new ArrayList<Byte>();


    int position = 10;
    int fInfoPos = position;
    byte[]frame = null;

    while(true){//!loop for seeking frames

        //!Get Frame Id of current frame in loop
        byte[] fID = getBytesInRange(ID3tag, fInfoPos, FRAME_ID_LENGTH);//^search start skips ten bytes

        //!Get Size of Frame Data
        byte[] fSize = getBytesInRange(ID3tag, fInfoPos+=fID.length, DATA_LENGTH);
        int frameDataSize = decodeSynchsafe(fSize);

        //!Get Frame Header Flags (to be implemented)
        byte[] fFlags = getBytesInRange(ID3tag, fInfoPos+=fSize.length, FLAG_LENGTH);

        try {
            if (frameID.equals(new String(fID, "UTF-8"))) {
                //!process image frame
                //!!get additional information data from image frame
                //!!!get text encoding
                byte[] fTextEnc = getBytesInRange(ID3tag, fInfoPos+=fFlags.length, TEXT_ENC_LENGTH);

                //!!!get MIME type
                for(int i = fInfoPos+=fTextEnc.length; i < ID3tag.length - 1; i++){
                    if(ID3tag[i] != 0){
                        fMimeType.add(ID3tag[i]);
                    }else{
                        fInfoPos += fMimeType.size() + 1;
                        break;
                    }
                }
                ////////////////
                byte[]mimeTypeData = new byte[fMimeType.size()];
                for(int i = 0; i < mimeTypeData.length; i++){
                    mimeTypeData[i] = fMimeType.get(i);
                }
                String mime = new String(mimeTypeData);
                ///////////////

                //!!!get image type
                byte[] imgType = getBytesInRange(ID3tag, fInfoPos, PICT_TYPE_LENGTH);

                //!!!get Description
                List<Byte>fDescList = new ArrayList<>();
                for(int i = fInfoPos+=imgType.length; i < ID3tag.length - 1; i++){
                    if(ID3tag[i] != 0){
                        fDescList.add(ID3tag[i]);
                    }else{
                        fInfoPos += fDescList.size() + 1;//+1
                        break;
                    }
                }

                //?if picture type is that of query

                if(imgType[0] == imageType ){
                    //!get image Frame
                    imageFrame = getBytesInRange(ID3tag, position, frameDataSize + 10);
                }

                //!Get Data from Image Frame
                int start = fInfoPos - position;/////////HERE
                //int end = (frameDataSize) - (fInfoPos - position);
                int end = (HEADER_LENGTH + frameDataSize) - start;
                byte[] image = getBytesInRange(imageFrame, start, end);
                String stop = "stop";
                return image;
            }else{
                //!skip to next frame
                position += frameDataSize + HEADER_LENGTH;
                fInfoPos = position;
            }
        }catch(Exception ex){

        }

        /*
        The frame header flags could have important information
         which can determine how the frame is to be processed.
         */
        /////////////////
        //No Frame Exists for the current tag
        // (The id3 tag has been scanned and the tag of query doesn't exist
        //!break
        if(frameDataSize == 0){
            break;
        }
        /////////////////
    }
    return null;
    ////////////////
}   

Solution

  • The problem is solved. For those who might wish to create a program to handle mp3 id3tag information, the solution is important.

    The problem was that the image-data byte array was incomplete with the image data. Incorrect id3 frame size data stored in the id3 image frame is the reason.

    Every id3 frame has a header of 10 bytes. The 4th-8th byte represents the size of the data in the frame (size of header is excluded). The decoded size for the image frame is simple corrupt in the mp3 I was using for the test. It was giving the frame data byte size of 26350 when the real data size is 85867.

    The problem was solved by searching through the id3tag byte array for the PNG end of file marker. 8 bytes from the marker is the end of file. To get the size of the png file, the number of bytes searched in the id3 byte array until the png end of file marker is found, is subtracted by the number of bytes before start of the image frame data, and then 8 is added. I know this sounds complicated but I thought this might help someone. Also, an id3 tag can hold two image mime types, PNG and JPG. The previous solution must be modified to also process JPG.

                    ///////////// Calculate size of PNG file /////////////
                    int[]chunk_id_IEND = {73,69,78,68};
                    int pngSize = 0;
                    int counter = 0;
                    for(int i = 0; i < _id3TagByteArray.length; i++){
                        if(_id3TagByteArray[i] == chunk_id_IEND[0]){
                            for(int j = 0; j < 4; j++ ){
                                if(_id3TagByteArray[i + j] == chunk_id_IEND[j]){
                                    ++counter;
                                }else{
                                    break;
                                }
                            }
                        }
                        if(counter == 4){
                            pngSize = (i - fInfoPos) + 8;
                            break;
                        }else{
                            counter = 0;
                        }
                    }
                    /////////////////////////////////////////////////////