| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| ID3Unsynchronization |
|
| 6.666666666666667;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 | } |