static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) { std::vector<std::pair<int, std::string>> subList; // Load blocks uint64_t startTime, endTime, filePos; unsigned int rt, frameSize, frameFlags; while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) { if (ps->IsCancelled()) return; if (frameSize == 0) continue; const auto readBuf = input->file.read(filePos, frameSize); const auto readBufEnd = readBuf + frameSize; // Get start and end times int64_t timecodeScaleLow = 1000000; agi::Time subStart = startTime / timecodeScaleLow; agi::Time subEnd = endTime / timecodeScaleLow; using str_range = boost::iterator_range<const char *>; // Process SSA/ASS if (!srt) { auto first = std::find(readBuf, readBufEnd, ','); if (first == readBufEnd) continue; auto second = std::find(first + 1, readBufEnd, ','); if (second == readBufEnd) continue; subList.emplace_back( boost::lexical_cast<int>(str_range(readBuf, first)), agi::format("Dialogue: %d,%s,%s,%s" , boost::lexical_cast<int>(str_range(first + 1, second)) , subStart.GetAssFormatted() , subEnd.GetAssFormatted() , str_range(second + 1, readBufEnd))); } // Process SRT else { auto line = agi::format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s" , subStart.GetAssFormatted() , subEnd.GetAssFormatted() , str_range(readBuf, readBufEnd)); boost::replace_all(line, "\r\n", "\\N"); boost::replace_all(line, "\r", "\\N"); boost::replace_all(line, "\n", "\\N"); subList.emplace_back(subList.size(), std::move(line)); } ps->SetProgress(startTime / timecodeScaleLow, totalTime); } // Insert into file sort(begin(subList), end(subList)); for (auto order_value_pair : subList) parser->AddLine(order_value_pair.second); }
////////////////// // Actually parse void MatroskaWrapper::Parse() { // Clear keyframes and timecodes keyFrames.Clear(); bytePos.Clear(); timecodes.clear(); // Get info int tracks = mkv_GetNumTracks(file); TrackInfo *trackInfo; SegmentInfo *segInfo = mkv_GetFileInfo(file); // Parse tracks for (int track=0; track<tracks; track++) { trackInfo = mkv_GetTrackInfo(file,track); // Video track if (trackInfo->Type == 1) { // Variables ulonglong startTime, endTime, filePos; unsigned int rt, frameSize, frameFlags; CompressedStream *cs = NULL; // Timecode scale __int64 timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale; // Mask other tracks away mkv_SetTrackMask(file, ~(1 << track)); // Progress bar int totalTime = double(segInfo->Duration) / timecodeScale; volatile bool canceled = false; DialogProgress *progress = new DialogProgress(NULL,_("Parsing Matroska"),&canceled,_("Reading keyframe and timecode data from Matroska file."),0,totalTime); progress->Show(); progress->SetProgress(0,1); // Read frames int frameN = 0; while (mkv_ReadFrame(file,0,&rt,&startTime,&endTime,&filePos,&frameSize,&frameFlags) == 0) { // Read value double curTime = double(startTime) / 1000000.0; frames.push_back(MkvFrame((frameFlags & FRAME_KF) != 0,curTime,filePos)); frameN++; // Cancelled? if (canceled) { Close(); throw _T("Canceled"); } // Update progress progress->SetProgress(curTime,totalTime); } // Clean up progress if (!canceled) progress->Destroy(); break; } } // Copy raw for (std::list<MkvFrame>::iterator cur=frames.begin(); cur!=frames.end(); cur++) { rawFrames.push_back(*cur); } // Process timecodes and keyframes frames.sort(); MkvFrame curFrame(false,0,0); int i = 0; for (std::list<MkvFrame>::iterator cur=frames.begin(); cur!=frames.end(); cur++) { curFrame = *cur; if (curFrame.isKey) keyFrames.Add(i); bytePos.Add(curFrame.filePos); timecodes.push_back(curFrame.time); i++; } }
///////////// // Get frame wxBitmap LAVCVideoProvider::GetFrame(int n) { // Return stored frame n = MID(0,n,GetFrameCount()-1); if (n == frameNumber) { if (!validFrame) { curFrame = AVFrameToWX(frame, n); validFrame = true; } return curFrame; } // Following frame, just get it if (n == frameNumber+1) { GetNextFrame(); //wxLogMessage(wxString::Format(_T("%i"),lastDecodeTime)); } // Needs to seek else { // Prepare seek __int64 seekTo; int result = 0; #if 0 // Get time to seek to if (isMkv) { //__int64 base = AV_TIME_BASE; //__int64 time = VFR_Output.GetTimeAtFrame(n,true) * base / 1000000; //seekTo = av_rescale(time,stream->time_base.den,AV_TIME_BASE * __int64(stream->time_base.num)); //seekTo = __int64(n) * 1000 * stream->r_frame_rate.den / stream->r_frame_rate.num; //seekTo = bytePos[n]; //result = av_seek_frame(formatContext,vidStream,seekTo,AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_BYTE); // Prepare mkv seek ulonglong startTime, endTime, filePos; unsigned int rt, frameSize, frameFlags; ulonglong targetTime = __int64(VFR_Output.GetTimeAtFrame(n,true,true))*1000000; //ulonglong targetTime = __int64(n) * 1000 * stream->r_frame_rate.den / stream->r_frame_rate.num; //ulonglong targetTime = mkv.rawFrames[n].time * 1000000; mkv_Seek(mkv.file,targetTime,MKVF_SEEK_TO_PREV_KEYFRAME); // Seek if (mkv_ReadFrame(mkv.file,0,&rt,&startTime,&endTime,&filePos,&frameSize,&frameFlags) == 0) { result = av_seek_frame(formatContext,vidStream,filePos,AVSEEK_FLAG_BYTE | AVSEEK_FLAG_BACKWARD); int curpos = 0; for (unsigned int i=0;i<mkv.rawFrames.size();i++) { if (mkv.rawFrames[i].time == startTime / 1000000.0) curpos = i; } int seek = n - curpos; for (int i=0;i<seek;i++) { GetNextFrame(); } } } // Constant frame rate else { #endif seekTo = n; result = av_seek_frame(lavcfile->fctx,vidStream,seekTo,AVSEEK_FLAG_BACKWARD); // Seek to keyframe if (result == 0) { avcodec_flush_buffers(codecContext); // Seek until final frame bool ok = true; do { ok = GetNextFrame(); } while (lastDecodeTime <= n && ok); } // Failed seeking else { GetNextFrame(); } #if 0 } #endif } // Bitmap wxBitmap bmp; if (frame) bmp = AVFrameToWX(frame, n); else bmp = wxBitmap(GetWidth(),GetHeight()); // Set current frame validFrame = true; curFrame = bmp; frameNumber = n; // Return return curFrame; }