1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.extractor.ts; 17 18 import android.util.Log; 19 import com.google.android.exoplayer2.C; 20 import com.google.android.exoplayer2.Format; 21 import com.google.android.exoplayer2.extractor.ExtractorOutput; 22 import com.google.android.exoplayer2.extractor.TrackOutput; 23 import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; 24 import com.google.android.exoplayer2.util.MimeTypes; 25 import com.google.android.exoplayer2.util.ParsableByteArray; 26 27 /** 28 * Parses ID3 data and extracts individual text information frames. 29 */ 30 public final class Id3Reader implements ElementaryStreamReader { 31 32 private static final String TAG = "Id3Reader"; 33 34 private static final int ID3_HEADER_SIZE = 10; 35 36 private final ParsableByteArray id3Header; 37 38 private TrackOutput output; 39 40 // State that should be reset on seek. 41 private boolean writingSample; 42 43 // Per sample state that gets reset at the start of each sample. 44 private long sampleTimeUs; 45 private int sampleSize; 46 private int sampleBytesRead; 47 Id3Reader()48 public Id3Reader() { 49 id3Header = new ParsableByteArray(ID3_HEADER_SIZE); 50 } 51 52 @Override seek()53 public void seek() { 54 writingSample = false; 55 } 56 57 @Override createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator)58 public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { 59 idGenerator.generateNewId(); 60 output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA); 61 output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_ID3, 62 null, Format.NO_VALUE, null)); 63 } 64 65 @Override packetStarted(long pesTimeUs, boolean dataAlignmentIndicator)66 public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { 67 if (!dataAlignmentIndicator) { 68 return; 69 } 70 writingSample = true; 71 sampleTimeUs = pesTimeUs; 72 sampleSize = 0; 73 sampleBytesRead = 0; 74 } 75 76 @Override consume(ParsableByteArray data)77 public void consume(ParsableByteArray data) { 78 if (!writingSample) { 79 return; 80 } 81 int bytesAvailable = data.bytesLeft(); 82 if (sampleBytesRead < ID3_HEADER_SIZE) { 83 // We're still reading the ID3 header. 84 int headerBytesAvailable = Math.min(bytesAvailable, ID3_HEADER_SIZE - sampleBytesRead); 85 System.arraycopy(data.data, data.getPosition(), id3Header.data, sampleBytesRead, 86 headerBytesAvailable); 87 if (sampleBytesRead + headerBytesAvailable == ID3_HEADER_SIZE) { 88 // We've finished reading the ID3 header. Extract the sample size. 89 id3Header.setPosition(0); 90 if ('I' != id3Header.readUnsignedByte() || 'D' != id3Header.readUnsignedByte() 91 || '3' != id3Header.readUnsignedByte()) { 92 Log.w(TAG, "Discarding invalid ID3 tag"); 93 writingSample = false; 94 return; 95 } 96 id3Header.skipBytes(3); // version (2) + flags (1) 97 sampleSize = ID3_HEADER_SIZE + id3Header.readSynchSafeInt(); 98 } 99 } 100 // Write data to the output. 101 int bytesToWrite = Math.min(bytesAvailable, sampleSize - sampleBytesRead); 102 output.sampleData(data, bytesToWrite); 103 sampleBytesRead += bytesToWrite; 104 } 105 106 @Override packetFinished()107 public void packetFinished() { 108 if (!writingSample || sampleSize == 0 || sampleBytesRead != sampleSize) { 109 return; 110 } 111 output.sampleMetadata(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null); 112 writingSample = false; 113 } 114 115 } 116