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