Java源码示例:com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement

示例1
private void parseStreamElementStartTag(XmlPullParser parser) throws ParserException {
  type = parseType(parser);
  putNormalizedAttribute(KEY_TYPE, type);
  if (type == StreamElement.TYPE_TEXT) {
    subType = parseRequiredString(parser, KEY_SUB_TYPE);
  } else {
    subType = parser.getAttributeValue(null, KEY_SUB_TYPE);
  }
  name = parser.getAttributeValue(null, KEY_NAME);
  qualityLevels = parseInt(parser, KEY_QUALITY_LEVELS, -1);
  url = parseRequiredString(parser, KEY_URL);
  maxWidth = parseInt(parser, KEY_MAX_WIDTH, -1);
  maxHeight = parseInt(parser, KEY_MAX_HEIGHT, -1);
  displayWidth = parseInt(parser, KEY_DISPLAY_WIDTH, -1);
  displayHeight = parseInt(parser, KEY_DISPLAY_HEIGHT, -1);
  language = parser.getAttributeValue(null, KEY_LANGUAGE);
  timescale = parseInt(parser, KEY_TIME_SCALE, -1);
  if (timescale == -1) {
    timescale = (Long) getNormalizedAttribute(KEY_TIME_SCALE);
  }
  startTimes = new ArrayList<Long>();
}
 
示例2
@Override
public void continueBuffering(long playbackPositionUs) {
  if (manifestFetcher == null || !currentManifest.isLive || fatalError != null) {
    return;
  }

  SmoothStreamingManifest newManifest = manifestFetcher.getManifest();
  if (currentManifest != newManifest && newManifest != null) {
    StreamElement currentElement = getElement(currentManifest);
    StreamElement newElement = getElement(newManifest);
    if (newElement.chunkCount == 0) {
      currentManifestChunkOffset += currentElement.chunkCount;
    } else if (currentElement.chunkCount > 0) {
      currentManifestChunkOffset += currentElement.getChunkIndex(newElement.getStartTimeUs(0));
    }
    currentManifest = newManifest;
    finishedCurrentManifest = false;
  }

  if (finishedCurrentManifest && (SystemClock.elapsedRealtime()
      > manifestFetcher.getManifestLoadTimestamp() + MINIMUM_MANIFEST_REFRESH_PERIOD_MS)) {
    manifestFetcher.requestRefresh();
  }
}
 
示例3
@Override
public void addChild(Object child) {
  if (child instanceof StreamElement) {
    streamElements.add((StreamElement) child);
  } else if (child instanceof ProtectionElement) {
    Assertions.checkState(protectionElement == null);
    protectionElement = (ProtectionElement) child;
  }
}
 
示例4
@Override
public Object build() {
  StreamElement[] streamElementArray = new StreamElement[streamElements.size()];
  streamElements.toArray(streamElementArray);
  return new SmoothStreamingManifest(majorVersion, minorVersion, timescale, duration,
      dvrWindowLength, lookAheadCount, isLive, protectionElement, streamElementArray);
}
 
示例5
private int parseType(XmlPullParser parser) throws ParserException {
  String value = parser.getAttributeValue(null, KEY_TYPE);
  if (value != null) {
    if (KEY_TYPE_AUDIO.equalsIgnoreCase(value)) {
      return StreamElement.TYPE_AUDIO;
    } else if (KEY_TYPE_VIDEO.equalsIgnoreCase(value)) {
      return StreamElement.TYPE_VIDEO;
    } else if (KEY_TYPE_TEXT.equalsIgnoreCase(value)) {
      return StreamElement.TYPE_TEXT;
    } else {
      throw new ParserException("Invalid key value[" + value + "]");
    }
  }
  throw new MissingFieldException(KEY_TYPE);
}
 
示例6
@Override
public Object build() {
  TrackElement[] trackElements = new TrackElement[tracks.size()];
  tracks.toArray(trackElements);
  return new StreamElement(baseUri, url, type, subType, timescale, name, qualityLevels,
      maxWidth, maxHeight, displayWidth, displayHeight, language, trackElements, startTimes,
      lastChunkDuration);
}
 
示例7
/**
 * For live playbacks, determines the seek position that snaps playback to be
 * {@link #liveEdgeLatencyUs} behind the live edge of the current manifest
 *
 * @return The seek position in microseconds.
 */
private long getLiveSeekPosition() {
  long liveEdgeTimestampUs = Long.MIN_VALUE;
  for (int i = 0; i < currentManifest.streamElements.length; i++) {
    StreamElement streamElement = currentManifest.streamElements[i];
    if (streamElement.chunkCount > 0) {
      long elementLiveEdgeTimestampUs =
          streamElement.getStartTimeUs(streamElement.chunkCount - 1)
          + streamElement.getChunkDurationUs(streamElement.chunkCount - 1);
      liveEdgeTimestampUs = Math.max(liveEdgeTimestampUs, elementLiveEdgeTimestampUs);
    }
  }
  return liveEdgeTimestampUs - liveEdgeLatencyUs;
}
 
示例8
public SmoothStreamMediaParser(ElementParser parent, Uri baseUri) {
  super(parent, baseUri, TAG);
  lookAheadCount = -1;
  protectionElement = null;
  streamElements = new LinkedList<StreamElement>();
}
 
示例9
@Override
public void parseStartTag(XmlPullParser parser) throws ParserException {
  int type = (Integer) getNormalizedAttribute(KEY_TYPE);
  content = null;
  String value;

  index = parseInt(parser, KEY_INDEX, -1);
  bitrate = parseRequiredInt(parser, KEY_BITRATE);
  nalUnitLengthField = parseInt(parser, KEY_NAL_UNIT_LENGTH_FIELD, 4);

  if (type == StreamElement.TYPE_VIDEO) {
    maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT);
    maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH);
    mimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
  } else {
    maxHeight = -1;
    maxWidth = -1;
    String fourCC = parser.getAttributeValue(null, KEY_FOUR_CC);
    // If fourCC is missing and the stream type is audio, we assume AAC.
    mimeType = fourCC != null ? fourCCToMimeType(fourCC)
        : type == StreamElement.TYPE_AUDIO ? MimeTypes.AUDIO_AAC : null;
  }

  if (type == StreamElement.TYPE_AUDIO) {
    samplingRate = parseRequiredInt(parser, KEY_SAMPLING_RATE);
    channels = parseRequiredInt(parser, KEY_CHANNELS);
    bitPerSample = parseRequiredInt(parser, KEY_BITS_PER_SAMPLE);
    packetSize = parseRequiredInt(parser, KEY_PACKET_SIZE);
    audioTag = parseRequiredInt(parser, KEY_AUDIO_TAG);
  } else {
    samplingRate = -1;
    channels = -1;
    bitPerSample = -1;
    packetSize = -1;
    audioTag = -1;
  }

  value = parser.getAttributeValue(null, KEY_CODEC_PRIVATE_DATA);
  if (value != null && value.length() > 0) {
    byte[] codecPrivateData = hexStringToByteArray(value);
    byte[][] split = CodecSpecificDataUtil.splitNalUnits(codecPrivateData);
    if (split == null) {
      csd.add(codecPrivateData);
    } else {
      for (int i = 0; i < split.length; i++) {
        Pair<Integer, Integer> spsParameters = CodecSpecificDataUtil.parseSpsNalUnit(split[i]);
        if (spsParameters != null) {
          profile = spsParameters.first;
          level = spsParameters.second;
        }
        csd.add(split[i]);
      }
    }
  }
}
 
示例10
private SmoothStreamingChunkSource(ManifestFetcher<SmoothStreamingManifest> manifestFetcher,
    SmoothStreamingManifest initialManifest, int streamElementIndex, int[] trackIndices,
    DataSource dataSource, FormatEvaluator formatEvaluator, long liveEdgeLatencyMs) {
  this.manifestFetcher = manifestFetcher;
  this.streamElementIndex = streamElementIndex;
  this.currentManifest = initialManifest;
  this.dataSource = dataSource;
  this.formatEvaluator = formatEvaluator;
  this.liveEdgeLatencyUs = liveEdgeLatencyMs * 1000;

  StreamElement streamElement = getElement(initialManifest);
  trackInfo = new TrackInfo(streamElement.tracks[0].mimeType, initialManifest.durationUs);
  evaluation = new Evaluation();

  TrackEncryptionBox[] trackEncryptionBoxes = null;
  ProtectionElement protectionElement = initialManifest.protectionElement;
  if (protectionElement != null) {
    byte[] keyId = getKeyId(protectionElement.data);
    trackEncryptionBoxes = new TrackEncryptionBox[1];
    trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
    psshInfo = Collections.singletonMap(protectionElement.uuid, protectionElement.data);
  } else {
    psshInfo = null;
  }

  int trackCount = trackIndices != null ? trackIndices.length : streamElement.tracks.length;
  formats = new SmoothStreamingFormat[trackCount];
  extractors = new SparseArray<FragmentedMp4Extractor>();
  int maxWidth = 0;
  int maxHeight = 0;
  for (int i = 0; i < trackCount; i++) {
    int trackIndex = trackIndices != null ? trackIndices[i] : i;
    TrackElement trackElement = streamElement.tracks[trackIndex];
    formats[i] = new SmoothStreamingFormat(String.valueOf(trackIndex), trackElement.mimeType,
        trackElement.maxWidth, trackElement.maxHeight, trackElement.numChannels,
        trackElement.sampleRate, trackElement.bitrate, trackIndex);
    maxWidth = Math.max(maxWidth, trackElement.maxWidth);
    maxHeight = Math.max(maxHeight, trackElement.maxHeight);

    MediaFormat mediaFormat = getMediaFormat(streamElement, trackIndex);
    int trackType = streamElement.type == StreamElement.TYPE_VIDEO ? Track.TYPE_VIDEO
        : Track.TYPE_AUDIO;
    FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
        FragmentedMp4Extractor.WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME);
    extractor.setTrack(new Track(trackIndex, trackType, streamElement.timescale,
        initialManifest.durationUs, mediaFormat, trackEncryptionBoxes));
    extractors.put(trackIndex, extractor);
  }
  this.maxHeight = maxHeight;
  this.maxWidth = maxWidth;
  Arrays.sort(formats, new DecreasingBandwidthComparator());
}
 
示例11
@Override
public final void getChunkOperation(List<? extends MediaChunk> queue, long seekPositionUs,
    long playbackPositionUs, ChunkOperationHolder out) {
  if (fatalError != null) {
    out.chunk = null;
    return;
  }

  evaluation.queueSize = queue.size();
  formatEvaluator.evaluate(queue, playbackPositionUs, formats, evaluation);
  SmoothStreamingFormat selectedFormat = (SmoothStreamingFormat) evaluation.format;
  out.queueSize = evaluation.queueSize;

  if (selectedFormat == null) {
    out.chunk = null;
    return;
  } else if (out.queueSize == queue.size() && out.chunk != null
      && out.chunk.format.id.equals(evaluation.format.id)) {
    // We already have a chunk, and the evaluation hasn't changed either the format or the size
    // of the queue. Do nothing.
    return;
  }

  // In all cases where we return before instantiating a new chunk at the bottom of this method,
  // we want out.chunk to be null.
  out.chunk = null;

  StreamElement streamElement = getElement(currentManifest);
  if (streamElement.chunkCount == 0) {
    // The manifest is currently empty for this stream.
    finishedCurrentManifest = true;
    return;
  }

  int chunkIndex;
  if (queue.isEmpty()) {
    if (currentManifest.isLive) {
      seekPositionUs = getLiveSeekPosition();
    }
    chunkIndex = streamElement.getChunkIndex(seekPositionUs);
  } else {
    chunkIndex = queue.get(out.queueSize - 1).nextChunkIndex - currentManifestChunkOffset;
  }

  if (currentManifest.isLive) {
    if (chunkIndex < 0) {
      // This is before the first chunk in the current manifest.
      fatalError = new BehindLiveWindowException();
      return;
    } else if (chunkIndex >= streamElement.chunkCount) {
      // This is beyond the last chunk in the current manifest.
      finishedCurrentManifest = true;
      return;
    } else if (chunkIndex == streamElement.chunkCount - 1) {
      // This is the last chunk in the current manifest. Mark the manifest as being finished,
      // but continue to return the final chunk.
      finishedCurrentManifest = true;
    }
  } else if (chunkIndex == -1) {
    // We've reached the end of the stream.
    return;
  }

  boolean isLastChunk = !currentManifest.isLive && chunkIndex == streamElement.chunkCount - 1;
  long chunkStartTimeUs = streamElement.getStartTimeUs(chunkIndex);
  long nextChunkStartTimeUs = isLastChunk ? -1
      : chunkStartTimeUs + streamElement.getChunkDurationUs(chunkIndex);
  int currentAbsoluteChunkIndex = chunkIndex + currentManifestChunkOffset;

  Uri uri = streamElement.buildRequestUri(selectedFormat.trackIndex, chunkIndex);
  Chunk mediaChunk = newMediaChunk(selectedFormat, uri, null,
      extractors.get(Integer.parseInt(selectedFormat.id)), psshInfo, dataSource,
      currentAbsoluteChunkIndex, isLastChunk, chunkStartTimeUs, nextChunkStartTimeUs, 0);
  out.chunk = mediaChunk;
}
 
示例12
private StreamElement getElement(SmoothStreamingManifest manifest) {
  return manifest.streamElements[streamElementIndex];
}