bool Segment::Finalize() {
  if (WriteFramesAll() < 0)
    return false;

  // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_|
  // is set. In all other modes, always call Cluster::Finalize.
  if ((mode_ == kLive ? accurate_cluster_duration_ : true) &&
      cluster_list_size_ > 0) {
    // Update last cluster's size
    Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1];

    // For the last frame of the last Cluster, we don't write it as a BlockGroup
    // with Duration unless the frame itself has duration set explicitly.
    if (!old_cluster || !old_cluster->Finalize(false, 0))
      return false;
  }

  if (mode_ == kFile) {
    if (chunking_ && chunk_writer_cluster_) {
      chunk_writer_cluster_->Close();
      chunk_count_++;
    }

    double duration =
        (static_cast<double>(last_timestamp_) + last_block_duration_) /
        segment_info_.timecode_scale();
    if (duration_ > 0.0) {
      duration = duration_;
    } else {
      if (last_block_duration_ == 0 && estimate_file_duration_) {
        const int num_tracks = static_cast<int>(tracks_.track_entries_size());
        for (int i = 0; i < num_tracks; ++i) {
          if (track_frames_written_[i] < 2)
            continue;

          // Estimate the duration for the last block of a Track.
          const double nano_per_frame =
              static_cast<double>(last_track_timestamp_[i]) /
              (track_frames_written_[i] - 1);
          const double track_duration =
              (last_track_timestamp_[i] + nano_per_frame) /
              segment_info_.timecode_scale();
          if (track_duration > duration)
            duration = track_duration;
        }
      }
    }
    segment_info_.set_duration(duration);
    if (!segment_info_.Finalize(writer_header_))
      return false;

    if (output_cues_)
      if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset()))
        return false;

    if (chunking_) {
      if (!chunk_writer_cues_)
        return false;

      char* name = NULL;
      if (!UpdateChunkName("cues", &name))
        return false;

      const bool cues_open = chunk_writer_cues_->Open(name);
      delete[] name;
      if (!cues_open)
        return false;
    }

    cluster_end_offset_ = writer_cluster_->Position();

    // Write the seek headers and cues
    if (output_cues_)
      if (!cues_.Write(writer_cues_))
        return false;

    if (!seek_head_.Finalize(writer_header_))
      return false;

    if (writer_header_->Seekable()) {
      if (size_position_ == -1)
        return false;

      const int64_t segment_size = MaxOffset();
      if (segment_size < 1)
        return false;

      const int64_t pos = writer_header_->Position();
      UpdateDocTypeVersion();
      if (doc_type_version_ != doc_type_version_written_) {
        if (writer_header_->Position(0))
          return false;

        const char* const doc_type =
            DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska;
        if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type))
          return false;
        if (writer_header_->Position() != ebml_header_size_)
          return false;

        doc_type_version_written_ = doc_type_version_;
      }

      if (writer_header_->Position(size_position_))
        return false;

      if (WriteUIntSize(writer_header_, segment_size, 8))
        return false;

      if (writer_header_->Position(pos))
        return false;
    }

    if (chunking_) {
      // Do not close any writers until the segment size has been written,
      // otherwise the size may be off.
      if (!chunk_writer_cues_ || !chunk_writer_header_)
        return false;

      chunk_writer_cues_->Close();
      chunk_writer_header_->Close();
    }
  }

  return true;
}