author Mike Hommey <>
Fri, 11 Jan 2019 16:01:15 +0000
changeset 453570 daf50f25895db073e44d50fecf2e4f6fe873865d
parent 448757 8a064f0dbb2f09c589514f71b06e37ed36362cd1
permissions -rw-r--r--
Bug 1519307 - Add a new project to build useful parts of breakpad independently. r=froydnj With `ac_add_options --enable-project=tools/crashreporter` in a mozconfig, `./mach build` builds minidump_stackwalk, dump_syms and fileid. One caveat is that due to limitation in how the build system works currently, it's cumbersome to keep dump_syms as a host program for Gecko, and to make it a target program for this project. For now, keep it as a host program. We're not going to use it on automation, but it's still convenient to have for quick local builds (I've had to resort to awful hacks downstream). Differential Revision:

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at */


#include "MediaSegment.h"
#include "nsAutoPtr.h"
#include "TrackID.h"

namespace mozilla {

inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
                                            TrackRate aInRate,
                                            TrackTicks aTicks) {
  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
  return (aTicks * aOutRate) / aInRate;
inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate, TrackRate aInRate,
                                          TrackTicks aTicks) {
  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
  return (aTicks * aOutRate + aInRate - 1) / aInRate;

 * This object contains the decoded data for a stream's tracks.
 * A StreamTracks can be appended to. Logically a StreamTracks only gets longer,
 * but we also have the ability to "forget" data before a certain time that
 * we know won't be used again. (We prune a whole number of seconds internally.)
 * StreamTrackss should only be used from one thread at a time.
 * A StreamTracks has a set of tracks that can be of arbitrary types ---
 * the data for each track is a MediaSegment. The set of tracks can vary
 * over the timeline of the StreamTracks.
class StreamTracks {
   * Every track has a start time --- when it started in the StreamTracks.
   * It has an end flag; when false, no end point is known; when true,
   * the track ends when the data we have for the track runs out.
   * Tracks have a unique ID assigned at creation. This allows us to identify
   * the same track across StreamTrackss. A StreamTracks should never have
   * two tracks with the same ID (even if they don't overlap in time).
   * TODO Tracks can also be enabled and disabled over time.
   * Takes ownership of aSegment.
  class Track final {
    Track(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
        : mStart(aStart),
          mNotifiedEnded(false) {

      NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
      NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(),
                   "Bad start position");

    ~Track() { MOZ_COUNT_DTOR(Track); }

    template <class T>
    T* Get() const {
      if (mSegment->GetType() == T::StaticType()) {
        return static_cast<T*>(mSegment.get());
      return nullptr;

    MediaSegment* GetSegment() const { return mSegment; }
    TrackID GetID() const { return mID; }
    bool IsEnded() const { return mEnded; }
    StreamTime GetStart() const { return mStart; }
    StreamTime GetEnd() const { return mSegment->GetDuration(); }
    MediaSegment::Type GetType() const { return mSegment->GetType(); }
    bool NotifiedEnded() const { return mNotifiedEnded; }

    void SetEnded() { mEnded = true; }
    void NotifyEnded() {
      mNotifiedEnded = true;
    void AppendFrom(Track* aTrack) {
      NS_ASSERTION(!mEnded, "Can't append to ended track");
      NS_ASSERTION(aTrack->mID == mID, "IDs must match");
      NS_ASSERTION(aTrack->mStart == 0, "Source track must start at zero");
      NS_ASSERTION(aTrack->mSegment->GetType() == GetType(),
                   "Track types must match");

      mEnded = aTrack->mEnded;
    MediaSegment* RemoveSegment() { return mSegment.forget(); }
    void ForgetUpTo(StreamTime aTime) { mSegment->ForgetUpTo(aTime); }
    void FlushAfter(StreamTime aNewEnd) {
      // Forget everything after a given endpoint
      // a specified amount

    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
      size_t amount = aMallocSizeOf(this);
      if (mSegment) {
        amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
      return amount;

    friend class StreamTracks;

    // Start offset is in ticks at rate mRate
    StreamTime mStart;
    // The segment data starts at the start of the owning StreamTracks, i.e.,
    // there's mStart silence/no video at the beginning.
    nsAutoPtr<MediaSegment> mSegment;
    // Unique ID
    TrackID mID;
    // True when the track ends with the data in mSegment
    bool mEnded;
    // True after NotifiedEnded() has been called.
    bool mNotifiedEnded;

  class MOZ_STACK_CLASS CompareTracksByID final {
    bool Equals(Track* aA, Track* aB) const {
      return aA->GetID() == aB->GetID();
    bool LessThan(Track* aA, Track* aB) const {
      return aA->GetID() < aB->GetID();

      : mGraphRate(0),
#ifdef DEBUG
  ~StreamTracks() { MOZ_COUNT_DTOR(StreamTracks); }

  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
    size_t amount = 0;
    amount += mTracks.ShallowSizeOfExcludingThis(aMallocSizeOf);
    for (size_t i = 0; i < mTracks.Length(); i++) {
      amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
    return amount;

   * Initialize the graph rate for use in calculating StreamTimes from track
   * ticks.  Called when a MediaStream's graph pointer is initialized.
  void InitGraphRate(TrackRate aGraphRate) {
    mGraphRate = aGraphRate;
    mGraphRateIsSet = true;

  TrackRate GraphRate() const {
    return mGraphRate;

   * Takes ownership of aSegment. Don't do this while iterating, or while
   * holding a Track reference.
   * aSegment must have aStart worth of null data.
  Track& AddTrack(TrackID aID, StreamTime aStart, MediaSegment* aSegment) {
    NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");

    Track* track = new Track(aID, aStart, aSegment);
    mTracks.InsertElementSorted(track, CompareTracksByID());
    mTracksDirty = true;

    return *track;

   * The end time for the StreamTracks is the latest time for which we have
   * data for all tracks that haven't ended by that time.
  StreamTime GetEarliestTrackEnd() const;

   * Returns the earliest time >= 0 at which all tracks have ended and all
   * their data has been played out, or STREAM_TIME_MAX if there is no such
   * time.
  StreamTime GetLatestTrackEnd() const;

#ifdef DEBUG
  void DumpTrackInfo() const;

  Track* FindTrack(TrackID aID) const;

  class MOZ_STACK_CLASS TrackIter final {
     * Iterate through the tracks of aBuffer in order of ID.
    explicit TrackIter(const StreamTracks& aBuffer)
        : mBuffer(&aBuffer.mTracks),
          mMatchType(false) {}
     * Iterate through the tracks of aBuffer with type aType, in order of ID.
    TrackIter(const StreamTracks& aBuffer, MediaSegment::Type aType)
        : mBuffer(&aBuffer.mTracks), mIndex(0), mType(aType), mMatchType(true) {
    bool IsEnded() const { return mIndex >= mBuffer->Length(); }
    void Next() {
    Track* get() const { return mBuffer->ElementAt(mIndex); }
    Track& operator*() { return *mBuffer->ElementAt(mIndex); }
    Track* operator->() { return mBuffer->ElementAt(mIndex); }

    void FindMatch() {
      if (!mMatchType) return;
      while (mIndex < mBuffer->Length() &&
             mBuffer->ElementAt(mIndex)->GetType() != mType) {

    const nsTArray<nsAutoPtr<Track>>* mBuffer;
    uint32_t mIndex;
    MediaSegment::Type mType;
    bool mMatchType;
  friend class TrackIter;

   * Forget stream data before aTime; they will no longer be needed.
   * Also can forget entire tracks that have ended at or before aTime.
   * Can't be used to forget beyond GetEnd().
  void ForgetUpTo(StreamTime aTime);
   * Clears out all Tracks and the data they are holding.
   * MediaStreamGraph calls this during forced shutdown.
  void Clear();
   * Returns the latest time passed to ForgetUpTo.
  StreamTime GetForgottenDuration() const { return mForgottenTime; }

  bool GetAndResetTracksDirty() {
    if (!mTracksDirty) {
      return false;

    mTracksDirty = false;
    return true;

  TrackRate mGraphRate;  // StreamTime per second
  StreamTime mForgottenTime;

  // All known tracks for this StreamTracks
  nsTArray<nsAutoPtr<Track>> mTracks;
  bool mTracksDirty;

#ifdef DEBUG
  bool mGraphRateIsSet;

}  // namespace mozilla