@Override public void displayTracks(String spotifyID, String artistName) { if (mTwoPane) { Bundle b = new Bundle(); b.putString(Utils.SPOTIFY_ID, spotifyID); b.putString(Utils.ARTIST_NAME, artistName); mArtistName = artistName; mTrackFragment = new TrackFragment(); mTrackFragment.setArguments(b); getSupportFragmentManager() .beginTransaction() .replace(R.id.track_fragment_container, mTrackFragment, TRACK_FRAGMENT_TAG) .commit(); } else { Intent trackIntent = new Intent(MainActivity.this, TrackActivity.class); trackIntent.putExtra(Utils.ARTIST_NAME, artistName); trackIntent.putExtra(Utils.SPOTIFY_ID, spotifyID); trackIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(trackIntent); } } // displayTracks
private static void parseSaiz( TrackEncryptionBox encryptionBox, ParsableByteArray saiz, TrackFragment out) throws ParserException { int vectorSize = encryptionBox.initializationVectorSize; 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.length) { throw new ParserException("Length mismatch: " + sampleCount + ", " + out.length); } 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); }
private void readEncryptionData(ExtractorInput input) throws IOException, InterruptedException { int bytesToSkip = (int) (fragmentRun.auxiliaryDataPosition - input.getPosition()); checkState(bytesToSkip >= 0, "Offset to encryption data was negative."); input.skipFully(bytesToSkip); fragmentRun.fillEncryptionData(input); parserState = STATE_READING_SAMPLE_START; }
private static void parseSenc(ParsableByteArray senc, int offset, TrackFragment out) throws ParserException { senc.setPosition(Atom.HEADER_SIZE + offset); int fullAtom = senc.readInt(); int flags = Atom.parseFullAtomFlags(fullAtom); if ((flags & 0x01 /* override_track_encryption_box_parameters */) != 0) { // TODO: Implement this. throw new ParserException("Overriding TrackEncryptionBox parameters is unsupported."); } boolean subsampleEncryption = (flags & 0x02 /* use_subsample_encryption */) != 0; int sampleCount = senc.readUnsignedIntToInt(); if (sampleCount != out.length) { throw new ParserException("Length mismatch: " + sampleCount + ", " + out.length); } Arrays.fill(out.sampleHasSubsampleEncryptionTable, 0, sampleCount, subsampleEncryption); out.initEncryptionData(senc.bytesLeft()); out.fillEncryptionData(senc); }
/** * Parses a tfhd atom (defined in 14496-12). * * @param extendsDefaults Default sample values from the trex atom. * @param tfhd The tfhd atom to parse. * @param out The track fragment to populate with data from the tfhd atom. */ private static void parseTfhd( DefaultSampleValues extendsDefaults, ParsableByteArray tfhd, TrackFragment out) { tfhd.setPosition(Atom.HEADER_SIZE); int fullAtom = tfhd.readInt(); int flags = Atom.parseFullAtomFlags(fullAtom); tfhd.skipBytes(4); // trackId if ((flags & 0x01 /* base_data_offset_present */) != 0) { long baseDataPosition = tfhd.readUnsignedLongToLong(); out.dataPosition = baseDataPosition; out.auxiliaryDataPosition = baseDataPosition; } int defaultSampleDescriptionIndex = ((flags & 0x02 /* default_sample_description_index_present */) != 0) ? tfhd.readUnsignedIntToInt() - 1 : extendsDefaults.sampleDescriptionIndex; int defaultSampleDuration = ((flags & 0x08 /* default_sample_duration_present */) != 0) ? tfhd.readUnsignedIntToInt() : extendsDefaults.duration; int defaultSampleSize = ((flags & 0x10 /* default_sample_size_present */) != 0) ? tfhd.readUnsignedIntToInt() : extendsDefaults.size; int defaultSampleFlags = ((flags & 0x20 /* default_sample_flags_present */) != 0) ? tfhd.readUnsignedIntToInt() : extendsDefaults.flags; out.header = new DefaultSampleValues( defaultSampleDescriptionIndex, defaultSampleDuration, defaultSampleSize, defaultSampleFlags); }
@Nullable @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View view = inflater.inflate(R.layout.fragment_schedule, container, false); scheduleView = (RecyclerView) view.findViewById(R.id.schedule); swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeContainer); mActivity = getActivity(); scheduleView.setLayoutManager(new LinearLayoutManager(mActivity)); scheduleView.setItemAnimator(new DefaultItemAnimator()); swipeRefreshLayout.setEnabled(false); swipeRefreshLayout.post( new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(true); } }); Call<List<Room>> roomCall = COSCUPClient.get().room(); roomCall.enqueue( new Callback<List<Room>>() { @Override public void onResponse(Call<List<Room>> call, Response<List<Room>> response) { if (response.isSuccessful()) { List<Room> rooms = response.body(); HashMap<String, String> roomMap = new HashMap(); for (Room room : rooms) { roomMap.put(room.getRoom(), room.getName()); } getType(roomMap); } else { loadOfflineScedule(); } } @Override public void onFailure(Call<List<Room>> call, Throwable t) { loadOfflineScedule(); } }); return view; }
@Override public void onCreateContextMenu( ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfoIn) { if (menuInfoIn == null) return; AdapterView.AdapterContextMenuInfo mi = (AdapterView.AdapterContextMenuInfo) menuInfoIn; selectedPosition = mi.position; adapter.getCursor().moveToPosition(selectedPosition); String mimeType = adapter .getCursor() .getString( adapter.getCursor().getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.MIME_TYPE)); if (isSong(mimeType)) { super.onCreateContextMenu(menu, view, menuInfoIn); } }
@Override public void onListItemClick(ListView l, View v, int position, long id) { selectedPosition = position; adapter.getCursor().moveToPosition(selectedPosition); String mimeType = adapter .getCursor() .getString( adapter.getCursor().getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.MIME_TYPE)); if ("artist".equals(mimeType)) { viewCategory(MusicContract.Artist.getMembersUri(id)); } else if ("album".equals(mimeType)) { viewCategory(MusicContract.Album.getMembersUri(id)); } else if (isSong(mimeType)) { super.onListItemClick(l, v, position, id); } }
/** * Parses a saio atom (defined in 14496-12). * * @param saio The saio atom to parse. * @param out The track fragment to populate with data from the saio atom. */ private static void parseSaio(ParsableByteArray saio, TrackFragment out) throws ParserException { saio.setPosition(Atom.HEADER_SIZE); int fullAtom = saio.readInt(); int flags = Atom.parseFullAtomFlags(fullAtom); if ((flags & 0x01) == 1) { saio.skipBytes(8); } int entryCount = saio.readUnsignedIntToInt(); if (entryCount != 1) { // We only support one trun element currently, so always expect one entry. throw new ParserException("Unexpected saio entry count: " + entryCount); } int version = Atom.parseFullAtomVersion(fullAtom); out.auxiliaryDataPosition += version == 0 ? saio.readUnsignedInt() : saio.readUnsignedLongToLong(); }
@Override public void onArtistsSearched() { if (mTwoPane) { mTrackFragment.clearTracks(); } }
/** * Attempts to extract the next sample in the current mdat atom. * * <p>If there are no more samples in the current mdat atom then the parser state is transitioned * to {@link #STATE_READING_ATOM_HEADER} and {@code false} is returned. * * <p>It is possible for a sample to be extracted in part in the case that an exception is thrown. * In this case the method can be called again to extract the remainder of the sample. * * @param input The {@link ExtractorInput} from which to read data. * @return True if a sample was extracted. False otherwise. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. */ private boolean readSample(ExtractorInput input) throws IOException, InterruptedException { if (sampleIndex == 0) { int bytesToSkip = (int) (fragmentRun.dataPosition - input.getPosition()); checkState(bytesToSkip >= 0, "Offset to sample data was negative."); input.skipFully(bytesToSkip); } if (sampleIndex >= fragmentRun.length) { int bytesToSkip = (int) (endOfMdatPosition - input.getPosition()); checkState(bytesToSkip >= 0, "Offset to end of mdat was negative."); input.skipFully(bytesToSkip); // We've run out of samples in the current mdat atom. enterReadingAtomHeaderState(); return false; } if (parserState == STATE_READING_SAMPLE_START) { sampleSize = fragmentRun.sampleSizeTable[sampleIndex]; if (fragmentRun.definesEncryptionData) { sampleBytesWritten = appendSampleEncryptionData(fragmentRun.sampleEncryptionData); sampleSize += sampleBytesWritten; } else { sampleBytesWritten = 0; } sampleCurrentNalBytesRemaining = 0; parserState = STATE_READING_SAMPLE_CONTINUE; } if (track.nalUnitLengthFieldLength != -1) { // Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case // they're only 1 or 2 bytes long. byte[] nalLengthData = nalLength.data; nalLengthData[0] = 0; nalLengthData[1] = 0; nalLengthData[2] = 0; int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength; int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength; // NAL units are length delimited, but the decoder requires start code delimited units. // Loop until we've written the sample to the track output, replacing length delimiters with // start codes as we encounter them. while (sampleBytesWritten < sampleSize) { if (sampleCurrentNalBytesRemaining == 0) { // Read the NAL length so that we know where we find the next one. input.readFully(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength); nalLength.setPosition(0); sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt(); // Write a start code for the current NAL unit. nalStartCode.setPosition(0); trackOutput.sampleData(nalStartCode, 4); sampleBytesWritten += 4; sampleSize += nalUnitLengthFieldLengthDiff; } else { // Write the payload of the NAL unit. int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false); sampleBytesWritten += writtenBytes; sampleCurrentNalBytesRemaining -= writtenBytes; } } } else { while (sampleBytesWritten < sampleSize) { int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false); sampleBytesWritten += writtenBytes; } } long sampleTimeUs = fragmentRun.getSamplePresentationTime(sampleIndex) * 1000L; int sampleFlags = (fragmentRun.definesEncryptionData ? C.SAMPLE_FLAG_ENCRYPTED : 0) | (fragmentRun.sampleIsSyncFrameTable[sampleIndex] ? C.SAMPLE_FLAG_SYNC : 0); int sampleDescriptionIndex = fragmentRun.header.sampleDescriptionIndex; byte[] encryptionKey = fragmentRun.definesEncryptionData ? track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId : null; trackOutput.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey); sampleIndex++; parserState = STATE_READING_SAMPLE_START; return true; }
/** * Parses a trun atom (defined in 14496-12). * * @param track The corresponding track. * @param defaultSampleValues Default sample values. * @param decodeTime The decode time. * @param trun The trun atom to parse. * @param out The {@TrackFragment} into which parsed data should be placed. */ private static void parseTrun( Track track, DefaultSampleValues defaultSampleValues, long decodeTime, int workaroundFlags, ParsableByteArray trun, TrackFragment out) { trun.setPosition(Atom.HEADER_SIZE); int fullAtom = trun.readInt(); int flags = Atom.parseFullAtomFlags(fullAtom); int sampleCount = trun.readUnsignedIntToInt(); if ((flags & 0x01 /* data_offset_present */) != 0) { out.dataPosition += trun.readInt(); } boolean firstSampleFlagsPresent = (flags & 0x04 /* first_sample_flags_present */) != 0; int firstSampleFlags = defaultSampleValues.flags; if (firstSampleFlagsPresent) { firstSampleFlags = trun.readUnsignedIntToInt(); } boolean sampleDurationsPresent = (flags & 0x100 /* sample_duration_present */) != 0; boolean sampleSizesPresent = (flags & 0x200 /* sample_size_present */) != 0; boolean sampleFlagsPresent = (flags & 0x400 /* sample_flags_present */) != 0; boolean sampleCompositionTimeOffsetsPresent = (flags & 0x800 /* sample_composition_time_offsets_present */) != 0; out.initTables(sampleCount); int[] sampleSizeTable = out.sampleSizeTable; int[] sampleCompositionTimeOffsetTable = out.sampleCompositionTimeOffsetTable; long[] sampleDecodingTimeTable = out.sampleDecodingTimeTable; boolean[] sampleIsSyncFrameTable = out.sampleIsSyncFrameTable; long timescale = track.timescale; long cumulativeTime = decodeTime; boolean workaroundEveryVideoFrameIsSyncFrame = track.type == Track.TYPE_vide && (workaroundFlags & WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME) != 0; for (int i = 0; i < sampleCount; i++) { // Use trun values if present, otherwise tfhd, otherwise trex. int sampleDuration = sampleDurationsPresent ? trun.readUnsignedIntToInt() : defaultSampleValues.duration; int sampleSize = sampleSizesPresent ? trun.readUnsignedIntToInt() : defaultSampleValues.size; int sampleFlags = (i == 0 && firstSampleFlagsPresent) ? firstSampleFlags : sampleFlagsPresent ? trun.readInt() : defaultSampleValues.flags; if (sampleCompositionTimeOffsetsPresent) { // The BMFF spec (ISO 14496-12) states that sample offsets should be unsigned integers in // version 0 trun boxes, however a significant number of streams violate the spec and use // signed integers instead. It's safe to always parse sample offsets as signed integers // here, because unsigned integers will still be parsed correctly (unless their top bit is // set, which is never true in practice because sample offsets are always small). int sampleOffset = trun.readInt(); sampleCompositionTimeOffsetTable[i] = (int) ((sampleOffset * 1000) / timescale); } else { sampleCompositionTimeOffsetTable[i] = 0; } sampleDecodingTimeTable[i] = Util.scaleLargeTimestamp(cumulativeTime, 1000, timescale); sampleSizeTable[i] = sampleSize; sampleIsSyncFrameTable[i] = ((sampleFlags >> 16) & 0x1) == 0 && (!workaroundEveryVideoFrameIsSyncFrame || i == 0); cumulativeTime += sampleDuration; } }
private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException { fragmentRun.reset(); parseMoof(track, extendsDefaults, moof, fragmentRun, workaroundFlags, extendedTypeScratch); sampleIndex = 0; }
private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException { if (atomHeaderBytesRead == 0) { // Read the standard length atom header. if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) { return false; } atomHeaderBytesRead = Atom.HEADER_SIZE; atomHeader.setPosition(0); atomSize = atomHeader.readUnsignedInt(); atomType = atomHeader.readInt(); } if (atomSize == Atom.LONG_SIZE_PREFIX) { // Read the extended atom size. int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE; input.readFully(atomHeader.data, Atom.HEADER_SIZE, headerBytesRemaining); atomHeaderBytesRead += headerBytesRemaining; atomSize = atomHeader.readUnsignedLongToLong(); } long atomPosition = input.getPosition() - atomHeaderBytesRead; if (atomType == Atom.TYPE_moof) { // The data positions may be updated when parsing the tfhd/trun. fragmentRun.auxiliaryDataPosition = atomPosition; fragmentRun.dataPosition = atomPosition; } if (atomType == Atom.TYPE_mdat) { endOfMdatPosition = atomPosition + atomSize; if (!haveOutputSeekMap) { extractorOutput.seekMap(SeekMap.UNSEEKABLE); haveOutputSeekMap = true; } if (fragmentRun.sampleEncryptionDataNeedsFill) { parserState = STATE_READING_ENCRYPTION_DATA; } else { parserState = STATE_READING_SAMPLE_START; } return true; } if (shouldParseContainerAtom(atomType)) { long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE; containerAtoms.add(new ContainerAtom(atomType, endPosition)); enterReadingAtomHeaderState(); } else if (shouldParseLeafAtom(atomType)) { // We don't support parsing of leaf atoms that define extended atom sizes, or that have // lengths greater than Integer.MAX_VALUE. checkState(atomHeaderBytesRead == Atom.HEADER_SIZE); checkState(atomSize <= Integer.MAX_VALUE); atomData = new ParsableByteArray((int) atomSize); System.arraycopy(atomHeader.data, 0, atomData.data, 0, Atom.HEADER_SIZE); parserState = STATE_READING_ATOM_PAYLOAD; } else { // We don't support skipping of atoms that have lengths greater than Integer.MAX_VALUE. checkState(atomSize <= Integer.MAX_VALUE); atomData = null; parserState = STATE_READING_ATOM_PAYLOAD; } return true; }