126
118
void PublishMedia(const Gtk::TreeIter& itr, RefPtr<MediaStore> ms, MediaItem mi)
128
120
FillThumbnail(itr, ms, *mi);
129
(*itr)[ms->columns.media] = mi;
121
(*itr)[MediaStore::Fields().media] = mi;
131
123
PublishMediaVis vis(itr, ms);
135
static std::string MarkError(const std::string& val, bool not_error)
139
return "<span foreground=\"red\">" + val + "</span>";
142
static std::string MarkBoolError(bool val, bool not_error)
144
return MarkError(std::string(val ? _("yes") : _("no")), not_error);
156
ErrorDesc(): res(true) {}
159
static void SetImportError(ErrorDesc& ed, bool is_good, const std::string& out_str,
160
const std::string& desc_str = std::string())
162
if( !is_good && ed.res )
165
ed.descStr = desc_str; // внимание на первую ошибку
169
ed.outStr += M_SPS + out_str;
173
static std::string FpsToStr(const Point& frate)
175
return (str::stream() << (double)frate.x/frate.y).str();
178
static std::string TVTypeStr(bool is_ntsc)
180
return std::string(is_ntsc ? "NTSC" : "PAL/SECAM");
183
void CheckVideoFormat(ErrorDesc& ed, const Mpeg::SequenceData& vid, bool is_ntsc)
185
using namespace boost;
187
// Doc: DVD-Video/1. Mpucoder Specs/DVD/dvdmpeg.html
188
// проверка подходимости для DVD
190
int kbps = vid.bytRat/400;
191
bool is_byte_rate_ok = kbps <= 9800; // Kbps
192
SetImportError(ed, is_byte_rate_ok,
193
std::string(_("Video bitrate")) + ":\t" +
194
MarkError(lexical_cast<std::string>(kbps), is_byte_rate_ok) + " " + _("kbps"),
195
_("Maximum data rate for video (9800 kbps) is exceeded."));
197
const char* Descriptions[] = {
198
N_("The %1% DVD-Video can accept MPEG-2 with resolutions: %2% only."),
199
N_("The %1% DVD-Video can accept MPEG-2 with frame rate: %2% only."),
200
N_("The %1% DVD-Video can accept MPEG-2 with aspects 4:3, 16:9 only.")
202
std::string tv_type = TVTypeStr(is_ntsc);
205
// * (352x480, 352x288 - не по стандарту, см. Trac#9)
206
Point ntsc_resolutions[] = { Point(720, 480), Point(704, 480), Point(352, 480), Point(352, 240) };
207
Point pal_resolutions[] = { Point(720, 576), Point(704, 576), Point(352, 576), Point(352, 288) };
208
Point sz(vid.wdh, vid.hgt);
210
std::string resol_list;
211
Point* resolutions = is_ntsc ? ntsc_resolutions : pal_resolutions ;
212
for( int i=0; i<(int)ARR_SIZE(ntsc_resolutions); i++ )
214
sz_ok = sz_ok || sz == resolutions[i];
215
if( !resol_list.empty() )
217
resol_list += PointToStr(resolutions[i]);
219
SetImportError(ed, sz_ok,
220
std::string(_("Video size")) + ": \t" + MarkError(PointToStr(sz), sz_ok),
221
BF_(Descriptions[0]) % tv_type % resol_list % bf::stop);
224
Point frate(vid.framRat);
225
// прогрессивность не учитываем, потому что на DVD Demystified(VTS_18_1.VOB) не сработало
226
//bool is_progr = vid.isProgr;
227
bool frate_ok = false;
228
std::string frate_list;
231
frate_ok = frate == Point(24, 1) || frate == Point(30000, 1001);
232
frate_list = FpsToStr(Point(24, 1)) + ", " + FpsToStr(Point(30000, 1001));
236
frate_ok = frate == Point(25, 1);
237
frate_list = FpsToStr(Point(25, 1));
239
SetImportError(ed, frate_ok,
240
std::string(_("Frame rate")) + ": \t" +
241
MarkError(FpsToStr(frate), frate_ok) + " " + _("fps"),
242
BF_(Descriptions[1]) % tv_type % frate_list % bf::stop);
245
bool is_aspect_ok = vid.sarCode == af4_3 || vid.sarCode == af16_9;
246
Point aspect = vid.SizeAspect();
247
std::string aspect_str = (str::stream() << aspect.x << ':' << aspect.y).str();
248
SetImportError(ed, is_aspect_ok,
249
std::string(_("Aspect ratio")) + ": \t" + MarkError(aspect_str, is_aspect_ok),
250
BF_(Descriptions[2]) % tv_type % bf::stop);
253
bool no_delay = !vid.lowDelay;
254
// Translators: Low delay is very tech term and can be left as is.
255
SetImportError(ed, no_delay, "\"Low delay\": \t" + MarkBoolError(!no_delay, no_delay));
261
bool CanOpenAsVideo(const char* fname, std::string& err_string, bool& must_be_video)
264
io::stream& strm = pd.srcStrm;
265
Mpeg::MediaInfo& inf = pd.mInf;
268
if( !Mpeg::GetInfo(pd, fname) )
269
err_string = inf.ErrorReason();
272
must_be_video = true; // дальше не проверяем тип - это точно видео
273
Mpeg::SequenceData& vid = inf.vidSeq;
276
bool is_ntsc = !AData().PalTvSystem();
277
CheckVideoFormat(ed, vid, is_ntsc);
278
bool video_check = ed.res;
280
// * мультиплексирование в формате DVD
281
bool is_dvd_mux = false;
282
bool is_nav_found = false;
283
const int pack_cnt = 8; // первые 8*2048=16Kb проверяем
284
if( inf.endPos >= pack_cnt*DVD_PACK_SZ )
286
uint8_t buf[DVD_PACK_SZ];
290
for( int i=0; i<pack_cnt && strm.read((char*)buf, DVD_PACK_SZ); )
292
if( !(buf[0] == 0 && buf[1] == 0 && buf[2] == 1 && buf[2] && 0xba) )
299
// COPY_N_PASTE_ETALON из dvdvob.c, http://dvdauthor.sourceforge.net/
301
// Замечание - из-за недоступности стандарта DVD-VIDEO для нас стандарт - dvdauthor!
302
// Более того, судя по первому замечанию в TODO для версии 0.6.14, - размер system header
303
// может быть не равен 18 байтам (но сути это не меняет - должны принимать только то, что
304
// сможет принять dvdauthor)
308
buf[17] == 0xbb ) // system header
310
if( buf[38] == 0 && buf[39] == 0 && buf[40] == 1 && buf[41] == 0xbf && // 1st private2
311
buf[1024] == 0 && buf[1025] == 0 && buf[1026] == 1 && buf[1027] == 0xbf ) // 2nd private2
315
// пропускаем спец. pad-данные самого dvdauthor - не считаются
316
// (добавляются при деавторинге, добавлении субтитров)
317
if ( buf[14] == 0 && buf[15] == 0 && buf[16] == 1 && buf[17] == 0xbe &&
318
strcmp((char*)buf+20,"dvdauthor-data") == 0 )
324
std::string dvd_mux_desc = _("<b>Bombono DVD</b> can use \"DVD-ready\" video only now."
325
" Use muxing programs like \"mplex -f 8\" (from <b>mjpegtools</b>),"
326
" mencoder (from <b>mplayer</b>) or <b>transcode</b> to make your video ready for <b>Bombono DVD</b>.");
327
SetImportError(ed, is_dvd_mux,
328
boost::format("%1%: \t") % _("DVD packs") % bf::stop + MarkBoolError(is_dvd_mux, is_dvd_mux), dvd_mux_desc);
329
SetImportError(ed, is_nav_found,
330
boost::format("%1%: \t") % _("NAV packets") % bf::stop + MarkBoolError(is_nav_found, is_nav_found), dvd_mux_desc);
331
bool dvd_check = is_dvd_mux && is_nav_found;
336
err_string = _("This video may not be added due to (errors in <span foreground=\"red\">red color</span>):");
337
err_string += "\n<tt>" + ed.outStr + "</tt>";
339
std::string desc_str = ed.descStr;
340
if( !video_check && dvd_check )
343
CheckVideoFormat(ed2, vid, !is_ntsc);
346
// подскажем пользователю, что он ошибся форматом проекта
347
desc_str = BF_("This video has %1% type and can't be added to"
348
" current project of %2% type. Create new project from"
349
" menu \"Project->New Project\" with right type.") %
350
TVTypeStr(!is_ntsc) % TVTypeStr(is_ntsc) % bf::stop;
354
if( !desc_str.empty() )
355
err_string += "\n\n" + desc_str;
362
inline bool CaseIEqual(const std::string &s1, const std::string &s2)
364
return strcasecmp(s1.c_str(), s2.c_str()) == 0;
367
StorageItem CreateMedia(const char* fname, std::string& err_string)
373
if( !fs::exists(pth) )
375
err_string = _("File doesn't exist.");
378
if( fs::is_directory(pth) )
380
err_string = _("Folders can't be added.");
385
// 2 определение типа файла
386
// Пока открываем только минимальное кол-во
387
// типов медиа (картинки и MPEG2), потому простой
389
// :TODO: переделать; с помощью библиотеки libmagick
390
// (утилита '/usr/bin/file') реализовать первичное
391
// определение типа файла
394
std::string fext = get_extension(pth);
395
bool must_be_video = CaseIEqual(fext, "mpeg") || CaseIEqual(fext, "mpg") ||
396
CaseIEqual(fext, "m2v") || CaseIEqual(fext, "vob");
398
std::string video_err_str;
399
if( CanOpenAsVideo(fname, video_err_str, must_be_video) )
403
md->MakeByPath(fname);
405
else if( !must_be_video && gdk_pixbuf_get_file_info(fname, &wdh, &hgt) )
408
md = new StillImageMD;
409
md->MakeByPath(fname);
414
// по расширению выводим наиболее вероятную ошибку
415
err_string = must_be_video ? video_err_str : _("Unknown file type.") ;
420
127
static void OnVideoView(TrackLayout& layout, VideoMD* vd, int chp_pos)
422
129
using namespace Timeline;
579
304
PublishMedia(ms->append(), ms, *itr);
582
bool TryAddMedia(const char* fname, Gtk::TreePath& pth, std::string& err_str,
586
if( StorageItem md = CreateMedia(fname, err_str) )
589
LOG_INF << "Insert Media!" << io::endl;
591
RefPtr<MediaStore> ms = GetAStores().mdStore;
592
Gtk::TreeIter itr = InsertByPos(ms, pth, insert_after);
593
PublishMedia(itr, ms, md);
596
pth = ms->get_path(itr);
601
// desc - метка происхождения, добавления
602
void TryAddMediaQuiet(const std::string& fname, const std::string& desc)
606
if( !TryAddMedia(fname.c_str(), pth, err_str) )
608
LOG_ERR << "TryAddMediaQuiet error (" << desc << "): " << err_str << io::endl;
612
static std::string StandFNameOut(const fs::path& pth)
614
return "<span style=\"italic\" underline=\"low\">" +
615
pth.leaf() + "</span>";
618
#if GTK_CHECK_VERSION(2,18,0)
619
#define LABEL_HAS_A_TAG
622
static void AddMediaError(const std::string& msg_str, const std::string& desc_str)
624
#ifdef LABEL_HAS_A_TAG
625
MessageBoxWeb(msg_str, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, desc_str);
627
MessageBox(msg_str, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, desc_str);
631
void TryAddMedias(const Str::List& paths, MediaBrowser& brw,
632
Gtk::TreePath& brw_pth, bool insert_after)
634
// * подсказка с импортом
637
const std::string fname = paths[0];
639
std::string leaf = pth.leaf();
641
std::string::const_iterator start = leaf.begin(), end = leaf.end();
642
static boost::regex dvd_video_vob("(VIDEO_TS|VTS_[0-9][0-9]_[0-9]).VOB",
643
boost::regex::perl|boost::regex::icase);
645
if( boost::regex_match(start, end, dvd_video_vob) &&
646
MessageBox(BF_("The file \"%1%\" looks like VOB from DVD.\nRun import?") % leaf % bf::stop,
647
Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL) == Gtk::RESPONSE_OK )
649
DVD::RunImport(*GetTopWindow(brw), pth.branch_path().string());
654
std::string ext = get_extension(pth);
655
const char* el_array[] = {"m2v", "mp2", "mpa", "ac3", "dts", "lpcm", 0 };
657
for( const char** el = el_array; *el ; el++ )
663
if( res && MessageBox(BF_("The file \"%1%\" looks like elementary stream and need to be muxed before using. Run muxing?") % leaf % bf::stop,
664
Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL) == Gtk::RESPONSE_OK )
666
MuxAddStreams(fname);
675
// куда переходим в браузере
676
Gtk::TreePath goto_path;
677
std::string err_desc;
678
for( Str::List::const_iterator itr = paths.begin(), end = paths.end();
681
const std::string& fpath = *itr;
682
std::string ConvertPathToUtf8(const std::string& path);
683
fs::path pth = ConvertPathToUtf8(fpath);
685
bool is_exist = false;
686
// * проверяем, есть ли такой уже
687
RefPtr<MediaStore> ms = brw.GetMediaStore();
688
for( MediaStore::iterator itr = ms->children().begin(), end = ms->children().end();
691
StorageItem si = GetAsStorage(ms->GetMedia(itr));
692
if( fs::equivalent(pth, si->GetPath()) )
694
// только переходим к нему
695
brw.get_selection()->select(GetBrowserPath(si));
703
bool res = TryAddMedia(fpath.c_str(), brw_pth, err_str, insert_after);
706
insert_after = true; // вставляем друг за другом
714
const int max_show_errors = 2+1;
715
if( err_cnt < max_show_errors )
717
if( !err_desc.empty() )
719
err_desc += StandFNameOut(pth);
720
if( !err_str.empty() )
721
err_desc += "\n" + err_str;
725
err_desc += (err_cnt == max_show_errors) ? std::string("\n\n") + _("Also:") + " " : std::string(", ") ;
726
err_desc += StandFNameOut(pth);
733
bool one_error = (err_cnt == 1);
734
std::string desc = one_error ? err_str : err_desc ;
736
#ifdef LABEL_HAS_A_TAG
737
std::string online_tip("\n\n");
738
online_tip += BF_("See more about preparing video for authoring in <a href=\"%1%\">online help</a>.") %
739
"http://www.bombono.org/Preparing_sources_for_DVD" % bf::stop;
744
// :KLUDGE: хотелось использовать ngettext() для того чтоб в PO строки были рядом,
745
// однако msgfmt для требует чтобы в обоих вариантах присутствовало одинаковое
746
// кол-во заполнителей
747
//boost::format frmt(ngettext("Can't add file \"%1%\".", "Can't add files:", err_cnt));
749
AddMediaError(BF_("Can't add file \"%1%\".") % err_pth.leaf() % bf::stop, desc);
751
AddMediaError(_("Can't add files:"), desc);
754
if( !goto_path.empty() )
755
GoToPos(brw, goto_path);
758
Str::List GetFilenames(Gtk::FileChooser& fc)
760
// я знаю, что это жутко неэффективно (2+1 копирования), но не актуально
761
typedef Glib::SListHandle<Glib::ustring> UList;
762
UList ulist = fc.get_filenames();
765
std::copy(ulist.begin(), ulist.end(), std::back_inserter(list));
769
static void MediaBrowserAdd(MediaBrowser& brw, const char* /*fname*/,
770
Gtk::FileChooser& fc)
772
Str::List paths(GetFilenames(fc));
774
Gtk::TreePath brw_pth = GetCursor(brw);
775
ValidateMediaInsertionPos(brw_pth);
777
TryAddMedias(paths, brw, brw_pth, true);
780
307
void OnBrowserRowActivated(MediaBrowser& brw, MediaActionFnr fnr, const Gtk::TreeModel::Path& path)
782
309
RefPtr<MediaStore> ms = brw.GetMediaStore();