Java Code Examples for com.google.android.exoplayer2.util.ParsableByteArray#readInt()
The following examples show how to use
com.google.android.exoplayer2.util.ParsableByteArray#readInt() .
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Mp3Extractor.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
/** * Returns {@link #SEEK_HEADER_XING}, {@link #SEEK_HEADER_INFO} or {@link #SEEK_HEADER_VBRI} if * the provided {@code frame} may have seeking metadata, or {@link #SEEK_HEADER_UNSET} otherwise. * If seeking metadata is present, {@code frame}'s position is advanced past the header. */ private static int getSeekFrameHeader(ParsableByteArray frame, int xingBase) { if (frame.limit() >= xingBase + 4) { frame.setPosition(xingBase); int headerData = frame.readInt(); if (headerData == SEEK_HEADER_XING || headerData == SEEK_HEADER_INFO) { return headerData; } } if (frame.limit() >= 40) { frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes. if (frame.readInt() == SEEK_HEADER_VBRI) { return SEEK_HEADER_VBRI; } } return SEEK_HEADER_UNSET; }
Example 2
Source File: MetadataUtil.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
private static @Nullable ApicFrame parseCoverArt(ParsableByteArray data) { int atomSize = data.readInt(); int atomType = data.readInt(); if (atomType == Atom.TYPE_data) { int fullVersionInt = data.readInt(); int flags = Atom.parseFullAtomFlags(fullVersionInt); String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null; if (mimeType == null) { Log.w(TAG, "Unrecognized cover art flags: " + flags); return null; } data.skipBytes(4); // empty (4) byte[] pictureData = new byte[atomSize - 16]; data.readBytes(pictureData, 0, pictureData.length); return new ApicFrame( mimeType, /* description= */ null, /* pictureType= */ PICTURE_TYPE_FRONT_COVER, pictureData); } Log.w(TAG, "Failed to parse cover art attribute"); return null; }
Example 3
Source File: AtomParsers.java From Telegram-FOSS with GNU General Public License v2.0 | 6 votes |
/** * Parses encryption data from an audio/video sample entry, returning a pair consisting of the * unencrypted atom type and a {@link TrackEncryptionBox}. Null is returned if no common * encryption sinf atom was present. */ private static Pair<Integer, TrackEncryptionBox> parseSampleEntryEncryptionData( ParsableByteArray parent, int position, int size) { int childPosition = parent.getPosition(); while (childPosition - position < size) { parent.setPosition(childPosition); int childAtomSize = parent.readInt(); Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); int childAtomType = parent.readInt(); if (childAtomType == Atom.TYPE_sinf) { Pair<Integer, TrackEncryptionBox> result = parseCommonEncryptionSinfFromParent(parent, childPosition, childAtomSize); if (result != null) { return result; } } childPosition += childAtomSize; } return null; }
Example 4
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
/** * Parses a udta atom. * * @param udtaAtom The udta (user data) atom to decode. * @param isQuickTime True for QuickTime media. False otherwise. * @return Parsed metadata, or null. */ public static Metadata parseUdta(Atom.LeafAtom udtaAtom, boolean isQuickTime) { if (isQuickTime) { // Meta boxes are regular boxes rather than full boxes in QuickTime. For now, don't try and // decode one. return null; } ParsableByteArray udtaData = udtaAtom.data; udtaData.setPosition(Atom.HEADER_SIZE); while (udtaData.bytesLeft() >= Atom.HEADER_SIZE) { int atomPosition = udtaData.getPosition(); int atomSize = udtaData.readInt(); int atomType = udtaData.readInt(); if (atomType == Atom.TYPE_meta) { udtaData.setPosition(atomPosition); return parseMetaAtom(udtaData, atomPosition + atomSize); } udtaData.skipBytes(atomSize - Atom.HEADER_SIZE); } return null; }
Example 5
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
/** * Parses an hdlr atom. * * @param hdlr The hdlr atom to decode. * @return The track type. */ private static int parseHdlr(ParsableByteArray hdlr) { hdlr.setPosition(Atom.FULL_HEADER_SIZE + 4); int trackType = hdlr.readInt(); if (trackType == TYPE_soun) { return C.TRACK_TYPE_AUDIO; } else if (trackType == TYPE_vide) { return C.TRACK_TYPE_VIDEO; } else if (trackType == TYPE_text || trackType == TYPE_sbtl || trackType == TYPE_subt || trackType == TYPE_clcp) { return C.TRACK_TYPE_TEXT; } else if (trackType == TYPE_meta) { return C.TRACK_TYPE_METADATA; } else { return C.TRACK_TYPE_UNKNOWN; } }
Example 6
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
/** * Parses the edts atom (defined in 14496-12 subsection 8.6.5). * * @param edtsAtom edts (edit box) atom to decode. * @return Pair of edit list durations and edit list media times, or a pair of nulls if they are * not present. */ private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) { Atom.LeafAtom elst; if (edtsAtom == null || (elst = edtsAtom.getLeafAtomOfType(Atom.TYPE_elst)) == null) { return Pair.create(null, null); } ParsableByteArray elstData = elst.data; elstData.setPosition(Atom.HEADER_SIZE); int fullAtom = elstData.readInt(); int version = Atom.parseFullAtomVersion(fullAtom); int entryCount = elstData.readUnsignedIntToInt(); long[] editListDurations = new long[entryCount]; long[] editListMediaTimes = new long[entryCount]; for (int i = 0; i < entryCount; i++) { editListDurations[i] = version == 1 ? elstData.readUnsignedLongToLong() : elstData.readUnsignedInt(); editListMediaTimes[i] = version == 1 ? elstData.readLong() : elstData.readInt(); int mediaRateInteger = elstData.readShort(); if (mediaRateInteger != 1) { // The extractor does not handle dwell edits (mediaRateInteger == 0). throw new IllegalArgumentException("Unsupported media rate."); } elstData.skipBytes(2); } return Pair.create(editListDurations, editListMediaTimes); }
Example 7
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 6 votes |
/** * Parses an hdlr atom. * * @param hdlr The hdlr atom to decode. * @return The track type. */ private static int parseHdlr(ParsableByteArray hdlr) { hdlr.setPosition(Atom.FULL_HEADER_SIZE + 4); int trackType = hdlr.readInt(); if (trackType == TYPE_soun) { return C.TRACK_TYPE_AUDIO; } else if (trackType == TYPE_vide) { return C.TRACK_TYPE_VIDEO; } else if (trackType == TYPE_text || trackType == TYPE_sbtl || trackType == TYPE_subt || trackType == TYPE_clcp) { return C.TRACK_TYPE_TEXT; } else if (trackType == TYPE_meta) { return C.TRACK_TYPE_METADATA; } else { return C.TRACK_TYPE_UNKNOWN; } }
Example 8
Source File: ProjectionDecoder.java From MediaSDK with Apache License 2.0 | 6 votes |
/** Parses MSHP data after the encoding_four_cc field. */ private static @Nullable ArrayList<Mesh> parseRawMshpData(ParsableByteArray input) { ArrayList<Mesh> meshes = new ArrayList<>(); int position = input.getPosition(); int limit = input.limit(); while (position < limit) { int childEnd = position + input.readInt(); if (childEnd <= position || childEnd > limit) { return null; } int childAtomType = input.readInt(); if (childAtomType == TYPE_MESH) { Mesh mesh = parseMesh(input); if (mesh == null) { return null; } meshes.add(mesh); } position = childEnd; input.setPosition(position); } return meshes; }
Example 9
Source File: TsDurationReader.java From TelePlus-Android with GNU General Public License v2.0 | 5 votes |
private static long readPcrFromPacket( ParsableByteArray packetBuffer, int startOfPacket, int pcrPid) { packetBuffer.setPosition(startOfPacket); if (packetBuffer.bytesLeft() < 5) { // Header = 4 bytes, adaptationFieldLength = 1 byte. return C.TIME_UNSET; } // Note: See ISO/IEC 13818-1, section 2.4.3.2 for details of the header format. int tsPacketHeader = packetBuffer.readInt(); if ((tsPacketHeader & 0x800000) != 0) { // transport_error_indicator != 0 means there are uncorrectable errors in this packet. return C.TIME_UNSET; } int pid = (tsPacketHeader & 0x1FFF00) >> 8; if (pid != pcrPid) { return C.TIME_UNSET; } boolean adaptationFieldExists = (tsPacketHeader & 0x20) != 0; if (!adaptationFieldExists) { return C.TIME_UNSET; } int adaptationFieldLength = packetBuffer.readUnsignedByte(); if (adaptationFieldLength >= 7 && packetBuffer.bytesLeft() >= 7) { int flags = packetBuffer.readUnsignedByte(); boolean pcrFlagSet = (flags & 0x10) == 0x10; if (pcrFlagSet) { byte[] pcrBytes = new byte[6]; packetBuffer.readBytes(pcrBytes, /* offset= */ 0, pcrBytes.length); return readPcrValueFromPcrBytes(pcrBytes); } } return C.TIME_UNSET; }
Example 10
Source File: MetadataUtil.java From Telegram-FOSS with GNU General Public License v2.0 | 5 votes |
@Nullable private static TextInformationFrame parseTextAttribute( int type, String id, ParsableByteArray data) { int atomSize = data.readInt(); int atomType = data.readInt(); if (atomType == Atom.TYPE_data) { data.skipBytes(8); // version (1), flags (3), empty (4) String value = data.readNullTerminatedString(atomSize - 16); return new TextInformationFrame(id, /* description= */ null, value); } Log.w(TAG, "Failed to parse text attribute: " + Atom.getAtomTypeString(type)); return null; }
Example 11
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 5 votes |
/** * Parses a mvhd atom (defined in 14496-12), returning the timescale for the movie. * * @param mvhd Contents of the mvhd atom to be parsed. * @return Timescale for the movie. */ private static long parseMvhd(ParsableByteArray mvhd) { mvhd.setPosition(Atom.HEADER_SIZE); int fullAtom = mvhd.readInt(); int version = Atom.parseFullAtomVersion(fullAtom); mvhd.skipBytes(version == 0 ? 8 : 16); return mvhd.readUnsignedInt(); }
Example 12
Source File: FragmentedMp4Extractor.java From MediaSDK with Apache License 2.0 | 5 votes |
private static void parseSaiz(TrackEncryptionBox encryptionBox, ParsableByteArray saiz, TrackFragment out) throws ParserException { int vectorSize = encryptionBox.perSampleIvSize; saiz.setPosition(Atom.HEADER_SIZE); int fullAtom = saiz.readInt(); int flags = Atom.parseFullAtomFlags(fullAtom); if ((flags & 0x01) == 1) { saiz.skipBytes(8); } int defaultSampleInfoSize = saiz.readUnsignedByte(); int sampleCount = saiz.readUnsignedIntToInt(); if (sampleCount != out.sampleCount) { throw new ParserException("Length mismatch: " + sampleCount + ", " + out.sampleCount); } int totalSize = 0; if (defaultSampleInfoSize == 0) { boolean[] sampleHasSubsampleEncryptionTable = out.sampleHasSubsampleEncryptionTable; for (int i = 0; i < sampleCount; i++) { int sampleInfoSize = saiz.readUnsignedByte(); totalSize += sampleInfoSize; sampleHasSubsampleEncryptionTable[i] = sampleInfoSize > vectorSize; } } else { boolean subsampleEncryption = defaultSampleInfoSize > vectorSize; totalSize += defaultSampleInfoSize * sampleCount; Arrays.fill(out.sampleHasSubsampleEncryptionTable, 0, sampleCount, subsampleEncryption); } out.initEncryptionData(totalSize); }
Example 13
Source File: AtomParsers.java From TelePlus-Android with GNU General Public License v2.0 | 5 votes |
/** * Parses the proj box from sv3d box, as specified by https://github.com/google/spatial-media. */ private static byte[] parseProjFromParent(ParsableByteArray parent, int position, int size) { int childPosition = position + Atom.HEADER_SIZE; while (childPosition - position < size) { parent.setPosition(childPosition); int childAtomSize = parent.readInt(); int childAtomType = parent.readInt(); if (childAtomType == Atom.TYPE_proj) { return Arrays.copyOfRange(parent.data, childPosition, childPosition + childAtomSize); } childPosition += childAtomSize; } return null; }
Example 14
Source File: MetadataUtil.java From Telegram-FOSS with GNU General Public License v2.0 | 5 votes |
@Nullable private static Id3Frame parseInternalAttribute(ParsableByteArray data, int endPosition) { String domain = null; String name = null; int dataAtomPosition = -1; int dataAtomSize = -1; while (data.getPosition() < endPosition) { int atomPosition = data.getPosition(); int atomSize = data.readInt(); int atomType = data.readInt(); data.skipBytes(4); // version (1), flags (3) if (atomType == Atom.TYPE_mean) { domain = data.readNullTerminatedString(atomSize - 12); } else if (atomType == Atom.TYPE_name) { name = data.readNullTerminatedString(atomSize - 12); } else { if (atomType == Atom.TYPE_data) { dataAtomPosition = atomPosition; dataAtomSize = atomSize; } data.skipBytes(atomSize - 12); } } if (domain == null || name == null || dataAtomPosition == -1) { return null; } data.setPosition(dataAtomPosition); data.skipBytes(16); // size (4), type (4), version (1), flags (3), empty (4) String value = data.readNullTerminatedString(dataAtomSize - 16); return new InternalFrame(domain, name, value); }
Example 15
Source File: MetadataUtil.java From Telegram with GNU General Public License v2.0 | 5 votes |
@Nullable private static Id3Frame parseInternalAttribute(ParsableByteArray data, int endPosition) { String domain = null; String name = null; int dataAtomPosition = -1; int dataAtomSize = -1; while (data.getPosition() < endPosition) { int atomPosition = data.getPosition(); int atomSize = data.readInt(); int atomType = data.readInt(); data.skipBytes(4); // version (1), flags (3) if (atomType == Atom.TYPE_mean) { domain = data.readNullTerminatedString(atomSize - 12); } else if (atomType == Atom.TYPE_name) { name = data.readNullTerminatedString(atomSize - 12); } else { if (atomType == Atom.TYPE_data) { dataAtomPosition = atomPosition; dataAtomSize = atomSize; } data.skipBytes(atomSize - 12); } } if (domain == null || name == null || dataAtomPosition == -1) { return null; } data.setPosition(dataAtomPosition); data.skipBytes(16); // size (4), type (4), version (1), flags (3), empty (4) String value = data.readNullTerminatedString(dataAtomSize - 16); return new InternalFrame(domain, name, value); }
Example 16
Source File: FragmentedMp4Extractor.java From TelePlus-Android with GNU General Public License v2.0 | 5 votes |
/** * Parses an mehd atom (defined in 14496-12). */ private static long parseMehd(ParsableByteArray mehd) { mehd.setPosition(Atom.HEADER_SIZE); int fullAtom = mehd.readInt(); int version = Atom.parseFullAtomVersion(fullAtom); return version == 0 ? mehd.readUnsignedInt() : mehd.readUnsignedLongToLong(); }
Example 17
Source File: FragmentedMp4Extractor.java From Telegram with GNU General Public License v2.0 | 5 votes |
/** * Parses a tfdt atom (defined in 14496-12). * * @return baseMediaDecodeTime The sum of the decode durations of all earlier samples in the * media, expressed in the media's timescale. */ private static long parseTfdt(ParsableByteArray tfdt) { tfdt.setPosition(Atom.HEADER_SIZE); int fullAtom = tfdt.readInt(); int version = Atom.parseFullAtomVersion(fullAtom); return version == 1 ? tfdt.readUnsignedLongToLong() : tfdt.readUnsignedInt(); }
Example 18
Source File: VbriSeeker.java From TelePlus-Android with GNU General Public License v2.0 | 4 votes |
/** * Returns a {@link VbriSeeker} for seeking in the stream, if required information is present. * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the * caller should reset it. * * @param inputLength The length of the stream in bytes, or {@link C#LENGTH_UNSET} if unknown. * @param position The position of the start of this frame in the stream. * @param mpegAudioHeader The MPEG audio header associated with the frame. * @param frame The data in this audio frame, with its position set to immediately after the * 'VBRI' tag. * @return A {@link VbriSeeker} for seeking in the stream, or {@code null} if the required * information is not present. */ public static VbriSeeker create(long inputLength, long position, MpegAudioHeader mpegAudioHeader, ParsableByteArray frame) { frame.skipBytes(10); int numFrames = frame.readInt(); if (numFrames <= 0) { return null; } int sampleRate = mpegAudioHeader.sampleRate; long durationUs = Util.scaleLargeTimestamp(numFrames, C.MICROS_PER_SECOND * (sampleRate >= 32000 ? 1152 : 576), sampleRate); int entryCount = frame.readUnsignedShort(); int scale = frame.readUnsignedShort(); int entrySize = frame.readUnsignedShort(); frame.skipBytes(2); long minPosition = position + mpegAudioHeader.frameSize; // Read table of contents entries. long[] timesUs = new long[entryCount]; long[] positions = new long[entryCount]; for (int index = 0; index < entryCount; index++) { timesUs[index] = (index * durationUs) / entryCount; // Ensure positions do not fall within the frame containing the VBRI header. This constraint // will normally only apply to the first entry in the table. positions[index] = Math.max(position, minPosition); int segmentSize; switch (entrySize) { case 1: segmentSize = frame.readUnsignedByte(); break; case 2: segmentSize = frame.readUnsignedShort(); break; case 3: segmentSize = frame.readUnsignedInt24(); break; case 4: segmentSize = frame.readUnsignedIntToInt(); break; default: return null; } position += segmentSize * scale; } if (inputLength != C.LENGTH_UNSET && inputLength != position) { Log.w(TAG, "VBRI data size mismatch: " + inputLength + ", " + position); } return new VbriSeeker(timesUs, positions, durationUs); }
Example 19
Source File: MetadataUtil.java From K-Sonic with MIT License | 4 votes |
/** * Parses a single ilst element from a {@link ParsableByteArray}. The element is read starting * from the current position of the {@link ParsableByteArray}, and the position is advanced by * the size of the element. The position is advanced even if the element's type is unrecognized. * * @param ilst Holds the data to be parsed. * @return The parsed element, or null if the element's type was not recognized. */ public static Metadata.Entry parseIlstElement(ParsableByteArray ilst) { int position = ilst.getPosition(); int endPosition = position + ilst.readInt(); int type = ilst.readInt(); int typeTopByte = (type >> 24) & 0xFF; try { if (typeTopByte == '\u00A9' /* Copyright char */ || typeTopByte == '\uFFFD' /* Replacement char */) { int shortType = type & 0x00FFFFFF; if (shortType == SHORT_TYPE_COMMENT) { return parseCommentAttribute(type, ilst); } else if (shortType == SHORT_TYPE_NAME_1 || shortType == SHORT_TYPE_NAME_2) { return parseTextAttribute(type, "TIT2", ilst); } else if (shortType == SHORT_TYPE_COMPOSER_1 || shortType == SHORT_TYPE_COMPOSER_2) { return parseTextAttribute(type, "TCOM", ilst); } else if (shortType == SHORT_TYPE_YEAR) { return parseTextAttribute(type, "TDRC", ilst); } else if (shortType == SHORT_TYPE_ARTIST) { return parseTextAttribute(type, "TPE1", ilst); } else if (shortType == SHORT_TYPE_ENCODER) { return parseTextAttribute(type, "TSSE", ilst); } else if (shortType == SHORT_TYPE_ALBUM) { return parseTextAttribute(type, "TALB", ilst); } else if (shortType == SHORT_TYPE_LYRICS) { return parseTextAttribute(type, "USLT", ilst); } else if (shortType == SHORT_TYPE_GENRE) { return parseTextAttribute(type, "TCON", ilst); } else if (shortType == TYPE_GROUPING) { return parseTextAttribute(type, "TIT1", ilst); } } else if (type == TYPE_GENRE) { return parseStandardGenreAttribute(ilst); } else if (type == TYPE_DISK_NUMBER) { return parseIndexAndCountAttribute(type, "TPOS", ilst); } else if (type == TYPE_TRACK_NUMBER) { return parseIndexAndCountAttribute(type, "TRCK", ilst); } else if (type == TYPE_TEMPO) { return parseUint8Attribute(type, "TBPM", ilst, true, false); } else if (type == TYPE_COMPILATION) { return parseUint8Attribute(type, "TCMP", ilst, true, true); } else if (type == TYPE_COVER_ART) { return parseCoverArt(ilst); } else if (type == TYPE_ALBUM_ARTIST) { return parseTextAttribute(type, "TPE2", ilst); } else if (type == TYPE_SORT_TRACK_NAME) { return parseTextAttribute(type, "TSOT", ilst); } else if (type == TYPE_SORT_ALBUM) { return parseTextAttribute(type, "TSO2", ilst); } else if (type == TYPE_SORT_ARTIST) { return parseTextAttribute(type, "TSOA", ilst); } else if (type == TYPE_SORT_ALBUM_ARTIST) { return parseTextAttribute(type, "TSOP", ilst); } else if (type == TYPE_SORT_COMPOSER) { return parseTextAttribute(type, "TSOC", ilst); } else if (type == TYPE_RATING) { return parseUint8Attribute(type, "ITUNESADVISORY", ilst, false, false); } else if (type == TYPE_GAPLESS_ALBUM) { return parseUint8Attribute(type, "ITUNESGAPLESS", ilst, false, true); } else if (type == TYPE_TV_SORT_SHOW) { return parseTextAttribute(type, "TVSHOWSORT", ilst); } else if (type == TYPE_TV_SHOW) { return parseTextAttribute(type, "TVSHOW", ilst); } else if (type == TYPE_INTERNAL) { return parseInternalAttribute(ilst, endPosition); } Log.d(TAG, "Skipped unknown metadata entry: " + Atom.getAtomTypeString(type)); return null; } finally { ilst.setPosition(endPosition); } }
Example 20
Source File: AtomParsers.java From MediaSDK with Apache License 2.0 | 4 votes |
/** * Parses a tkhd atom (defined in 14496-12). * * @return An object containing the parsed data. */ private static TkhdData parseTkhd(ParsableByteArray tkhd) { tkhd.setPosition(Atom.HEADER_SIZE); int fullAtom = tkhd.readInt(); int version = Atom.parseFullAtomVersion(fullAtom); tkhd.skipBytes(version == 0 ? 8 : 16); int trackId = tkhd.readInt(); tkhd.skipBytes(4); boolean durationUnknown = true; int durationPosition = tkhd.getPosition(); int durationByteCount = version == 0 ? 4 : 8; for (int i = 0; i < durationByteCount; i++) { if (tkhd.data[durationPosition + i] != -1) { durationUnknown = false; break; } } long duration; if (durationUnknown) { tkhd.skipBytes(durationByteCount); duration = C.TIME_UNSET; } else { duration = version == 0 ? tkhd.readUnsignedInt() : tkhd.readUnsignedLongToLong(); if (duration == 0) { // 0 duration normally indicates that the file is fully fragmented (i.e. all of the media // samples are in fragments). Treat as unknown. duration = C.TIME_UNSET; } } tkhd.skipBytes(16); int a00 = tkhd.readInt(); int a01 = tkhd.readInt(); tkhd.skipBytes(4); int a10 = tkhd.readInt(); int a11 = tkhd.readInt(); int rotationDegrees; int fixedOne = 65536; if (a00 == 0 && a01 == fixedOne && a10 == -fixedOne && a11 == 0) { rotationDegrees = 90; } else if (a00 == 0 && a01 == -fixedOne && a10 == fixedOne && a11 == 0) { rotationDegrees = 270; } else if (a00 == -fixedOne && a01 == 0 && a10 == 0 && a11 == -fixedOne) { rotationDegrees = 180; } else { // Only 0, 90, 180 and 270 are supported. Treat anything else as 0. rotationDegrees = 0; } return new TkhdData(trackId, duration, rotationDegrees); }