|
@@ -4,43 +4,111 @@ using GtkCommonMark;
|
|
|
|
|
|
namespace PpubViewer.DocumentView {
|
|
|
|
|
|
- public class VideoView : MarkdownView {
|
|
|
+ public class VideoView : MarkdownView, DocumentViewWidget {
|
|
|
|
|
|
private Box box;
|
|
|
private Ppub.VideoManifest ppvm;
|
|
|
+ private Stack video_stack;
|
|
|
+ private Clamp video_clamp;
|
|
|
+ private Spinner spinner;
|
|
|
private Video video;
|
|
|
- private MediaStream current_stream;
|
|
|
+ private Ppub.Publication publication;
|
|
|
+
|
|
|
+ private File temp = null;
|
|
|
+ private double ratio_frac = 1;
|
|
|
+
|
|
|
|
|
|
public VideoView() {
|
|
|
base();
|
|
|
|
|
|
- box = new Box(Orientation.VERTICAL, 8);
|
|
|
+ box = new Box(Orientation.VERTICAL, 0);
|
|
|
scrolled_window.child = box;
|
|
|
markdown_view.vexpand = true;
|
|
|
|
|
|
video = new Video();
|
|
|
- box.append (video);
|
|
|
- box.append (clamp);
|
|
|
+ video_stack = new Stack();
|
|
|
+ video_clamp = new Clamp ();
|
|
|
+ spinner = new Spinner();
|
|
|
+
|
|
|
+ spinner.halign = Align.CENTER;
|
|
|
+ spinner.valign = Align.CENTER;
|
|
|
+ video_clamp.maximum_size = 1280;
|
|
|
+ video_clamp.child = video_stack;
|
|
|
+ spinner.spinning = true;
|
|
|
+ video_stack.add_child (spinner);
|
|
|
+ video_stack.add_child (video);
|
|
|
+ video_stack.transition_type = StackTransitionType.CROSSFADE;
|
|
|
+ video_stack.visible_child = spinner;
|
|
|
+ video_stack.vhomogeneous = true;
|
|
|
|
|
|
+ box.append (video_clamp);
|
|
|
+ box.append (clamp);
|
|
|
}
|
|
|
|
|
|
public override void load_asset (Ppub.Publication publication, Ppub.Asset asset) {
|
|
|
+ this.publication = publication;
|
|
|
+ video_stack.visible_child = spinner;
|
|
|
|
|
|
var stream = publication.read_asset (asset.name);
|
|
|
ppvm = new Ppub.VideoManifest.from_stream (stream);
|
|
|
-
|
|
|
- var s = publication.read_asset(ppvm.streams.first().file_name);
|
|
|
- var tmp = File.new_for_path ("/tmp/video");
|
|
|
- var ts = tmp.create (FileCreateFlags.REPLACE_DESTINATION);
|
|
|
- ts.splice (s, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET);
|
|
|
-
|
|
|
- var mf = MediaFile.for_file (tmp);
|
|
|
- video.set_media_stream(mf);
|
|
|
|
|
|
- print(@"Error: $(mf.error?.message)\n");
|
|
|
+ ratio_frac = ppvm.ratio[1] / ppvm.ratio[0];
|
|
|
|
|
|
var description_asset = publication.get_asset (ppvm.description_file_name);
|
|
|
base.load_asset (publication, description_asset);
|
|
|
+
|
|
|
+ new Thread<void>("ppvm_video_extractor", () => prepare_stream(ppvm.streams.first()));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void prepare_stream(Ppub.VideoDescription video) throws Error {
|
|
|
+
|
|
|
+ cleanup();
|
|
|
+
|
|
|
+ var s = publication.read_asset(ppvm.streams.first().file_name);
|
|
|
+ FileIOStream fios;
|
|
|
+ temp = File.new_tmp("ppub_ppvm_tmp_video_XXXXXX", out fios);
|
|
|
+
|
|
|
+ fios.output_stream.splice(s, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET);
|
|
|
+
|
|
|
+ GLib.Idle.add(video_ready);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool video_ready() {
|
|
|
+
|
|
|
+ var mf = MediaFile.for_file (temp);
|
|
|
+ video.set_media_stream(mf);
|
|
|
+ video_stack.visible_child = video;
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public override void reflow() {
|
|
|
+ var video_height = video_stack.get_width () * ratio_frac;
|
|
|
+ video_stack.height_request = (int)video_height;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void reset() {
|
|
|
+ video.set_media_stream(null);
|
|
|
+ video_stack.visible_child = spinner;
|
|
|
+ cleanup();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void cleanup() {
|
|
|
+ if(temp != null) {
|
|
|
+ try{
|
|
|
+ temp.delete();
|
|
|
+ }
|
|
|
+ catch {
|
|
|
+ warning(@"Could not clean up temporary file \"$(temp.get_path())\".");
|
|
|
+ }
|
|
|
+ temp = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ~VideoView() {
|
|
|
+ cleanup();
|
|
|
}
|
|
|
|
|
|
}
|