~ubuntu-branches/ubuntu/wily/aegisub/wily-proposed

« back to all changes in this revision

Viewing changes to src/async_video_provider.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel, Pascal De Vuyst, Juan Picca, Sebastian Reichel
  • Date: 2015-08-04 21:40:50 UTC
  • mfrom: (5.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20150804214050-y2aghm9vdksoc8t7
Tags: 3.2.2+dfsg-1
[ Pascal De Vuyst ]
* Fix Typo in package description (Closes: #739219)

[ Juan Picca ]
* Add patch to fix reproducible build (Closes: #789728)

[ Sebastian Reichel ]
* New upstream release
 - remove vendor directory from orig tarball
* Update Debian Standards Version to 3.9.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
 
2
//
 
3
// Permission to use, copy, modify, and distribute this software for any
 
4
// purpose with or without fee is hereby granted, provided that the above
 
5
// copyright notice and this permission notice appear in all copies.
 
6
//
 
7
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 
10
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
11
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
12
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 
13
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
14
//
 
15
// Aegisub Project http://www.aegisub.org/
 
16
 
 
17
#include "async_video_provider.h"
 
18
 
 
19
#include "ass_dialogue.h"
 
20
#include "ass_file.h"
 
21
#include "export_fixstyle.h"
 
22
#include "include/aegisub/subtitles_provider.h"
 
23
#include "video_frame.h"
 
24
#include "video_provider_manager.h"
 
25
 
 
26
#include <libaegisub/dispatch.h>
 
27
 
 
28
enum {
 
29
        NEW_SUBS_FILE = -1,
 
30
        SUBS_FILE_ALREADY_LOADED = -2
 
31
};
 
32
 
 
33
std::shared_ptr<VideoFrame> AsyncVideoProvider::ProcFrame(int frame_number, double time, bool raw) {
 
34
        // Find an unused buffer to use or allocate a new one if needed
 
35
        std::shared_ptr<VideoFrame> frame;
 
36
        for (auto& buffer : buffers) {
 
37
                if (buffer.use_count() == 1) {
 
38
                        frame = buffer;
 
39
                        break;
 
40
                }
 
41
        }
 
42
 
 
43
        if (!frame) {
 
44
                frame = std::make_shared<VideoFrame>();
 
45
                buffers.push_back(frame);
 
46
        }
 
47
 
 
48
        try {
 
49
                source_provider->GetFrame(frame_number, *frame);
 
50
        }
 
51
        catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); }
 
52
 
 
53
        if (raw || !subs_provider || !subs) return frame;
 
54
 
 
55
        try {
 
56
                if (single_frame != frame_number && single_frame != SUBS_FILE_ALREADY_LOADED) {
 
57
                        // Generally edits and seeks come in groups; if the last thing done
 
58
                        // was seek it is more likely that the user will seek again and
 
59
                        // vice versa. As such, if this is the first frame requested after
 
60
                        // an edit, only export the currently visible lines (because the
 
61
                        // other lines will probably not be viewed before the file changes
 
62
                        // again), and if it's a different frame, export the entire file.
 
63
                        if (single_frame != NEW_SUBS_FILE) {
 
64
                                subs_provider->LoadSubtitles(subs.get());
 
65
                                single_frame = SUBS_FILE_ALREADY_LOADED;
 
66
                        }
 
67
                        else {
 
68
                                AssFixStylesFilter::ProcessSubs(subs.get());
 
69
                                single_frame = frame_number;
 
70
                                subs_provider->LoadSubtitles(subs.get(), time);
 
71
                        }
 
72
                }
 
73
        }
 
74
        catch (agi::Exception const& err) { throw SubtitlesProviderErrorEvent(err.GetMessage()); }
 
75
 
 
76
        try {
 
77
                subs_provider->DrawSubtitles(*frame, time / 1000.);
 
78
        }
 
79
        catch (agi::UserCancelException const&) { }
 
80
 
 
81
        return frame;
 
82
}
 
83
 
 
84
static std::unique_ptr<SubtitlesProvider> get_subs_provider(wxEvtHandler *evt_handler, agi::BackgroundRunner *br) {
 
85
        try {
 
86
                return SubtitlesProviderFactory::GetProvider(br);
 
87
        }
 
88
        catch (agi::Exception const& err) {
 
89
                evt_handler->AddPendingEvent(SubtitlesProviderErrorEvent(err.GetMessage()));
 
90
                return nullptr;
 
91
        }
 
92
}
 
93
 
 
94
AsyncVideoProvider::AsyncVideoProvider(agi::fs::path const& video_filename, std::string const& colormatrix, wxEvtHandler *parent, agi::BackgroundRunner *br)
 
95
: worker(agi::dispatch::Create())
 
96
, subs_provider(get_subs_provider(parent, br))
 
97
, source_provider(VideoProviderFactory::GetProvider(video_filename, colormatrix, br))
 
98
, parent(parent)
 
99
{
 
100
}
 
101
 
 
102
AsyncVideoProvider::~AsyncVideoProvider() {
 
103
        // Block until all currently queued jobs are complete
 
104
        worker->Sync([]{});
 
105
}
 
106
 
 
107
void AsyncVideoProvider::LoadSubtitles(const AssFile *new_subs) throw() {
 
108
        uint_fast32_t req_version = ++version;
 
109
 
 
110
        auto copy = new AssFile(*new_subs);
 
111
        worker->Async([=]{
 
112
                subs.reset(copy);
 
113
                single_frame = NEW_SUBS_FILE;
 
114
                ProcAsync(req_version, false);
 
115
        });
 
116
}
 
117
 
 
118
void AsyncVideoProvider::UpdateSubtitles(const AssFile *new_subs, std::set<const AssDialogue*> const& changes) throw() {
 
119
        uint_fast32_t req_version = ++version;
 
120
 
 
121
        // Copy just the lines which were changed, then replace the lines at the
 
122
        // same indices in the worker's copy of the file with the new entries
 
123
        std::vector<AssDialogue *> changed;
 
124
        for (auto d : changes)
 
125
                changed.push_back(new AssDialogue(*d));
 
126
 
 
127
        worker->Async([=]{
 
128
                int i = 0;
 
129
                auto it = subs->Events.begin();
 
130
                for (auto& update : changed) {
 
131
                        std::advance(it, update->Row - i);
 
132
                        i = update->Row;
 
133
                        subs->Events.insert(it, *update);
 
134
                        delete &*it--;
 
135
                }
 
136
 
 
137
                single_frame = NEW_SUBS_FILE;
 
138
                ProcAsync(req_version, true);
 
139
        });
 
140
}
 
141
 
 
142
void AsyncVideoProvider::RequestFrame(int new_frame, double new_time) throw() {
 
143
        uint_fast32_t req_version = ++version;
 
144
 
 
145
        worker->Async([=]{
 
146
                time = new_time;
 
147
                frame_number = new_frame;
 
148
                ProcAsync(req_version, false);
 
149
        });
 
150
}
 
151
 
 
152
bool AsyncVideoProvider::NeedUpdate(std::vector<AssDialogueBase const*> const& visible_lines) {
 
153
        // Always need to render after a seek
 
154
        if (single_frame != NEW_SUBS_FILE || frame_number != last_rendered)
 
155
                return true;
 
156
 
 
157
        // Obviously need to render if the number of visible lines has changed
 
158
        if (visible_lines.size() != last_lines.size())
 
159
                return true;
 
160
 
 
161
        for (size_t i = 0; i < last_lines.size(); ++i) {
 
162
                auto const& last = last_lines[i];
 
163
                auto const& cur = *visible_lines[i];
 
164
                if (last.Layer  != cur.Layer)  return true;
 
165
                if (last.Margin != cur.Margin) return true;
 
166
                if (last.Style  != cur.Style)  return true;
 
167
                if (last.Effect != cur.Effect) return true;
 
168
                if (last.Text   != cur.Text)   return true;
 
169
 
 
170
                // Changing the start/end time effects the appearance only if the
 
171
                // line is animated. This is obviously not a very accurate check for
 
172
                // animated lines, but false positives aren't the end of the world
 
173
                if ((last.Start != cur.Start || last.End != cur.End) &&
 
174
                        (!cur.Effect.get().empty() || cur.Text.get().find('\\') != std::string::npos))
 
175
                        return true;
 
176
        }
 
177
 
 
178
        return false;
 
179
}
 
180
 
 
181
void AsyncVideoProvider::ProcAsync(uint_fast32_t req_version, bool check_updated) {
 
182
        // Only actually produce the frame if there's no queued changes waiting
 
183
        if (req_version < version || frame_number < 0) return;
 
184
 
 
185
        std::vector<AssDialogueBase const*> visible_lines;
 
186
        for (auto const& line : subs->Events) {
 
187
                if (!line.Comment && !(line.Start > time || line.End <= time))
 
188
                        visible_lines.push_back(&line);
 
189
        }
 
190
 
 
191
        if (check_updated && !NeedUpdate(visible_lines)) return;
 
192
 
 
193
        last_lines.clear();
 
194
        last_lines.reserve(visible_lines.size());
 
195
        for (auto line : visible_lines)
 
196
                last_lines.push_back(*line);
 
197
        last_rendered = frame_number;
 
198
 
 
199
        try {
 
200
                FrameReadyEvent *evt = new FrameReadyEvent(ProcFrame(frame_number, time), time);
 
201
                evt->SetEventType(EVT_FRAME_READY);
 
202
                parent->QueueEvent(evt);
 
203
        }
 
204
        catch (wxEvent const& err) {
 
205
                // Pass error back to parent thread
 
206
                parent->QueueEvent(err.Clone());
 
207
        }
 
208
}
 
209
 
 
210
std::shared_ptr<VideoFrame> AsyncVideoProvider::GetFrame(int frame, double time, bool raw) {
 
211
        std::shared_ptr<VideoFrame> ret;
 
212
        worker->Sync([&]{ ret = ProcFrame(frame, time, raw); });
 
213
        return ret;
 
214
}
 
215
 
 
216
void AsyncVideoProvider::SetColorSpace(std::string const& matrix) {
 
217
        worker->Async([=] { source_provider->SetColorSpace(matrix); });
 
218
}
 
219
 
 
220
wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
 
221
wxDEFINE_EVENT(EVT_VIDEO_ERROR, VideoProviderErrorEvent);
 
222
wxDEFINE_EVENT(EVT_SUBTITLES_ERROR, SubtitlesProviderErrorEvent);
 
223
 
 
224
VideoProviderErrorEvent::VideoProviderErrorEvent(VideoProviderError const& err)
 
225
: agi::Exception(err.GetMessage())
 
226
{
 
227
        SetEventType(EVT_VIDEO_ERROR);
 
228
}
 
229
SubtitlesProviderErrorEvent::SubtitlesProviderErrorEvent(std::string const& err)
 
230
: agi::Exception(err)
 
231
{
 
232
        SetEventType(EVT_SUBTITLES_ERROR);
 
233
}