Exemple #1
0
int64_t
vc1::es_parser_c::get_next_timecode() {
  int64_t next_timecode = m_previous_timecode
                        + (m_num_timecodes + m_num_repeated_fields) * m_default_duration
                        - m_num_repeated_fields                     * m_default_duration / 2;

  if (is_timecode_available()) {
    mxverb(3,
           boost::format("\nvc1::es_parser_c::get_next_timecode(): provided timecode available; original next %1%, provided %2%\n")
           % format_timecode(next_timecode) % format_timecode(m_timecodes.front()));

    next_timecode         = m_timecodes.front();
    m_previous_timecode   = m_timecodes.front();
    m_num_timecodes       = 0;
    m_num_repeated_fields = 0;

    m_timecodes.pop_front();
    m_timecode_positions.pop_front();
  }

  m_num_timecodes += 1 + m_current_frame->header.repeat_frame;
  if (m_seqhdr.interlace_flag && m_current_frame->header.repeat_first_field_flag && !m_current_frame->contains_field)
    ++m_num_repeated_fields;

  return next_timecode;
}
void
cluster_helper_c::handle_discarded_duration(bool create_new_file,
                                            bool previously_discarding) {
  m->previous_discarded_duration = m->discarded_duration;

  if (create_new_file) { // || (!previously_discarding && m->discarding)) {
    mxdebug_if(m->debug_splitting,
               boost::format("RESETTING discarded duration of %1%, create_new_file %2% previously_discarding %3% m->discarding %4%\n")
               % format_timecode(m->discarded_duration) % create_new_file % previously_discarding % m->discarding);
    m->discarded_duration = 0;

  } else if (previously_discarding && !m->discarding) {
    auto diff              = m->last_discarded_timecode_and_duration - std::max<int64_t>(m->first_discarded_timecode, 0);
    m->discarded_duration += diff;

    mxdebug_if(m->debug_splitting,
               boost::format("ADDING to discarded duration TC at %1% / %2% diff %3% new total %4% create_new_file %5% previously_discarding %6% m->discarding %7%\n")
               % format_timecode(m->first_discarded_timecode) % format_timecode(m->last_discarded_timecode_and_duration) % format_timecode(diff) % format_timecode(m->discarded_duration)
               % create_new_file % previously_discarding % m->discarding);
  } else
    mxdebug_if(m->debug_splitting,
               boost::format("KEEPING discarded duration at %1%, create_new_file %2% previously_discarding %3% m->discarding %4%\n")
               % format_timecode(m->discarded_duration) % create_new_file % previously_discarding % m->discarding);

  m->first_discarded_timecode             = -1;
  m->last_discarded_timecode_and_duration =  0;
}
void
cluster_helper_c::create_tags_for_track_statistics(KaxTags &tags,
                                                   std::string const &writing_app,
                                                   boost::posix_time::ptime const &writing_date) {
  auto writing_date_str = !writing_date.is_not_a_date_time() ? mtx::date_time::to_string(writing_date, "%Y-%m-%d %H:%M:%S") : "1970-01-01 00:00:00";

  for (auto const &ptzr : g_packetizers) {
    auto track_uid    = ptzr.packetizer->get_uid();
    auto const &stats = m->track_statistics[track_uid];
    auto bps          = stats.get_bits_per_second();
    auto duration     = stats.get_duration();

    mtx::tags::remove_simple_tags_for<KaxTagTrackUID>(tags, track_uid, "BPS");
    mtx::tags::remove_simple_tags_for<KaxTagTrackUID>(tags, track_uid, "DURATION");
    mtx::tags::remove_simple_tags_for<KaxTagTrackUID>(tags, track_uid, "NUMBER_OF_FRAMES");
    mtx::tags::remove_simple_tags_for<KaxTagTrackUID>(tags, track_uid, "NUMBER_OF_BYTES");

    auto tag = mtx::tags::find_tag_for<KaxTagTrackUID>(tags, track_uid, mtx::tags::Movie, true);

    mtx::tags::set_target_type(*tag, mtx::tags::Movie, "MOVIE");

    mtx::tags::set_simple(*tag, "BPS",              to_string(bps ? *bps : 0));
    mtx::tags::set_simple(*tag, "DURATION",         format_timecode(duration ? *duration : 0));
    mtx::tags::set_simple(*tag, "NUMBER_OF_FRAMES", to_string(stats.get_num_frames()));
    mtx::tags::set_simple(*tag, "NUMBER_OF_BYTES",  to_string(stats.get_num_bytes()));

    mtx::tags::set_simple(*tag, "_STATISTICS_WRITING_APP",      writing_app);
    mtx::tags::set_simple(*tag, "_STATISTICS_WRITING_DATE_UTC", writing_date_str);
    mtx::tags::set_simple(*tag, "_STATISTICS_TAGS",             "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES");
  }

  m->track_statistics.clear();
}
EbmlElement *
kax_file_c::read_next_level1_element(uint32_t wanted_id,
                                     bool report_cluster_timecode) {
  try {
    auto element = read_next_level1_element_internal(wanted_id);

    if (report_cluster_timecode && (-1 != m_timecode_scale))
      mxinfo(boost::format(Y("The first cluster timecode after the resync is %1%.\n"))
             % format_timecode(FindChildValue<KaxClusterTimecode>(static_cast<KaxCluster *>(element)) * m_timecode_scale));

    return element;

  } catch (mtx::mm_io::exception &e) {
    mxwarn(boost::format("%1% %2% %3%\n")
           % (boost::format(Y("%1%: an exception occurred (message: %2%; type: %3%).")) % "kax_file_c::read_next_level1_element()" % (boost::format("%1% / %2%") % e.what() % e.error()) % typeid(e).name())
           % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further."));

  } catch (std::exception &e) {
    mxwarn(boost::format("%1% %2% %3%\n")
           % (boost::format(Y("%1%: an exception occurred (message: %2%; type: %3%).")) % "kax_file_c::read_next_level1_element()" % e.what() % typeid(e).name())
           % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further."));

  } catch (...) {
    mxwarn(boost::format("%1% %2% %3%\n")
           % (boost::format(Y("%1%: an unknown exception occurred.")) % "kax_file_c::read_next_level1_element()")
           % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further."));
  }
  return nullptr;
}
Exemple #5
0
void
vc1::es_parser_c::handle_frame_packet(memory_cptr packet) {
  flush_frame();

  vc1::frame_header_t frame_header;
  if (!m_seqhdr_found || !vc1::parse_frame_header(packet->get_buffer(), packet->get_size(), frame_header, m_seqhdr))
    return;

  m_current_frame        = frame_cptr(new frame_t);
  m_current_frame->data  = packet;
  m_current_frame->data->grab();

  memcpy(&m_current_frame->header, &frame_header, sizeof(frame_header_t));

  if (!m_timecodes.empty())
    mxverb(2,
           boost::format("vc1::es_parser_c::handle_frame_packet: next provided timecode %1% next calculated timecode %2%\n")
           % format_timecode(m_timecodes.front()) % format_timecode(peek_next_calculated_timecode()));

}
ScannedFileItem *
ScannedFileItem::create(SourceFile const &scannedFile) {
  auto item = new ScannedFileItem{ scannedFile, QStringList{
      QFileInfo{scannedFile.m_fileName}.fileName(),
      to_qs(format_timecode(scannedFile.m_playlistDuration, 0)),
      to_qs(format_file_size(scannedFile.m_playlistSize)),
    }};

  item->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
  item->setTextAlignment(2, Qt::AlignRight | Qt::AlignVCenter);

  return item;
}
std::string
split_point_c::str()
  const {
  return (boost::format("<%1% %2% once:%3% discard:%4% create_file:%5%>")
          % format_timecode(m_point)
          % (  duration          == m_type ? "duration"
             : size              == m_type ? "size"
             : timecode          == m_type ? "timecode"
             : chapter           == m_type ? "chapter"
             : parts             == m_type ? "part"
             : parts_frame_field == m_type ? "part(frame/field)"
             : frame_field       == m_type ? "frame/field"
             :                               "unknown")
          % m_use_once % m_discard % m_create_new_file).str();
}
Exemple #8
0
void
xtr_usf_c::finish_track() {
  auto subtitles = m_doc->document_element().append_child("subtitles");
  subtitles.append_child("language").append_attribute("code").set_value(m_language.c_str());

  for (auto &entry : m_entries) {
    std::string text = std::string{"<subtitle>"} + entry.m_text + "</subtitle>";
    strip(text, true);

    std::stringstream text_in(text);
    pugi::xml_document subtitle_doc;
    if (!subtitle_doc.load(text_in, pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_pi | pugi::parse_comments)) {
      mxwarn(boost::format(Y("Track %1%: An USF subtitle entry starting at timecode %2% is not well-formed XML and will be skipped.\n")) % m_tid % format_timecode(entry.m_start * 1000000, 3));
      continue;
    }

    auto subtitle = subtitles.append_child("subtitle");
    subtitle.append_attribute("start").set_value(format_timecode(entry.m_start * 1000000, 3).c_str());
    subtitle.append_attribute("stop"). set_value(format_timecode(entry.m_end   * 1000000, 3).c_str());

    for (auto child : subtitle_doc.document_element())
      subtitle.append_copy(child);
  }
}
void
SelectPlaylistDialog::onScannedFileSelected(QTreeWidgetItem *current,
                                            QTreeWidgetItem *) {
  auto selectedItem = static_cast<ScannedFileItem *>(current);
  if (!selectedItem)
    return;

  auto const &file = *selectedItem->m_file;

  ui->duration->setText(to_qs(format_timecode(file.m_playlistDuration, 0)));
  ui->size->setText(to_qs(format_file_size(file.m_playlistSize)));
  ui->numberOfChapters->setText(QString::number(file.m_playlistChapters));

  ui->tracks->setSortingEnabled(false);
  ui->playlistItems->setSortingEnabled(false);

  ui->tracks->clear();
  ui->playlistItems->clear();

  auto newItems = QList<QTreeWidgetItem *>{};
  for (auto const &track : file.m_tracks)
    newItems << TrackItem::create(*track);

  ui->tracks->insertTopLevelItems(0, newItems);

  newItems.clear();
  for (auto const &playlistFile : file.m_playlistFiles)
    newItems << createPlaylistItemItem(playlistFile);

  ui->playlistItems->insertTopLevelItems(0, newItems);

  ui->tracks->setSortingEnabled(true);
  ui->tracks->sortItems(ui->tracks->sortColumn(), Qt::AscendingOrder);

  ui->playlistItems->setSortingEnabled(true);
  ui->playlistItems->sortItems(ui->playlistItems->sortColumn(), Qt::AscendingOrder);

  Util::resizeViewColumnsToContents(ui->tracks);
  Util::resizeViewColumnsToContents(ui->playlistItems);
}
void
select_scanned_file_dlg::update_info() {
  wxListItem item;

  auto &playlist = *m_playlists[m_selected_playlist_idx];

  m_st_duration->SetLabel(wxU(format_timecode(playlist.duration, 0)));
  m_st_size->SetLabel(wxU(format_file_size(playlist.size)));
  m_st_chapters->SetLabel(wxU(boost::format("%1%") % playlist.chapters));

  m_lc_tracks->Hide();
  m_lc_tracks->DeleteAllItems();
  long idx = 0;
  for (auto &track : playlist.tracks) {
    auto id = m_lc_tracks->InsertItem(idx, track.type);
    m_lc_tracks->SetItem(id, 1, track.codec);
    m_lc_tracks->SetItem(id, 2, wxU(boost::format("%|1$ -15s|") % to_utf8(track.language)));
    ++idx;
  }

  m_lc_tracks->SetColumnWidth(0, wxLIST_AUTOSIZE);
  m_lc_tracks->SetColumnWidth(1, wxLIST_AUTOSIZE);
  m_lc_tracks->SetColumnWidth(2, wxLIST_AUTOSIZE);

  m_lc_tracks->Show();

  m_lc_items->Hide();
  m_lc_items->DeleteAllItems();
  idx = 0;
  for (auto &file : playlist.files) {
    auto id = m_lc_items->InsertItem(idx, wxFileName{file}.GetFullName());
    m_lc_items->SetItem(id, 1, wxFileName{file}.GetPath());
    ++idx;
  }

  m_lc_items->SetColumnWidth(0, wxLIST_AUTOSIZE);
  m_lc_items->SetColumnWidth(1, wxLIST_AUTOSIZE);

  m_lc_items->Show();
}
Exemple #11
0
void
xtr_ssa_c::handle_frame(xtr_frame_t &f) {
  if (0 > f.duration) {
    mxwarn(boost::format(Y("Subtitle track %1% is missing some duration elements. "
                           "Please check the resulting SSA/ASS file for entries that have the same start and end time.\n"))
           % m_tid);
    m_warning_printed = true;
  }

  int64_t start =         f.timecode / 1000000;
  int64_t end   = start + f.duration / 1000000;

  char *s       = (char *)safemalloc(f.frame->get_size() + 1);
  memory_c af_s((unsigned char *)s, 0, true);
  memcpy(s, f.frame->get_buffer(), f.frame->get_size());
  s[f.frame->get_size()] = 0;

  // Split the line into the fields.
  // Specs say that the following fields are to put into the block:
  // 0: ReadOrder, 1: Layer, 2: Style, 3: Name, 4: MarginL, 5: MarginR,
  // 6: MarginV, 7: Effect, 8: Text
  std::vector<std::string> fields = split(s, ",", 9);
  if (9 < fields.size()) {
    mxwarn(boost::format(Y("Invalid format for a SSA line ('%1%') at timecode %2%: Too many fields found (%3% instead of 9). This entry will be skipped.\n"))
           % s % format_timecode(f.timecode * 1000000, 3) % fields.size());
    return;
  }

  while (9 != fields.size())
    fields.push_back("");

  // Convert the ReadOrder entry so that we can re-order the entries later.
  int num;
  if (!parse_number(fields[0], num)) {
    mxwarn(boost::format(Y("Invalid format for a SSA line ('%1%') at timecode %2%: The first field is not an integer. This entry will be skipped.\n"))
           % s % format_timecode(f.timecode * 1000000, 3));
    return;
  }

  // Reconstruct the 'original' line. It'll look like this for SSA:
  //   Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
  // and for ASS:
  //   Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text

  // Problem is that the CodecPrivate may contain a Format: line
  // that defines a different layout. So let's account for that.

  std::string line = "Dialogue: ";
  size_t i;
  for (i = 0; i < m_ssa_format.size(); i++) {
    std::string format = m_ssa_format[i];

    if (balg::iequals(format, "actor"))
      format = "name";

    if (0 < i)
      line += ",";

    if (format == "marked")
      line += "Marked=0";

    else if (format == "start")
      line += (boost::format("%1%:%|2$02d|:%|3$02d|.%|4$02d|")
               % (start / 1000 / 60 / 60) % ((start / 1000 / 60) % 60) % ((start / 1000) % 60) % ((start % 1000) / 10)).str();

    else if (format == "end")
      line += (boost::format("%1%:%|2$02d|:%|3$02d|.%|4$02d|")
               % (end   / 1000 / 60 / 60) % ((end   / 1000 / 60) % 60) % ((end   / 1000) % 60) % ((end   % 1000) / 10)).str();

    else {
      int k;
      for (k = 0; ms_kax_ssa_fields[k]; ++k)
        if (format == ms_kax_ssa_fields[k]) {
          line += fields[k];
          break;
        }
    }
  }

  // Do the charset conversion.
  line  = m_conv->native(line);
  line += "\n";

  // Now store that entry.
  m_lines.push_back(ssa_line_c(line, num));
}
Exemple #12
0
EbmlElement *
kax_file_c::resync_to_level1_element_internal(uint32_t wanted_id) {
  if (m_segment_end && (m_in->getFilePointer() >= m_segment_end))
    return nullptr;

  m_resynced         = true;
  m_resync_start_pos = m_in->getFilePointer();

  uint32_t actual_id = m_in->read_uint32_be();
  int64_t start_time = mtx::sys::get_current_time_millis();
  bool is_cluster_id = !wanted_id || (EBML_ID_VALUE(EBML_ID(KaxCluster)) == wanted_id); // 0 means: any level 1 element will do

  mxinfo(boost::format(Y("%1%: Error in the Matroska file structure at position %2%. Resyncing to the next level 1 element.\n"))
         % m_in->get_file_name() % m_resync_start_pos);

  if (is_cluster_id && (-1 != m_last_timecode)) {
    mxinfo(boost::format(Y("The last timecode processed before the error was encountered was %1%.\n")) % format_timecode(m_last_timecode));
    m_last_timecode = -1;
  }

  if (m_debug_resync)
    mxinfo(boost::format("kax_file::resync_to_level1_element(): starting at %1% potential ID %|2$08x|\n") % m_resync_start_pos % actual_id);

  while (m_in->getFilePointer() < m_file_size) {
    int64_t now = mtx::sys::get_current_time_millis();
    if ((now - start_time) >= 10000) {
      mxinfo(boost::format("Still resyncing at position %1%.\n") % m_in->getFilePointer());
      start_time = now;
    }

    actual_id = (actual_id << 8) | m_in->read_uint8();

    if (   ((0 != wanted_id) && (wanted_id != actual_id))
        || ((0 == wanted_id) && !is_level1_element_id(vint_c(actual_id, 4))))
      continue;

    uint64_t current_start_pos = m_in->getFilePointer() - 4;
    uint64_t element_pos       = current_start_pos;
    unsigned int num_headers   = 1;
    bool valid_unknown_size    = false;

    if (m_debug_resync)
      mxinfo(boost::format("kax_file::resync_to_level1_element(): byte-for-byte search, found level 1 ID %|2$x| at %1%\n") % current_start_pos % actual_id);

    try {
      unsigned int idx;
      for (idx = 0; 3 > idx; ++idx) {
        vint_c length = vint_c::read(m_in);

        if (m_debug_resync)
          mxinfo(boost::format("kax_file::resync_to_level1_element():   read ebml length %1%/%2% valid? %3% unknown? %4%\n")
                 % length.m_value % length.m_coded_size % length.is_valid() % length.is_unknown());

        if (length.is_unknown()) {
          valid_unknown_size = true;
          break;
        }

        if (   !length.is_valid()
            || ((element_pos + length.m_value + length.m_coded_size + 2 * 4) >= m_file_size)
            || !m_in->setFilePointer2(element_pos + 4 + length.m_value + length.m_coded_size, seek_beginning))
          break;

        element_pos      = m_in->getFilePointer();
        uint32_t next_id = m_in->read_uint32_be();

        if (m_debug_resync)
          mxinfo(boost::format("kax_file::resync_to_level1_element():   next ID is %|1$x| at %2%\n") % next_id % element_pos);

        if (   ((0 != wanted_id) && (wanted_id != next_id))
            || ((0 == wanted_id) && !is_level1_element_id(vint_c(next_id, 4))))
          break;

        ++num_headers;
      }
    } catch (...) {
    }

    if ((4 == num_headers) || valid_unknown_size) {
      mxinfo(boost::format(Y("Resyncing successful at position %1%.\n")) % current_start_pos);
      m_in->setFilePointer(current_start_pos, seek_beginning);
      return read_next_level1_element(wanted_id, is_cluster_id);
    }

    m_in->setFilePointer(current_start_pos + 4, seek_beginning);
  }

  mxinfo(Y("Resync failed: no valid Matroska level 1 element found.\n"));

  return nullptr;
}
select_scanned_file_dlg::select_scanned_file_dlg(wxWindow *parent,
                                                 std::vector<playlist_file_cptr> const &playlists,
                                                 wxString const &orig_file_name)
  : wxDialog(parent, wxID_ANY, Z("Select file to add"), wxDefaultPosition, wxSize{1000, 650})
  , m_playlists{playlists}
  , m_selected_playlist_idx{0}
{
  // controls left column
	auto st_scanned_files = new wxStaticText(this, wxID_ANY, Z("Scanned files"));
	auto sl_left          = new wxStaticLine(this);
	m_lc_files            = new wxListCtrl(  this, ID_LC_PLAYLIST_FILE, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);

  // controls right column
	auto st_details        = new wxStaticText(this, wxID_ANY, Z("Details"));
	auto sl_right          = new wxStaticLine(this);
	auto st_duration       = new wxStaticText(this, wxID_ANY, Z("Duration:"));
	m_st_duration          = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_size           = new wxStaticText(this, wxID_ANY, Z("Size:"));
	m_st_size              = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_chapters       = new wxStaticText(this, wxID_ANY, Z("Number of chapters:"));
	m_st_chapters          = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_tracks         = new wxStaticText(this, wxID_ANY, Z("Tracks:"));
	m_lc_tracks            = new wxListCtrl(  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
	auto st_playlist_items = new wxStaticText(this, wxID_ANY, Z("Playlist items:"));
	m_lc_items             = new wxListCtrl(  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);

  // button controls
	auto b_add    = new wxButton(this, wxID_OK,     Z("&Add"));
	auto b_cancel = new wxButton(this, wxID_CANCEL, Z("&Cancel"));
	b_add->SetDefault();

  // list controls
  wxListItem item;

  item.SetText(Z("File name"));
  m_lc_files->InsertColumn(0, item);
  item.SetText(Z("Duration"));
  m_lc_files->InsertColumn(1, item);
  item.SetText(Z("Size"));
  m_lc_files->InsertColumn(2, item);

  item.SetText(Z("Type"));
  m_lc_tracks->InsertColumn(0, item);
  item.SetText(Z("Codec"));
  m_lc_tracks->InsertColumn(1, item);
  item.SetText(Z("Language"));
  m_lc_tracks->InsertColumn(2, item);

  item.SetText(Z("File name"));
  m_lc_items->InsertColumn(0, item);
  item.SetText(Z("Directory"));
  m_lc_items->InsertColumn(1, item);

  // fill "files" with data
  m_lc_files->Hide();
  long idx = 0;
  for (auto &playlist : m_playlists) {
    auto id = m_lc_files->InsertItem(idx, wxFileName{playlist->file_name}.GetFullName());
    m_lc_files->SetItem(id, 1, wxU(format_timecode(playlist->duration, 0)));
    m_lc_files->SetItem(id, 2, wxU(format_file_size(playlist->size)));
    m_lc_files->SetItemData(id, idx);

    if (orig_file_name == playlist->file_name) {
      m_lc_files->SetItemState(id, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
      m_selected_playlist_idx = idx;
    }

    ++idx;
  }

  m_lc_files->SetColumnWidth(0, wxLIST_AUTOSIZE);
  m_lc_files->SetColumnWidth(1, wxLIST_AUTOSIZE);
  m_lc_files->SetColumnWidth(2, wxLIST_AUTOSIZE);

  m_lc_files->Show();

  // layout
	auto siz_left_column = new wxBoxSizer(wxVERTICAL);
	siz_left_column->Add(st_scanned_files, 0, wxALL,            5);
	siz_left_column->Add(sl_left,          0, wxALL | wxEXPAND, 5);
	siz_left_column->Add(m_lc_files,       1, wxALL | wxEXPAND, 5);

	auto siz_info = new wxGridSizer(3, 2, 0, 0);
	siz_info->Add(st_duration,   0, wxALL, 5);
	siz_info->Add(m_st_duration, 0, wxALL, 5);
	siz_info->Add(st_size,       0, wxALL, 5);
	siz_info->Add(m_st_size,     0, wxALL, 5);
	siz_info->Add(st_chapters,   0, wxALL, 5);
	siz_info->Add(m_st_chapters, 0, wxALL, 5);

	auto siz_right_column = new wxBoxSizer(wxVERTICAL);
	siz_right_column->Add(st_details,        0, wxALL,            5);
	siz_right_column->Add(sl_right,          0, wxEXPAND | wxALL, 5);
	siz_right_column->Add(siz_info,          0, wxEXPAND,         5);
	siz_right_column->Add(st_tracks,         0, wxALL,            5);
	siz_right_column->Add(m_lc_tracks,       1, wxALL | wxEXPAND, 5);
	siz_right_column->Add(st_playlist_items, 0, wxALL,            5);
	siz_right_column->Add(m_lc_items,        1, wxALL | wxEXPAND, 5);

	auto siz_columns = new wxBoxSizer(wxHORIZONTAL);
	siz_columns->Add(siz_left_column,  1, wxEXPAND, 5);
	siz_columns->Add(siz_right_column, 1, wxEXPAND, 5);

	auto siz_buttons = new wxBoxSizer(wxHORIZONTAL);
	siz_buttons->AddStretchSpacer();
	siz_buttons->Add(b_add,    0, wxALL, 5);
	siz_buttons->AddStretchSpacer();
	siz_buttons->Add(b_cancel, 0, wxALL, 5);
	siz_buttons->AddStretchSpacer();

	auto siz_all = new wxBoxSizer(wxVERTICAL);
	siz_all->Add(siz_columns, 1, wxEXPAND, 5);
	siz_all->Add(siz_buttons, 0, wxEXPAND, 5);

	SetSizer(siz_all);
	Layout();

	Centre(wxBOTH);

  update_info();
}
select_scanned_file_dlg::select_scanned_file_dlg(wxWindow *parent,
                                                 std::vector<playlist_file_cptr> const &playlists,
                                                 wxString const &orig_file_name)
  : wxDialog{parent, wxID_ANY, Z("Select file to add"), wxDefaultPosition, wxSize{1000, 650}, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX}
  , m_playlists{playlists}
  , m_selected_playlist_idx{0}
  , m_geometry_saver{this, "select_scanned_file_dlg"}
  , m_sorted_by_column{}
  , m_sorted_ascending{}
{
  // controls left column
	auto st_scanned_files = new wxStaticText(this, wxID_ANY, Z("Scanned files"));
	auto sl_left          = new wxStaticLine(this);
	m_lc_files            = new wxListCtrl(  this, ID_LC_PLAYLIST_FILE, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);

  // controls right column
	auto st_details        = new wxStaticText(this, wxID_ANY, Z("Details"));
	auto sl_right          = new wxStaticLine(this);
	auto st_duration       = new wxStaticText(this, wxID_ANY, Z("Duration:"));
	m_st_duration          = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_size           = new wxStaticText(this, wxID_ANY, Z("Size:"));
	m_st_size              = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_chapters       = new wxStaticText(this, wxID_ANY, Z("Number of chapters:"));
	m_st_chapters          = new wxStaticText(this, wxID_ANY, wxEmptyString);
	auto st_tracks         = new wxStaticText(this, wxID_ANY, Z("Tracks:"));
	m_lc_tracks            = new wxListCtrl(  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
	auto st_playlist_items = new wxStaticText(this, wxID_ANY, Z("Playlist items:"));
	m_lc_items             = new wxListCtrl(  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);

  // button controls
	auto b_add    = new wxButton(this, wxID_OK,     Z("&Add"));
	auto b_cancel = new wxButton(this, wxID_CANCEL, Z("&Cancel"));
	b_add->SetDefault();

  // list controls
  m_lc_files->InsertColumn(0, Z("File name"));
  m_lc_files->InsertColumn(1, Z("Duration"), wxLIST_FORMAT_RIGHT);
  m_lc_files->InsertColumn(2, Z("Size"), wxLIST_FORMAT_RIGHT);

  m_lc_tracks->InsertColumn(0, Z("Type"));
  m_lc_tracks->InsertColumn(1, Z("Codec"));
  m_lc_tracks->InsertColumn(2, Z("Language"));

  m_lc_items->InsertColumn(0, Z("File name"));
  m_lc_items->InsertColumn(1, Z("Directory"));

  m_sort_arrows.Add(wx_get_png(sort_ascending));
  m_sort_arrows.Add(wx_get_png(sort_descending));
  m_lc_files->SetImageList(&m_sort_arrows, wxIMAGE_LIST_SMALL);

  // fill "files" with data
  m_lc_files->Hide();
  long idx = 0;
  for (auto &playlist : m_playlists) {
    auto id = m_lc_files->InsertItem(idx, wxFileName{playlist->file_name}.GetFullName());
    m_lc_files->SetItemData(id, idx);

    auto item = wxListItem{};
    item.SetId(id);
    item.SetColumn(1);
    item.SetText(wxU(format_timecode(playlist->duration, 0)));
    item.SetAlign(wxLIST_FORMAT_RIGHT);
    item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT);
    m_lc_files->SetItem(item);

    item.SetColumn(2);
    item.SetText(wxU(format_file_size(playlist->size)));
    m_lc_files->SetItem(item);

    if (orig_file_name == playlist->file_name) {
      m_lc_files->SetItemState(id, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
      m_selected_playlist_idx = idx;
    }

    ++idx;
  }

  sort_by(0, true);

  m_lc_files->SetColumnWidth(0, wxLIST_AUTOSIZE);
  m_lc_files->SetColumnWidth(1, wxLIST_AUTOSIZE);
  m_lc_files->SetColumnWidth(2, wxLIST_AUTOSIZE);

  m_lc_files->Show();

  // layout
	auto siz_left_column = new wxBoxSizer(wxVERTICAL);
	siz_left_column->Add(st_scanned_files, 0, wxALL,            5);
	siz_left_column->Add(sl_left,          0, wxALL | wxEXPAND, 5);
	siz_left_column->Add(m_lc_files,       1, wxALL | wxEXPAND, 5);

	auto siz_info = new wxGridSizer(3, 2, 0, 0);
	siz_info->Add(st_duration,   0, wxALL, 5);
	siz_info->Add(m_st_duration, 0, wxALL, 5);
	siz_info->Add(st_size,       0, wxALL, 5);
	siz_info->Add(m_st_size,     0, wxALL, 5);
	siz_info->Add(st_chapters,   0, wxALL, 5);
	siz_info->Add(m_st_chapters, 0, wxALL, 5);

	auto siz_right_column = new wxBoxSizer(wxVERTICAL);
	siz_right_column->Add(st_details,        0, wxALL,            5);
	siz_right_column->Add(sl_right,          0, wxEXPAND | wxALL, 5);
	siz_right_column->Add(siz_info,          0, wxEXPAND,         5);
	siz_right_column->Add(st_tracks,         0, wxALL,            5);
	siz_right_column->Add(m_lc_tracks,       1, wxALL | wxEXPAND, 5);
	siz_right_column->Add(st_playlist_items, 0, wxALL,            5);
	siz_right_column->Add(m_lc_items,        1, wxALL | wxEXPAND, 5);

	auto siz_columns = new wxBoxSizer(wxHORIZONTAL);
	siz_columns->Add(siz_left_column,  1, wxEXPAND, 5);
	siz_columns->Add(siz_right_column, 1, wxEXPAND, 5);

	auto siz_buttons = new wxBoxSizer(wxHORIZONTAL);
	siz_buttons->AddStretchSpacer();
	siz_buttons->Add(b_add,    0, wxALL, 5);
	siz_buttons->AddStretchSpacer();
	siz_buttons->Add(b_cancel, 0, wxALL, 5);
	siz_buttons->AddStretchSpacer();

	auto siz_all = new wxBoxSizer(wxVERTICAL);
	siz_all->Add(siz_columns, 1, wxEXPAND, 5);
	siz_all->Add(siz_buttons, 0, wxEXPAND, 5);

	SetSizerAndFit(siz_all);
	Layout();

	Centre(wxBOTH);

  m_geometry_saver.set_default_size(1000, 650, true).restore();

  update_info();
}
Exemple #15
0
int64_t
spu_extract_duration(unsigned char *data,
                     size_t buf_size,
                     int64_t timecode) {
    uint32_t date, control_start, next_off, start_off, off;
    unsigned char type;
    int duration;
    bool unknown;

    control_start = get_uint16_be(data + 2);
    next_off = control_start;
    duration = -1;
    start_off = 0;

    while ((start_off != next_off) && (next_off < buf_size)) {
        start_off = next_off;
        date = get_uint16_be(data + start_off) * 1024;
        next_off = get_uint16_be(data + start_off + 2);
        if (next_off < start_off) {
            mxwarn(boost::format(Y("spu_extraction_duration: Encountered broken SPU packet (next_off < start_off) at timecode %1%. "
                                   "This packet might be displayed incorrectly or not at all.\n")) % format_timecode(timecode, 3));
            return -1;
        }
        mxverb(4, boost::format("spu_extraction_duration: date = %1%\n") % date);
        off = start_off + 4;
        for (type = data[off++]; type != 0xff; type = data[off++]) {
            mxverb(4, boost::format("spu_extraction_duration: cmd = %1% ") % type);
            unknown = false;
            switch(type) {
            case 0x00:
                /* Menu ID, 1 byte */
                mxverb(4, "menu ID");
                break;
            case 0x01:
                /* Start display */
                mxverb(4, "start display");
                break;
            case 0x02:
                /* Stop display */
                mxverb(4, boost::format("stop display: %1%") % (date / 90));
                return (int64_t)date * 1000000 / 90;
                break;
            case 0x03:
                /* Palette */
                mxverb(4, "palette");
                off+=2;
                break;
            case 0x04:
                /* Alpha */
                mxverb(4, "alpha");
                off+=2;
                break;
            case 0x05:
                mxverb(4, "coords");
                off+=6;
                break;
            case 0x06:
                mxverb(4, "graphic lines");
                off+=4;
                break;
            case 0xff:
                /* All done, bye-bye */
                mxverb(4, "done");
                return duration;
            default:
                mxverb(4, boost::format("unknown (0x%|1$02x|), skipping %2% bytes.") % type % (next_off - off));
                unknown = true;
            }
            mxverb(4, "\n");
            if (unknown)
                break;
        }
    }
    return duration;
}
void
cluster_helper_c::split(packet_cptr &packet) {
  render();

  m->num_cue_elements = 0;

  bool create_new_file       = m->current_split_point->m_create_new_file;
  bool previously_discarding = m->discarding;

  mxdebug_if(m->debug_splitting, boost::format("Splitting: splitpoint %1% reached before timecode %2%, create new? %3%.\n") % m->current_split_point->str() % format_timecode(packet->assigned_timecode) % create_new_file);

  finish_file(false, create_new_file, previously_discarding);

  if (m->current_split_point->m_use_once) {
    if (   m->current_split_point->m_discard
        && (   (split_point_c::parts             == m->current_split_point->m_type)
            || (split_point_c::parts_frame_field == m->current_split_point->m_type))
        && (m->split_points.end() == (m->current_split_point + 1))) {
      mxdebug_if(m->debug_splitting, boost::format("Splitting: Last part in 'parts:' splitting mode finished\n"));
      m->splitting_and_processed_fully = true;
    }

    m->discarding = m->current_split_point->m_discard;
    ++m->current_split_point;
  }

  if (create_new_file) {
    create_next_output_file();
    if (g_no_linking) {
      m->previous_cluster_tc = -1;
      m->timecode_offset = g_video_packetizer ? m->max_video_timecode_rendered : packet->assigned_timecode;
    }

    m->bytes_in_file          =  0;
    m->first_timecode_in_file = -1;
    m->max_timecode_in_file   = -1;
    m->min_timecode_in_file.reset();
  }

  m->first_timecode_in_part = -1;

  handle_discarded_duration(create_new_file, previously_discarding);

  prepare_new_cluster();
}
void
cluster_helper_c::render_before_adding_if_necessary(packet_cptr &packet) {
  int64_t timecode        = get_timecode();
  int64_t timecode_delay  = (   (packet->assigned_timecode > m->max_timecode_in_cluster)
                             || (-1 == m->max_timecode_in_cluster))                       ? packet->assigned_timecode : m->max_timecode_in_cluster;
  timecode_delay         -= (   (-1 == m->min_timecode_in_cluster)
                             || (packet->assigned_timecode < m->min_timecode_in_cluster)) ? packet->assigned_timecode : m->min_timecode_in_cluster;
  timecode_delay          = (int64_t)(timecode_delay / g_timecode_scale);

  mxdebug_if(m->debug_packets,
             boost::format("cluster_helper_c::add_packet(): new packet { source %1%/%2% "
                           "timecode: %3% duration: %4% bref: %5% fref: %6% assigned_timecode: %7% timecode_delay: %8% }\n")
             % packet->source->m_ti.m_id % packet->source->m_ti.m_fname % packet->timecode          % packet->duration
             % packet->bref              % packet->fref                 % packet->assigned_timecode % format_timecode(timecode_delay));

  bool is_video_keyframe = (packet->source == g_video_packetizer) && packet->is_key_frame();
  bool do_render         = (std::numeric_limits<int16_t>::max() < timecode_delay)
                        || (std::numeric_limits<int16_t>::min() > timecode_delay)
                        || (   (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc)
                            && (packet->assigned_timecode                        > m->min_timecode_in_cluster)
                            && (!g_video_packetizer || !is_video_keyframe || m->first_video_keyframe_seen)
                            && (   (packet->gap_following && !m->packets.empty())
                                || ((packet->assigned_timecode - timecode) > g_max_ns_per_cluster)
                                || is_video_keyframe));

  if (is_video_keyframe)
    m->first_video_keyframe_seen = true;

  mxdebug_if(m->debug_rendering,
             boost::format("render check cur_tc %9% min_tc_ic %1% prev_cl_tc %2% test %3% is_vid_and_key %4% tc_delay %5% gap_following_and_not_empty %6% cur_tc>min_tc_ic %8% first_video_key_seen %10% do_render %7%\n")
             % m->min_timecode_in_cluster % m->previous_cluster_tc % (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc) % is_video_keyframe
             % timecode_delay % (packet->gap_following && !m->packets.empty()) % do_render % (packet->assigned_timecode > m->min_timecode_in_cluster) % packet->assigned_timecode % m->first_video_keyframe_seen);

  if (!do_render)
    return;

  render();
  prepare_new_cluster();
}