Coverage Report - org.jaudiotagger.tag.id3.ID3Unsynchronization
 
Classes in this File Line Coverage Branch Coverage Complexity
ID3Unsynchronization
93%
45/48
90%
29/32
6.667
 
 1  
 package org.jaudiotagger.tag.id3;
 2  
 
 3  
 import org.jaudiotagger.audio.mp3.MPEGFrameHeader;
 4  
 
 5  
 import java.io.ByteArrayInputStream;
 6  
 import java.io.ByteArrayOutputStream;
 7  
 import java.nio.ByteBuffer;
 8  
 import java.util.logging.Logger;
 9  
 import java.util.logging.Level;
 10  
 
 11  
 /**
 12  
  * Performs unsynchronization and synchronization tasks on a buffer.
 13  
  * <p/>
 14  
  * Is currently required for V23Tags and V24Frames
 15  
  */
 16  0
 public class ID3Unsynchronization
 17  
 {
 18  
     //Logger
 19  4
     public static Logger logger = Logger.getLogger("org.jaudiotagger.tag.id3");
 20  
 
 21  
 
 22  
     /**
 23  
      * Check if a byte array will require unsynchronization before being written as a tag.
 24  
      * If the byte array contains any $FF $E0 bytes, then it will require unsynchronization.
 25  
      *
 26  
      * @param abySource the byte array to be examined
 27  
      * @return true if unsynchronization is required, false otherwise
 28  
      */
 29  
     public static boolean requiresUnsynchronization(byte[] abySource)
 30  
     {
 31  13744
         for (int i = 0; i < abySource.length - 1; i++)
 32  
         {
 33  13648
             if (((abySource[i] & MPEGFrameHeader.SYNC_BYTE1) == MPEGFrameHeader.SYNC_BYTE1) && ((abySource[i + 1] & MPEGFrameHeader.SYNC_BYTE2) == MPEGFrameHeader.SYNC_BYTE2))
 34  
             {
 35  32
                 if(logger.isLoggable(Level.FINEST))
 36  
                 {
 37  16
                     logger.finest("Unsynchronisation required found bit at:" + i);
 38  
                 }
 39  32
                 return true;
 40  
             }
 41  
         }
 42  
 
 43  96
         return false;
 44  
     }
 45  
 
 46  
     /**
 47  
      * Unsynchronize an array of bytes, this should only be called if the decision has already been made to
 48  
      * unsynchronize the byte array
 49  
      * <p/>
 50  
      * In order to prevent a media player from incorrectly interpreting the contents of a tag, all $FF bytes
 51  
      * followed by a byte with value >=224 must be followed by a $00 byte (thus, $FF $F0 sequences become $FF $00 $F0).
 52  
      * Additionally because unsynchronisation is being applied any existing $FF $00 have to be converted to
 53  
      * $FF $00 $00
 54  
      *
 55  
      * @param abySource a byte array to be unsynchronized
 56  
      * @return a unsynchronized representation of the source
 57  
      */
 58  
     public static byte[] unsynchronize(byte[] abySource)
 59  
     {
 60  32
         ByteArrayInputStream   input = new ByteArrayInputStream(abySource);
 61  32
         ByteArrayOutputStream output = new ByteArrayOutputStream(abySource.length);
 62  
 
 63  32
         int count = 0;
 64  583212
         while (input.available() > 0)
 65  
         {
 66  583180
             int firstByte = input.read();
 67  583180
             count++;
 68  583180
             output.write(firstByte);
 69  583180
             if ((firstByte & MPEGFrameHeader.SYNC_BYTE1) == MPEGFrameHeader.SYNC_BYTE1)
 70  
             {
 71  
                 // if byte is $FF, we must check the following byte if there is one
 72  3608
                 if (input.available() > 0)
 73  
                 {
 74  3608
                     input.mark(1);  // remember where we were, if we don't need to unsynchronize
 75  3608
                     int secondByte = input.read();
 76  3608
                     if ((secondByte & MPEGFrameHeader.SYNC_BYTE2) == MPEGFrameHeader.SYNC_BYTE2)
 77  
                     {
 78  
                         // we need to unsynchronize here
 79  628
                         if(logger.isLoggable(Level.FINEST))
 80  
                         {
 81  314
                             logger.finest("Writing unsynchronisation bit at:" + count);
 82  
                         }
 83  628
                         output.write(0);
 84  
 
 85  
                     }
 86  2980
                     else if (secondByte == 0)
 87  
                     {
 88  
                         // we need to unsynchronize here
 89  2212
                         if(logger.isLoggable(Level.FINEST))
 90  
                         {
 91  1106
                             logger.finest("Inserting zero unsynchronisation bit at:" + count);
 92  
                         }
 93  2212
                         output.write(0);
 94  
                     }
 95  3608
                     input.reset();                     
 96  
                 }
 97  
             }
 98  583180
         }
 99  
         // if we needed to unsynchronize anything, and this tag ends with 0xff, we have to append a zero byte,
 100  
         // which will be removed on de-unsynchronization later
 101  32
         if ((abySource[abySource.length - 1] & MPEGFrameHeader.SYNC_BYTE1) == MPEGFrameHeader.SYNC_BYTE1)
 102  
         {
 103  0
             logger.finest("Adding unsynchronisation bit at end of stream");
 104  0
             output.write(0);
 105  
         }
 106  32
         return output.toByteArray();
 107  
     }
 108  
 
 109  
 
 110  
     /**
 111  
      * Synchronize an array of bytes, this should only be called if it has been determined the tag is unsynchronised
 112  
      * <p/>
 113  
      * Any patterns of the form $FF $00 should be replaced by $FF
 114  
      *
 115  
      * @param source a ByteBuffer to be unsynchronized
 116  
      * @return a synchronized representation of the source
 117  
      */
 118  
     public static ByteBuffer synchronize(ByteBuffer source)
 119  
     {
 120  61
         int bufferSize = source.limit();
 121  61
         ByteArrayOutputStream oBAOS = new ByteArrayOutputStream(bufferSize);
 122  61
         int position = 0;
 123  869547
         while (position < bufferSize)
 124  
         {
 125  869486
             int byteValue = source.get();
 126  869486
             position ++;
 127  869486
             oBAOS.write(byteValue);
 128  869486
             if ((byteValue & MPEGFrameHeader.SYNC_BYTE1) == MPEGFrameHeader.SYNC_BYTE1)
 129  
             {
 130  
                 // we are skipping if $00 byte but check not an end of stream
 131  5586
                 if (position < bufferSize)
 132  
                 {
 133  5586
                     int unsyncByteValue = source.get();
 134  5586
                     position++;
 135  
                     //If its the null byte we just ignore it
 136  5586
                     if (unsyncByteValue != 0)
 137  
                     {
 138  986
                         oBAOS.write(unsyncByteValue);
 139  
                     }
 140  
                 }
 141  
             }
 142  869486
         }
 143  61
         return ByteBuffer.wrap(oBAOS.toByteArray());
 144  
     }
 145  
 }