Initial commit
[fillmore] / src / marina / video_track.vala
1 /* Copyright 2009 Yorba Foundation
2  *
3  * This software is licensed under the GNU Lesser General Public License
4  * (version 2.1 or later).  See the COPYING file in this distribution. 
5  */
6
7
8 namespace Model {
9 public class VideoTrack : Track {
10     
11     public VideoTrack(Model.Project project) {
12         base(project, "Video Track");
13     }
14
15     protected override string name() { return "video"; }
16
17     public override MediaType media_type() {
18         return MediaType.VIDEO;
19     }
20
21     protected override bool check(Clip clip) {
22         Fraction rate1;
23         Fraction rate2;
24
25         if (!clip.clipfile.is_online())
26             return true;
27             
28         if (clips.size == 0)
29             return true;
30
31         if (!get_framerate(out rate2)) {
32             error_occurred("Cannot get initial frame rate!", null);
33             return false;
34         }
35         
36         if (!clip.clipfile.get_frame_rate(out rate1)) {
37             error_occurred("can't get frame rate", null);
38             return false;
39         }
40         
41         if (!rate1.equal(rate2)) {
42             error_occurred("can't insert clip with different frame rate", null);
43             return false;
44         }
45         return true;
46     }
47     
48     /* It would be nice if we could query or seek in frames using GST_FORMAT_DEFAULT, or ask
49      * GStreamer to convert frame->time and time->frame for us using gst_element_query_convert().
50      * Unfortunately there are several reasons we can't.
51      * 1) It appears that position queries using GST_FORMAT_DEFAULT are broken since GstBaseSink
52      *    won't pass them upstream.
53      * 2) Some codecs (e.g. theoradec) will convert between time and frames, but
54      *    others (e.g. ffmpegdec, dvdec) haven't implemented this conversion.
55      * 3) Even when a codec will perform conversions, its frame->time and time->frame functions may
56      *    not be perfect inverses; see the comments in time_to_frame(), below.
57      *
58      * So instead we must convert manually using the frame rate.
59      *
60      * TODO:   We should file GStreamer bugs for all of these.
61      */
62     
63     int64 frame_to_time(int frame) {
64         Fraction rate;
65         if (!get_framerate(out rate))
66             return 0;
67
68         return (int64) Gst.util_uint64_scale(frame, Gst.SECOND * rate.denominator, rate.numerator);
69     }
70     
71     int time_to_frame(int64 time) {
72         Fraction rate;
73         if (!get_framerate(out rate))
74             return 0;
75         return time_to_frame_with_rate(time, rate);
76     }
77     
78     public int get_current_frame(int64 time) {
79         return time_to_frame(time);
80     }
81     
82     public int64 previous_frame(int64 position) {
83         int frame = time_to_frame(position);
84         return frame_to_time(frame - 1);
85     }
86     
87     public int64 next_frame(int64 position) {
88         int frame = time_to_frame(position);
89         return frame_to_time(frame + 1);
90     }
91
92     public bool get_framerate(out Fraction rate) {
93         if (clips.size == 0)
94             return false;
95     
96         foreach (Clip c in clips) {
97             if (c.clipfile.is_online()) {
98                 bool can = c.clipfile.get_frame_rate(out rate);
99                 assert(can);
100                 
101                 return can;
102             }
103         }
104         
105         if (project.default_framerate.equal(Project.INVALID_FRAME_RATE))
106             return false;
107         
108         rate = project.default_framerate;
109         return true;
110     }
111 }
112     
113 }
114