1 /* 2 * Copyright (C) 2017 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.source.dash; 17 18 import com.google.android.exoplayer2.C; 19 import com.google.android.exoplayer2.Format; 20 import com.google.android.exoplayer2.FormatHolder; 21 import com.google.android.exoplayer2.decoder.DecoderInputBuffer; 22 import com.google.android.exoplayer2.metadata.emsg.EventMessage; 23 import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder; 24 import com.google.android.exoplayer2.source.SampleStream; 25 import com.google.android.exoplayer2.source.dash.manifest.EventStream; 26 import com.google.android.exoplayer2.util.Util; 27 import java.io.IOException; 28 29 /** 30 * A {@link SampleStream} consisting of serialized {@link EventMessage}s read from an 31 * {@link EventStream}. 32 */ 33 /* package */ final class EventSampleStream implements SampleStream { 34 35 private final Format upstreamFormat; 36 private final EventMessageEncoder eventMessageEncoder; 37 38 private long[] eventTimesUs; 39 private boolean eventStreamAppendable; 40 private EventStream eventStream; 41 42 private boolean isFormatSentDownstream; 43 private int currentIndex; 44 private long pendingSeekPositionUs; 45 EventSampleStream( EventStream eventStream, Format upstreamFormat, boolean eventStreamAppendable)46 public EventSampleStream( 47 EventStream eventStream, Format upstreamFormat, boolean eventStreamAppendable) { 48 this.upstreamFormat = upstreamFormat; 49 this.eventStream = eventStream; 50 eventMessageEncoder = new EventMessageEncoder(); 51 pendingSeekPositionUs = C.TIME_UNSET; 52 eventTimesUs = eventStream.presentationTimesUs; 53 updateEventStream(eventStream, eventStreamAppendable); 54 } 55 eventStreamId()56 public String eventStreamId() { 57 return eventStream.id(); 58 } 59 updateEventStream(EventStream eventStream, boolean eventStreamAppendable)60 public void updateEventStream(EventStream eventStream, boolean eventStreamAppendable) { 61 long lastReadPositionUs = currentIndex == 0 ? C.TIME_UNSET : eventTimesUs[currentIndex - 1]; 62 63 this.eventStreamAppendable = eventStreamAppendable; 64 this.eventStream = eventStream; 65 this.eventTimesUs = eventStream.presentationTimesUs; 66 if (pendingSeekPositionUs != C.TIME_UNSET) { 67 seekToUs(pendingSeekPositionUs); 68 } else if (lastReadPositionUs != C.TIME_UNSET) { 69 currentIndex = 70 Util.binarySearchCeil( 71 eventTimesUs, lastReadPositionUs, /* inclusive= */ false, /* stayInBounds= */ false); 72 } 73 } 74 75 /** 76 * Seeks to the specified position in microseconds. 77 * 78 * @param positionUs The seek position in microseconds. 79 */ seekToUs(long positionUs)80 public void seekToUs(long positionUs) { 81 currentIndex = 82 Util.binarySearchCeil( 83 eventTimesUs, positionUs, /* inclusive= */ true, /* stayInBounds= */ false); 84 boolean isPendingSeek = eventStreamAppendable && currentIndex == eventTimesUs.length; 85 pendingSeekPositionUs = isPendingSeek ? positionUs : C.TIME_UNSET; 86 } 87 88 @Override isReady()89 public boolean isReady() { 90 return true; 91 } 92 93 @Override maybeThrowError()94 public void maybeThrowError() throws IOException { 95 // Do nothing. 96 } 97 98 @Override readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired)99 public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, 100 boolean formatRequired) { 101 if (formatRequired || !isFormatSentDownstream) { 102 formatHolder.format = upstreamFormat; 103 isFormatSentDownstream = true; 104 return C.RESULT_FORMAT_READ; 105 } 106 if (currentIndex == eventTimesUs.length) { 107 if (!eventStreamAppendable) { 108 buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); 109 return C.RESULT_BUFFER_READ; 110 } else { 111 return C.RESULT_NOTHING_READ; 112 } 113 } 114 int sampleIndex = currentIndex++; 115 byte[] serializedEvent = eventMessageEncoder.encode(eventStream.events[sampleIndex]); 116 if (serializedEvent != null) { 117 buffer.ensureSpaceForWrite(serializedEvent.length); 118 buffer.data.put(serializedEvent); 119 buffer.timeUs = eventTimesUs[sampleIndex]; 120 buffer.setFlags(C.BUFFER_FLAG_KEY_FRAME); 121 return C.RESULT_BUFFER_READ; 122 } else { 123 return C.RESULT_NOTHING_READ; 124 } 125 } 126 127 @Override skipData(long positionUs)128 public int skipData(long positionUs) { 129 int newIndex = 130 Math.max(currentIndex, Util.binarySearchCeil(eventTimesUs, positionUs, true, false)); 131 int skipped = newIndex - currentIndex; 132 currentIndex = newIndex; 133 return skipped; 134 } 135 136 } 137