Merge lp:~agronick/vocal/vocal into lp:vocal

Proposed by Kyle
Status: Needs review
Proposed branch: lp:~agronick/vocal/vocal
Merge into: lp:vocal
Diff against target: 448 lines (+114/-117)
4 files modified
CMakeLists.txt (+2/-1)
src/Objects/Episode.vala (+7/-3)
src/Utils/Player.vala (+103/-112)
src/Widgets/EpisodeDetailBox.vala (+2/-1)
To merge this branch: bzr merge lp:~agronick/vocal/vocal
Reviewer Review Type Date Requested Status
Nathan Dyer Pending
Review via email: mp+261302@code.launchpad.net

Description of the change

Vocal was crashing with a segmentation fault when playing certain videos. When I looked at the URL I found that it was doing a 302 redirect and setting the Location header. In Player.vala Gst.PbUtils.DiscovererInfo.get_video_streams() did not know how to handle this and would return an empty list. When it was cast to DiscovererVideoInfo a segmentation fault is caused.

Many podcasting services use redirects for analytics and tracking. All of the videos in this RSS feed will crash Vocal unless my code is used: http://feeds2.feedburner.com/AllJupiterVideos

The new code also prevents Vocal from cashing if get_video_streams() returns an empty list for some other reason.

To post a comment you must log in.
lp:~agronick/vocal/vocal updated
296. By Kyle

Fixed segfault with certain podcasts

Revision history for this message
Kyle (agronick) wrote :

I also fixed the bug that was causing the segfault with certain feeds. This was from accessing an array out of bounds in Eppisode.vala if the dates are not formatted correctly. After checking the length of the array it was displaying "(null)" where the dates are set to appear on the sidepane. I made it so it shows an empty string instead.

Revision history for this message
Kyle (agronick) wrote :

With regard to the first bug, some of the URLs in the feed may work. If you use this URL:
http://www.podtrac.com/pts/redirect.mp4/201406.jb-dl.cdn.scaleengine.net/unfilter/2015/unfilter-0144.mp4

It is one that will definitely crash with the current code and work with the new code.

lp:~agronick/vocal/vocal updated
297. By Kyle

Merging changes

298. By Kyle

Turns out redirects were not the issue with the origional bug. The timeouts were too small. Removed redirect handling code. Kept code preventing segmentation faults. Increased timeouts.

Revision history for this message
Kyle (agronick) wrote :

Turns out redirects were not the issue with the original bug. The timeouts were too small. Removed redirect handling code. Kept code preventing segmentation faults. CMakeLists.txt can be kept in it's original state but we probably want to remove the -g option for release builds. The first fix is in Player.vala and the second one is in Episode.vala. Email me for any questions: <email address hidden>

lp:~agronick/vocal/vocal updated
299. By Kyle

New seek method makes the player a lot more responsive.

Unmerged revisions

299. By Kyle

New seek method makes the player a lot more responsive.

298. By Kyle

Turns out redirects were not the issue with the origional bug. The timeouts were too small. Removed redirect handling code. Kept code preventing segmentation faults. Increased timeouts.

297. By Kyle

Merging changes

296. By Kyle

Fixed segfault with certain podcasts

295. By Kyle

Fixed bug with redirecting URLs

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-04-05 16:46:38 +0000
+++ CMakeLists.txt 2015-06-08 19:25:26 +0000
@@ -15,7 +15,7 @@
15list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)15list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
1616
17file(GLOB_RECURSE sources src/*.vala)17file(GLOB_RECURSE sources src/*.vala)
1818
19# Some configuration19# Some configuration
20configure_file (${CMAKE_SOURCE_DIR}/src/config.vala.cmake ${CMAKE_SOURCE_DIR}/src/config.vala)20configure_file (${CMAKE_SOURCE_DIR}/src/config.vala.cmake ${CMAKE_SOURCE_DIR}/src/config.vala)
2121
@@ -35,6 +35,7 @@
35 --disable-warnings35 --disable-warnings
36 --target-glib=2.3236 --target-glib=2.32
37 --thread37 --thread
38 --save-temps
38 -g39 -g
39)40)
4041
4142
=== modified file 'src/Objects/Episode.vala'
--- src/Objects/Episode.vala 2015-04-17 01:14:15 +0000
+++ src/Objects/Episode.vala 2015-06-08 19:25:26 +0000
@@ -122,15 +122,19 @@
122 offset = -2;122 offset = -2;
123 }123 }
124124
125 125 if(fields.length < 7)
126 126 return;
127
127 // Since the pubdate is a standard, we can hard-code the values where each item is located128 // Since the pubdate is a standard, we can hard-code the values where each item is located
128
129 int day = int.parse(fields[2 + offset]);129 int day = int.parse(fields[2 + offset]);
130 int year = int.parse(fields[4 + offset]);130 int year = int.parse(fields[4 + offset]);
131 int hour = int.parse(fields[5 + offset]);131 int hour = int.parse(fields[5 + offset]);
132 int minute = int.parse(fields[6 + offset]);132 int minute = int.parse(fields[6 + offset]);
133 int seconds = int.parse(fields[7 + offset]);133 int seconds = int.parse(fields[7 + offset]);
134
135 if(day == 0 || year == 0)
136 return;
137
134 138
135 139
136 // Determine the month number from the string as read from the pubdate140 // Determine the month number from the string as read from the pubdate
137141
=== modified file 'src/Utils/Player.vala'
--- src/Utils/Player.vala 2015-04-04 21:51:53 +0000
+++ src/Utils/Player.vala 2015-06-08 19:25:26 +0000
@@ -20,18 +20,18 @@
20using Clutter;20using Clutter;
21using GLib;21using GLib;
22using Gst;22using Gst;
23using Gst.PbUtils;23using Gst.PbUtils;
2424
25namespace Vocal {25namespace Vocal {
26 class Player : Actor {26 class Player : Actor {
2727
28 private static Player? player = null;28 private static Player? player = null;
29 public static Player? get_default (string[] args) {29 public static Player? get_default (string[] args) {
30 if (player == null)30 if (player == null)
31 player = new Player(args);31 player = new Player(args);
32 return player;32 return player;
33 }33 }
34 34
35 public signal void state_changed(Gst.State new_state);35 public signal void state_changed(Gst.State new_state);
36 public signal void stream_ended();36 public signal void stream_ended();
37 public signal void additional_plugins_required(Gst.Message message);37 public signal void additional_plugins_required(Gst.Message message);
@@ -43,20 +43,20 @@
4343
44 private MainLoop loop = new MainLoop ();44 private MainLoop loop = new MainLoop ();
45 public signal void new_position_available();45 public signal void new_position_available();
46 private string tag_string; 46 private string tag_string;
47 47
48 public bool is_currently_playing;48 public bool is_currently_playing;
49 49
50 public Gst.State current_state;50 public Gst.State current_state;
51 51
52 public Episode current_episode;52 public Episode current_episode;
53 53
54 private Player(string[]? args) {54 private Player(string[]? args) {
5555
56 /* NOTE:56 /* NOTE:
57 This code is heavily influenced by the Audience source code, written by Tom Beckmann <tomjonabc@gmail.com>57 This code is heavily influenced by the Audience source code, written by Tom Beckmann <tomjonabc@gmail.com>
58 and Corentin Noël <corentin@elementaryos.org>, available at https://launchpad.net/audience.58 and Corentin Noël <corentin@elementaryos.org>, available at https://launchpad.net/audience.
59 */59 */
6060
61 // Set up the Clutter actor61 // Set up the Clutter actor
62 video = new Clutter.Texture ();62 video = new Clutter.Texture ();
@@ -77,7 +77,7 @@
77 current_episode = null;77 current_episode = null;
7878
79 is_currently_playing = false;79 is_currently_playing = false;
80 80
81 // Check every half-second if the current media is playing, and if it is81 // Check every half-second if the current media is playing, and if it is
82 // send a signal that there is a new position available82 // send a signal that there is a new position available
83 GLib.Timeout.add(500, () => {83 GLib.Timeout.add(500, () => {
@@ -85,14 +85,14 @@
85 new_position_available();85 new_position_available();
86 return true;86 return true;
87 });87 });
88 88
89 }89 }
90 90
91 /*91 /*
92 * Bus callback92 * Bus callback
93 */93 */
94 private bool bus_callback (Gst.Bus bus, Gst.Message message) {94 private bool bus_callback (Gst.Bus bus, Gst.Message message) {
95 95
96 switch (message.type) {96 switch (message.type) {
97 case MessageType.ERROR:97 case MessageType.ERROR:
98 GLib.Error err;98 GLib.Error err;
@@ -115,11 +115,11 @@
115 Gst.State oldstate;115 Gst.State oldstate;
116 Gst.State newstate;116 Gst.State newstate;
117 Gst.State pending;117 Gst.State pending;
118 118
119 119
120 message.parse_state_changed (out oldstate, out newstate,120 message.parse_state_changed (out oldstate, out newstate,
121 out pending);121 out pending);
122 122
123 current_state = newstate;123 current_state = newstate;
124 state_changed(newstate);124 state_changed(newstate);
125125
@@ -135,30 +135,30 @@
135135
136 return true;136 return true;
137 }137 }
138 138
139 /*139 /*
140 * Reconfigures the video (width, positions, etc.) as needed140 * Reconfigures the video (width, positions, etc.) as needed
141 */141 */
142 public void configure_video() {142 public void configure_video() {
143143
144 // Make sure the video has both a known width and height at this point144 // Make sure the video has both a known width and height at this point
145 if(video_width > 0 && video_height > 0) {145 if(video_width > 0 && video_height > 0) {
146146
147 // Get the stage147 // Get the stage
148 var stage = get_stage ();148 var stage = get_stage ();
149149
150 // Calculate the videos aspect ratio150 // Calculate the videos aspect ratio
151151
152 var aspect = (float)stage.width / (float)video_width < (float)stage.height / (float)video_height ?152 var aspect = (float)stage.width / (float)video_width < (float)stage.height / (float)video_height ?
153 (float)stage.width / (float)video_width : (float)stage.height / (float)video_height;153 (float)stage.width / (float)video_width : (float)stage.height / (float)video_height;
154154
155 video.width = video_width * aspect;155 video.width = video_width * aspect;
156 video.height = video_height * aspect;156 video.height = video_height * aspect;
157 video.x = (stage.width - video.width) / 2;157 video.x = (stage.width - video.width) / 2;
158 video.y = (stage.height - video.height) / 2;158 video.y = (stage.height - video.height) / 2;
159 }159 }
160 }160 }
161 161
162 /*162 /*
163 * Iterates through a list of tags and sets matching properties163 * Iterates through a list of tags and sets matching properties
164 */164 */
@@ -171,28 +171,28 @@
171 break;171 break;
172 }172 }
173 }173 }
174 174
175 /*175 /*
176 * Returns the total duration of the currently playing media176 * Returns the total duration of the currently playing media
177 */177 */
178 public int64 get_duration () {178 public int64 get_duration () {
179 int64 rv = (int64)0;179 int64 rv = (int64)0;
180 Gst.Format f = Gst.Format.TIME;180 Gst.Format f = Gst.Format.TIME;
181 181
182 playbin.query_duration (f, out rv);182 playbin.query_duration (f, out rv);
183 183
184 return rv;184 return rv;
185 }185 }
186 186
187 /*187 /*
188 * Returns the current position of the media188 * Returns the current position of the media
189 */189 */
190 public int64 get_position () {190 public int64 get_position () {
191 int64 rv = (int64)0;191 int64 rv = (int64)0;
192 Gst.Format f = Gst.Format.TIME;192 Gst.Format f = Gst.Format.TIME;
193 193
194 playbin.query_position (f, out rv);194 playbin.query_position (f, out rv);
195 195
196 return rv;196 return rv;
197 }197 }
198/*198/*
@@ -202,21 +202,21 @@
202 }202 }
203203
204 public double get_volume () {204 public double get_volume () {
205 var val = GLib.Value (typeof(double));205 var val = GLib.Value (typeof(double));
206 playbin.get_property ("volume", ref val);206 playbin.get_property ("volume", ref val);
207 return (double)val;207 return (double)val;
208 }208 }
209*/209*/
210 210
211 /*211 /*
212 * Sets the current state to pause212 * Sets the current state to pause
213 */213 */
214 public void pause () {214 public void pause () {
215 set_state (Gst.State.PAUSED);215 set_state (Gst.State.PAUSED);
216 is_currently_playing = false;216 is_currently_playing = false;
217 }217 }
218 218
219 /*219 /*
220 * Sets the current state to playing220 * Sets the current state to playing
221 */221 */
222 public void play () {222 public void play () {
@@ -225,7 +225,7 @@
225225
226 is_currently_playing = true;226 is_currently_playing = true;
227 }227 }
228 228
229 /*229 /*
230 * Seeks backward in the currently playing media n number of seconds230 * Seeks backward in the currently playing media n number of seconds
231 */231 */
@@ -235,13 +235,13 @@
235 int64 current = this.get_position();235 int64 current = this.get_position();
236 int64 new_position = current - mil;236 int64 new_position = current - mil;
237 this.set_position(new_position);237 this.set_position(new_position);
238 238
239 new_position_available();239 new_position_available();
240 240
241 // If it was paused before, pause it again241 // If it was paused before, pause it again
242 set_state(previous);242 set_state(previous);
243 }243 }
244 244
245 /*245 /*
246 * Seeks forward in the currently playing media n number of seconds246 * Seeks forward in the currently playing media n number of seconds
247 */247 */
@@ -251,13 +251,13 @@
251 int64 current = this.get_position();251 int64 current = this.get_position();
252 int64 new_position = current + mil;252 int64 new_position = current + mil;
253 this.set_position(new_position);253 this.set_position(new_position);
254 254
255 new_position_available();255 new_position_available();
256 256
257 // If it was paused before, pause it again257 // If it was paused before, pause it again
258 set_state(previous);258 set_state(previous);
259 }259 }
260 260
261 /*261 /*
262 * Sets the episode that is currently being played262 * Sets the episode that is currently being played
263 */263 */
@@ -266,38 +266,44 @@
266 // Set the previous state to ready266 // Set the previous state to ready
267 set_state(Gst.State.PAUSED);267 set_state(Gst.State.PAUSED);
268 set_state(Gst.State.READY);268 set_state(Gst.State.READY);
269 269
270 this.current_episode = episode;270 this.current_episode = episode;
271 271
272 // Set playbin to null272 // Set playbin to null
273 set_state(Gst.State.NULL);273 set_state(Gst.State.NULL);
274 274
275 // Set the URI275 // Set the URI
276 playbin.uri = episode.playback_uri;276 playbin.uri = episode.playback_uri;
277 info("Setting URI: %s".printf(episode.playback_uri));277 info("Setting URI: %s".printf(episode.playback_uri));
278 278
279 Gst.Bus bus = playbin.get_bus ();
280 bus.add_watch (0, bus_callback);
281
279 // If it's a video podcast, get the width and height and configure that information282 // If it's a video podcast, get the width and height and configure that information
280 if(episode.parent.content_type == MediaType.VIDEO) {283 if(episode.parent.content_type == MediaType.VIDEO) {
281 try {284
282 var info = new Gst.PbUtils.Discoverer (10 * Gst.SECOND).discover_uri (playbin.uri);285 var info = new Gst.PbUtils.Discoverer (50 * Gst.SECOND).discover_uri (episode.playback_uri);
283 var video = info.get_video_streams ();286 var video = info.get_video_streams ();
284 if (video.data != null) {287
285 var video_info = (Gst.PbUtils.DiscovererVideoInfo)video.data;288 try {
286 video_width = video_info.get_width ();289
287 video_height = video_info.get_height ();290 video.foreach ((entry) => {
288291 DiscovererVideoInfo video_info = (DiscovererVideoInfo)entry;
289 configure_video();292 video_width = video_info.get_width ();
290293 video_height = video_info.get_height ();
291 }294 });
292 }catch(Error e) {295
293 warning(e.message);296 configure_video();
294 }297
295 }298 }catch(Error e) {
296 299 warning(e.message);
297 Gst.Bus bus = playbin.get_bus ();300 }
298 bus.add_watch (0, bus_callback);301 }
302
303
299 }304 }
300 305
306
301 /*307 /*
302 * Change the playback rate308 * Change the playback rate
303 */309 */
@@ -307,35 +313,20 @@
307 Gst.Format.TIME, Gst.SeekFlags.SKIP,313 Gst.Format.TIME, Gst.SeekFlags.SKIP,
308 Gst.SeekType.NONE, pos,314 Gst.SeekType.NONE, pos,
309 Gst.SeekType.NONE, get_duration ());315 Gst.SeekType.NONE, get_duration ());
310 } 316 }
311 317
312 /*318 /*
313 * Sets the currently playing media position319 * Sets the currently playing media position
314 */320 */
315 public void set_position (int64 pos) {321 public void set_position (int64 pos) {
316322
317 // Slowly dial the volume back (should help avoid the crappy choppy sound when streaming)323 playbin.seek_simple (Gst.Format.TIME,
318 for(double i = 0.1; i <= 1; i += 0.1) {324 Gst.SeekFlags.FLUSH |
319 set_volume(1.0 - i);325 Gst.SeekFlags.KEY_UNIT, pos);
320 Thread.usleep(50000);326
321 }327
322
323 playbin.seek (1.0,
324 Gst.Format.TIME, Gst.SeekFlags.FLUSH,
325 Gst.SeekType.SET, pos,
326 Gst.SeekType.NONE, get_duration ());
327
328
329 // Let this thread sleep for .7 seconds to let GStreamer get caught up
330 Thread.usleep(700000);
331
332 // Turn the volume back up
333 for(double j = 0.1; j <= 1; j += 0.1) {
334 set_volume(j);
335 Thread.usleep(10000);
336 }
337 }328 }
338 329
339 /*330 /*
340 * Set the player to a designated state331 * Set the player to a designated state
341 */332 */
@@ -348,11 +339,11 @@
348 playbin.set_state (s);339 playbin.set_state (s);
349 }340 }
350341
351 /*342 /*
352 * Sets the current volume343 * Sets the current volume
353 */344 */
354 public void set_volume (double val) {345 public void set_volume (double val) {
355 playbin.set_property ("volume", val);346 playbin.set_property ("volume", val);
356 }347 }
357 }348 }
358}349}
359350
=== modified file 'src/Widgets/EpisodeDetailBox.vala'
--- src/Widgets/EpisodeDetailBox.vala 2015-04-04 19:32:30 +0000
+++ src/Widgets/EpisodeDetailBox.vala 2015-06-08 19:25:26 +0000
@@ -150,7 +150,8 @@
150 if(episode.datetime_released != null) {150 if(episode.datetime_released != null) {
151 release_label = new Gtk.Label(episode.datetime_released.format(" %x"));151 release_label = new Gtk.Label(episode.datetime_released.format(" %x"));
152 } else {152 } else {
153 release_label = new Gtk.Label(episode.date_released);153 //We must fill the label with an empty string otherwise it prints "(null)"
154 release_label = new Gtk.Label("");
154 }155 }
155 release_label.xalign = 0;156 release_label.xalign = 0;
156 release_label.justify = Gtk.Justification.LEFT;157 release_label.justify = Gtk.Justification.LEFT;

Subscribers

People subscribed via source and target branches

to all changes: