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