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; }
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(); }
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(); }
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)); }
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(); }
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(); }