示例#1
0
static void
create_extractors(KaxTracks &kax_tracks,
                  std::vector<track_spec_t> &tracks) {
  size_t i;
  int64_t track_id = -1;

  for (i = 0; i < kax_tracks.ListSize(); i++) {
    if (!Is<KaxTrackEntry>(kax_tracks[i]))
      continue;

    ++track_id;

    KaxTrackEntry &track = *static_cast<KaxTrackEntry *>(kax_tracks[i]);
    int64_t tnum         = kt_get_number(track);

    // Is the track number present and valid?
    if (0 == tnum)
      continue;

    // Is there more than one track with the same track number?
    if (track_extractors_by_track_number.find(tnum) != track_extractors_by_track_number.end()) {
      mxwarn(boost::format(Y("More than one track with the track number %1% found.\n")) % tnum);
      continue;
    }

    // Does the user want this track to be extracted?
    auto tspec_itr = brng::find_if(tracks, [track_id](auto const &t) { return (track_spec_t::tm_timestamps != t.target_mode) && (track_id == t.tid); });
    if (tspec_itr == tracks.end())
      continue;

    // Let's find the codec ID and create an extractor for it.
    std::string codec_id = kt_get_codec_id(track);
    if (codec_id.empty())
      mxerror(boost::format(Y("The track ID %1% does not have a valid CodecID.\n")) % track_id);

    auto extractor = xtr_base_c::create_extractor(codec_id, track_id, *tspec_itr);
    if (!extractor)
      mxerror(boost::format(Y("Extraction of track ID %1% with the CodecID '%2%' is not supported.\n")) % track_id % codec_id);

    extractor->m_track_num = tnum;

    // Has there another file been requested with the same name?
    auto extractor_itr = brng::find_if(track_extractor_list, [&tspec_itr](auto &x) { return x->m_file_name == tspec_itr->out_name; });
    xtr_base_c *master = extractor_itr != track_extractor_list.end() ? extractor_itr->get() : nullptr;

    // Let the extractor create the file.
    extractor->create_file(master, track);

    // We're done.
    track_extractors_by_track_number.insert({ tnum, extractor });
    track_extractor_list.push_back(extractor);

    mxinfo(boost::format(Y("Extracting track %1% with the CodecID '%2%' to the file '%3%'. Container format: %4%\n"))
           % track_id % codec_id % extractor->get_file_name().string() % extractor->get_container_name());
  }

  // Signal that all headers have been taken care of.
  for (auto &extractor : track_extractor_list)
    extractor->headers_done();
}
示例#2
0
static void
create_timecode_files(KaxTracks &kax_tracks,
                      std::vector<track_spec_t> &tracks,
                      int version) {
    size_t i;
    for (auto &tspec : tracks) {
        int track_number     = -1;
        KaxTrackEntry *track = nullptr;
        for (i = 0; kax_tracks.ListSize() > i; ++i) {
            if (!is_id(kax_tracks[i], KaxTrackEntry))
                continue;

            ++track_number;
            if (track_number != tspec.tid)
                continue;

            track = static_cast<KaxTrackEntry *>(kax_tracks[i]);
            break;
        }

        if (!track)
            continue;

        try {
            mm_io_cptr file = mm_write_buffer_io_c::open(tspec.out_name, 128 * 1024);
            timecode_extractors.push_back(timecode_extractor_t(tspec.tid, kt_get_number(*track), file, std::max(kt_get_default_duration(*track), static_cast<int64_t>(0))));
            file->puts(boost::format("# timecode format v%1%\n") % version);

        } catch(mtx::mm_io::exception &ex) {
            close_timecode_files();
            mxerror(boost::format(Y("Could not open the timecode file '%1%' for writing (%2%).\n")) % tspec.out_name % ex);
        }
    }
}
示例#3
0
void
find_and_verify_track_uids(KaxTracks &tracks,
                           std::vector<track_spec_t> &tspecs) {
  std::map<int64_t, bool> available_track_ids;
  size_t t;
  int64_t track_id = -1;

  for (t = 0; t < tracks.ListSize(); t++) {
    KaxTrackEntry *track_entry = dynamic_cast<KaxTrackEntry *>(tracks[t]);
    if (!track_entry)
      continue;

    ++track_id;
    available_track_ids[track_id] = true;

    for (auto &tspec : tspecs)
      if (tspec.tid == track_id) {
        tspec.tuid = kt_get_uid(*track_entry);
        break;
      }
  }

  for (auto &tspec : tspecs)
    if (!available_track_ids[ tspec.tid ])
      mxerror(boost::format(Y("No track with the ID %1% was found in the source file.\n")) % tspec.tid);
}
示例#4
0
/** \brief Add missing mandatory track header elements

   The Matroska specs and \c libmatroska say that several elements are
   mandatory. This function makes sure that they all exist by adding them
   with their default values if they're missing. It works recursively.

   The parameters are checked for validity.

   \param e An element that really is an \c EbmlMaster. \a e's children
     should be checked.
*/
void
fix_mandatory_segment_tracks_elements(EbmlElement *e) {
  if (!e)
    return;

  KaxTracks *tracks = dynamic_cast<KaxTracks *>(e);
  if (!tracks)
    return;

  size_t i;
  for (i = 0; tracks->ListSize() > i; ++i) {
    e = (*tracks)[i];

    if (dynamic_cast<KaxTrackEntry *>(e))
      fix_mandatory_track_entry_elements(static_cast<KaxTrackEntry *>(e));
  }
}
示例#5
0
static void
create_timestamp_files(KaxTracks &kax_tracks,
                       std::vector<track_spec_t> &tracks) {
  size_t i;
  for (auto &tspec : tracks) {
    if (track_spec_t::tm_timestamps != tspec.target_mode)
      continue;

    int track_number     = -1;
    KaxTrackEntry *track = nullptr;
    for (i = 0; kax_tracks.ListSize() > i; ++i) {
      if (!Is<KaxTrackEntry>(kax_tracks[i]))
        continue;

      ++track_number;
      if (track_number != tspec.tid)
        continue;

      track = static_cast<KaxTrackEntry *>(kax_tracks[i]);
      break;
    }

    if (!track)
      continue;

    try {
      mm_io_cptr file = mm_write_buffer_io_c::open(tspec.out_name, 128 * 1024);
      timestamp_extractors[kt_get_number(*track)] = std::make_shared<timestamp_extractor_t>(tspec.tid, kt_get_number(*track), file, std::max<int64_t>(kt_get_default_duration(*track), 0));
      file->puts("# timestamp format v2\n");

    } catch(mtx::mm_io::exception &ex) {
      close_timestamp_files();
      mxerror(boost::format(Y("Could not open the timestamp file '%1%' for writing (%2%).\n")) % tspec.out_name % ex);
    }
  }
}
示例#6
0
static void
create_extractors(KaxTracks &kax_tracks,
                  std::vector<track_spec_t> &tracks) {
  size_t i;
  int64_t track_id = -1;

  for (i = 0; i < kax_tracks.ListSize(); i++) {
    if (!Is<KaxTrackEntry>(kax_tracks[i]))
      continue;

    ++track_id;

    KaxTrackEntry &track = *static_cast<KaxTrackEntry *>(kax_tracks[i]);
    int64_t tnum         = kt_get_number(track);

    // Is the track number present and valid?
    if (0 == tnum)
      continue;

    // Is there more than one track with the same track number?
    xtr_base_c *extractor = nullptr;
    size_t k;
    for (k = 0; k < extractors.size(); k++)
      if (extractors[k]->m_track_num == tnum) {
        mxwarn(boost::format(Y("More than one track with the track number %1% found.\n")) % tnum);
        extractor = extractors[k];
        break;
      }
    if (extractor)
      continue;

    // Does the user want this track to be extracted?
    track_spec_t *tspec = nullptr;
    for (k = 0; k < tracks.size(); k++)
      if (tracks[k].tid == track_id) {
        tspec = &tracks[k];
        break;
      }
    if (!tspec)
      continue;

    // Let's find the codec ID and create an extractor for it.
    std::string codec_id = kt_get_codec_id(track);
    if (codec_id.empty())
      mxerror(boost::format(Y("The track ID %1% does not have a valid CodecID.\n")) % track_id);

    extractor = xtr_base_c::create_extractor(codec_id, track_id, *tspec);
    if (!extractor)
      mxerror(boost::format(Y("Extraction of track ID %1% with the CodecID '%2%' is not supported.\n")) % track_id % codec_id);

    extractor->m_track_num = tnum;

    // Has there another file been requested with the same name?
    xtr_base_c *master = nullptr;
    for (k = 0; k < extractors.size(); k++)
      if (extractors[k]->m_file_name == tspec->out_name) {
        master = extractors[k];
        break;
      }

    // Let the extractor create the file.
    extractor->create_file(master, track);

    // We're done.
    extractors.push_back(extractor);

    mxinfo(boost::format(Y("Extracting track %1% with the CodecID '%2%' to the file '%3%'. Container format: %4%\n"))
           % track_id % codec_id % tspec->out_name % extractor->get_container_name());
  }

  // Signal that all headers have been taken care of.
  for (i = 0; i < extractors.size(); i++)
    extractors[i]->headers_done();
}
示例#7
0
ComponentResult MatroskaImport::ReadTracks(KaxTracks &trackEntries)
{
	Track firstVideoTrack = NULL; short firstVideoTrackLang = 0; bool videoEnabled = false;
	Track firstAudioTrack = NULL; short firstAudioTrackLang = 0; bool audioEnabled = false;
	Track firstSubtitleTrack = NULL; short firstSubtitleTrackLang = 0; bool subtitleEnabled = false;
	ComponentResult err = noErr;
	
	if (seenTracks)
		return noErr;
	
	// Since creating a subtitle track requires a video track to have already been created
    // (so that it can be sized to fit exactly over the video track), we go through the 
    // track entries in two passes, first to add audio/video, second to add subtitle tracks.
    for (int pass = 1; pass <= 2; pass++) {
		for (int i = 0; i < trackEntries.ListSize(); i++) {
			if (EbmlId(*trackEntries[i]) != KaxTrackEntry::ClassInfos.GlobalId)
				continue;
			KaxTrackEntry & track = *static_cast<KaxTrackEntry *>(trackEntries[i]);
			KaxTrackNumber & number = GetChild<KaxTrackNumber>(track);
			KaxTrackType & type = GetChild<KaxTrackType>(track);
			KaxTrackDefaultDuration * defaultDuration = FindChild<KaxTrackDefaultDuration>(track);
			KaxTrackFlagDefault & enabled = GetChild<KaxTrackFlagDefault>(track);
			KaxTrackFlagLacing & lacing = GetChild<KaxTrackFlagLacing>(track);
			MatroskaTrack mkvTrack;
			
			mkvTrack.number = uint16(number);
			mkvTrack.type = uint8(type);
			if (defaultDuration)
				mkvTrack.defaultDuration = uint32(*defaultDuration) / float(timecodeScale) + .5;
			else
				mkvTrack.defaultDuration = 0;
			mkvTrack.isEnabled = uint8(enabled);
			mkvTrack.usesLacing = uint8(lacing);
			
			KaxTrackLanguage & trackLang = GetChild<KaxTrackLanguage>(track);
			KaxTrackName & trackName = GetChild<KaxTrackName>(track);
			KaxContentEncodings * encodings = FindChild<KaxContentEncodings>(track);
			short qtLang = ISO639_2ToQTLangCode(string(trackLang).c_str());
			
			switch (uint8(type)) {
				case track_video:
					if (pass == 2) continue;
					err = AddVideoTrack(track, mkvTrack, encodings);
					if (err) return err;
					
					if (mkvTrack.isEnabled)
						videoEnabled = true;
					
					if (firstVideoTrack && qtLang != firstVideoTrackLang)
						SetTrackAlternate(firstVideoTrack, mkvTrack.theTrack);
					else {
						firstVideoTrack = mkvTrack.theTrack;
						firstVideoTrackLang = qtLang;
					}
					break;
					
				case track_audio:
					if (pass == 2) continue;
					err = AddAudioTrack(track, mkvTrack, encodings);
					if (err) return err;
					
					if (mkvTrack.isEnabled)
						audioEnabled = true;
					
					if (firstAudioTrack && qtLang != firstAudioTrackLang)
						SetTrackAlternate(firstAudioTrack, mkvTrack.theTrack);
					else {
						firstAudioTrack = mkvTrack.theTrack;
						firstAudioTrackLang = qtLang;
					}
					break;
					
				case track_subtitle:
					if (pass == 1) continue;
					err = AddSubtitleTrack(track, mkvTrack, encodings);
					if (err) return err;
					if (mkvTrack.theTrack == NULL) continue;
					
					if (mkvTrack.isEnabled)
						subtitleEnabled = true;
					
					if (firstSubtitleTrack && qtLang != firstSubtitleTrackLang)
						SetTrackAlternate(firstSubtitleTrack, mkvTrack.theTrack);
					else {
						firstSubtitleTrack = mkvTrack.theTrack;
						firstSubtitleTrackLang = qtLang;
					}
					break;
					
				case track_complex:
				case track_logo:
				case track_buttons:
				case track_control:
					// not likely to be implemented soon
				default:
					continue;
			}
			
			SetMediaLanguage(mkvTrack.theMedia, qtLang);
			
			if (!trackName.IsDefaultValue()) {
				QTMetaDataRef trackMetaData;
				err = QTCopyTrackMetaData(mkvTrack.theTrack, &trackMetaData);
				
				if (err == noErr) {
					OSType key = 'name';
					// QuickTime differentiates between the title of a track and its name
					// so we set both
					QTMetaDataAddItem(trackMetaData,
									  kQTMetaDataStorageFormatQuickTime,
									  kQTMetaDataKeyFormatCommon,
									  (UInt8 *)&key, sizeof(key),
									  (UInt8 *)UTFstring(trackName).GetUTF8().c_str(),
									  UTFstring(trackName).GetUTF8().size(),
									  kQTMetaDataTypeUTF8, NULL);
					
					QTMetaDataAddItem(trackMetaData,
									  kQTMetaDataStorageFormatUserData,
									  kQTMetaDataKeyFormatUserData,
									  (UInt8 *)&key, sizeof(key),
									  (UInt8 *)UTFstring(trackName).GetUTF8().c_str(),
									  UTFstring(trackName).GetUTF8().size(),
									  kQTMetaDataTypeUTF8, NULL);
					
					QTMetaDataRelease(trackMetaData);
				}
			}
			tracks.push_back(mkvTrack);
		}
	}
	
	for (int i = 0; i < tracks.size(); i++) {
		SetTrackEnabled(tracks[i].theTrack, tracks[i].isEnabled);
	}
	// ffmpeg used to write a TrackDefault of 0 for all tracks
	// ensure that at least one track of each media type is enabled, if none were originally
	// this picks the first track, which may not be the best, but the situation is quite rare anyway
	// FIXME: properly choose tracks based on forced/default/language flags, and consider turning auto-alternates back on
	if (!videoEnabled && firstVideoTrack)
		SetTrackEnabled(firstVideoTrack, 1);
	if (!audioEnabled && firstAudioTrack)
		SetTrackEnabled(firstAudioTrack, 1);
	if (!subtitleEnabled && firstSubtitleTrack)
		SetTrackEnabled(firstSubtitleTrack, 1);
	
	seenTracks = true;
	return noErr;
}