1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Libbrasero-burn
4 * Copyright (C) Philippe Rouquier 2005-2009 <bonfire-app@wanadoo.fr>
5 *
6 * Libbrasero-burn is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * The Libbrasero-burn authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Libbrasero-burn. This permission is above and beyond the permissions granted
14 * by the GPL license by which Libbrasero-burn is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * Libbrasero-burn is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to:
26 * The Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor
28 * Boston, MA 02110-1301, USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include "brasero-drive.h"
36 #include "brasero-medium.h"
37
38 #include "burn-debug.h"
39 #include "brasero-session-helper.h"
40 #include "brasero-track.h"
41 #include "brasero-track-data.h"
42 #include "brasero-track-data-cfg.h"
43 #include "brasero-session-span.h"
44
45 typedef struct _BraseroSessionSpanPrivate BraseroSessionSpanPrivate;
46 struct _BraseroSessionSpanPrivate
47 {
48 GSList * track_list;
49 BraseroTrack * last_track;
50 };
51
52 #define BRASERO_SESSION_SPAN_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_SESSION_SPAN, BraseroSessionSpanPrivate))
53
54 G_DEFINE_TYPE (BraseroSessionSpan, brasero_session_span, BRASERO_TYPE_BURN_SESSION);
55
56 /**
57 * brasero_session_span_get_max_space:
58 * @session: a #BraseroSessionSpan
59 *
60 * Returns the maximum required space (in sectors)
61 * among all the possible spanned batches.
62 * This means that when burningto a media
63 * it will also be the minimum required
64 * space to burn all the contents in several
65 * batches.
66 *
67 * Return value: a #goffset.
68 **/
69
70 goffset
brasero_session_span_get_max_space(BraseroSessionSpan * session)71 brasero_session_span_get_max_space (BraseroSessionSpan *session)
72 {
73 GSList *tracks;
74 goffset max_sectors = 0;
75 BraseroSessionSpanPrivate *priv;
76
77 g_return_val_if_fail (BRASERO_IS_SESSION_SPAN (session), 0);
78
79 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
80
81 if (priv->last_track) {
82 tracks = g_slist_find (priv->track_list, priv->last_track);
83
84 if (!tracks->next)
85 return 0;
86
87 tracks = tracks->next;
88 }
89 else if (priv->track_list)
90 tracks = priv->track_list;
91 else
92 tracks = brasero_burn_session_get_tracks (BRASERO_BURN_SESSION (session));
93
94 for (; tracks; tracks = tracks->next) {
95 BraseroTrack *track;
96 goffset track_blocks = 0;
97
98 track = tracks->data;
99
100 if (BRASERO_IS_TRACK_DATA_CFG (track))
101 return brasero_track_data_cfg_span_max_space (BRASERO_TRACK_DATA_CFG (track));
102
103 /* This is the common case */
104 brasero_track_get_size (BRASERO_TRACK (track),
105 &track_blocks,
106 NULL);
107
108 max_sectors = MAX (max_sectors, track_blocks);
109 }
110
111 return max_sectors;
112 }
113
114 /**
115 * brasero_session_span_again:
116 * @session: a #BraseroSessionSpan
117 *
118 * Checks whether some data were not included during calls to brasero_session_span_next ().
119 *
120 * Return value: a #BraseroBurnResult. BRASERO_BURN_OK if there is not anymore data.
121 * BRASERO_BURN_RETRY if the operation was successful and a new #BraseroTrackDataCfg was created.
122 * BRASERO_BURN_ERR otherwise.
123 **/
124
125 BraseroBurnResult
brasero_session_span_again(BraseroSessionSpan * session)126 brasero_session_span_again (BraseroSessionSpan *session)
127 {
128 GSList *tracks;
129 BraseroTrack *track;
130 BraseroSessionSpanPrivate *priv;
131
132 g_return_val_if_fail (BRASERO_IS_SESSION_SPAN (session), BRASERO_BURN_ERR);
133
134 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
135
136 /* This is not an error */
137 if (!priv->track_list)
138 return BRASERO_BURN_OK;
139
140 if (priv->last_track) {
141 tracks = g_slist_find (priv->track_list, priv->last_track);
142 if (!tracks->next) {
143 priv->track_list = NULL;
144 return BRASERO_BURN_OK;
145 }
146
147 return BRASERO_BURN_RETRY;
148 }
149
150 tracks = priv->track_list;
151 track = tracks->data;
152
153 if (BRASERO_IS_TRACK_DATA_CFG (track))
154 return brasero_track_data_cfg_span_again (BRASERO_TRACK_DATA_CFG (track));
155
156 return (tracks != NULL)? BRASERO_BURN_RETRY:BRASERO_BURN_OK;
157 }
158
159 /**
160 * brasero_session_span_possible:
161 * @session: a #BraseroSessionSpan
162 *
163 * Checks if a new #BraseroTrackData can be created from the files remaining in the tree
164 * after calls to brasero_session_span_next (). The maximum size of the data will be the one
165 * of the medium inserted in the #BraseroDrive set for @session (see brasero_burn_session_set_burner ()).
166 *
167 * Return value: a #BraseroBurnResult. BRASERO_BURN_OK if there is not anymore data.
168 * BRASERO_BURN_RETRY if the operation was successful and a new #BraseroTrackDataCfg was created.
169 * BRASERO_BURN_ERR otherwise.
170 **/
171
172 BraseroBurnResult
brasero_session_span_possible(BraseroSessionSpan * session)173 brasero_session_span_possible (BraseroSessionSpan *session)
174 {
175 GSList *tracks;
176 BraseroTrack *track;
177 goffset max_sectors = 0;
178 goffset track_blocks = 0;
179 BraseroSessionSpanPrivate *priv;
180
181 g_return_val_if_fail (BRASERO_IS_SESSION_SPAN (session), BRASERO_BURN_ERR);
182
183 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
184
185 max_sectors = brasero_burn_session_get_available_medium_space (BRASERO_BURN_SESSION (session));
186 if (max_sectors <= 0)
187 return BRASERO_BURN_ERR;
188
189 if (!priv->track_list)
190 tracks = brasero_burn_session_get_tracks (BRASERO_BURN_SESSION (session));
191 else if (priv->last_track) {
192 tracks = g_slist_find (priv->track_list, priv->last_track);
193 if (!tracks->next) {
194 priv->track_list = NULL;
195 return BRASERO_BURN_OK;
196 }
197 tracks = tracks->next;
198 }
199 else
200 tracks = priv->track_list;
201
202 if (!tracks)
203 return BRASERO_BURN_ERR;
204
205 track = tracks->data;
206
207 if (BRASERO_IS_TRACK_DATA_CFG (track))
208 return brasero_track_data_cfg_span_possible (BRASERO_TRACK_DATA_CFG (track), max_sectors);
209
210 /* This is the common case */
211 brasero_track_get_size (BRASERO_TRACK (track),
212 &track_blocks,
213 NULL);
214
215 if (track_blocks >= max_sectors)
216 return BRASERO_BURN_ERR;
217
218 return BRASERO_BURN_RETRY;
219 }
220
221 /**
222 * brasero_session_span_start:
223 * @session: a #BraseroSessionSpan
224 *
225 * Get the object ready for spanning a #BraseroBurnSession object. This function
226 * must be called before brasero_session_span_next ().
227 *
228 * Return value: a #BraseroBurnResult. BRASERO_BURN_OK if successful.
229 **/
230
231 BraseroBurnResult
brasero_session_span_start(BraseroSessionSpan * session)232 brasero_session_span_start (BraseroSessionSpan *session)
233 {
234 BraseroSessionSpanPrivate *priv;
235
236 g_return_val_if_fail (BRASERO_IS_SESSION_SPAN (session), BRASERO_BURN_ERR);
237
238 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
239
240 priv->track_list = brasero_burn_session_get_tracks (BRASERO_BURN_SESSION (session));
241 if (priv->last_track) {
242 g_object_unref (priv->last_track);
243 priv->last_track = NULL;
244 }
245
246 return BRASERO_BURN_OK;
247 }
248
249 /**
250 * brasero_session_span_next:
251 * @session: a #BraseroSessionSpan
252 *
253 * Sets the next batch of data to be burnt onto the medium inserted in the #BraseroDrive
254 * set for @session (see brasero_burn_session_set_burner ()). Its free space or it capacity
255 * will be used as the maximum amount of data to be burnt.
256 *
257 * Return value: a #BraseroBurnResult. BRASERO_BURN_OK if successful.
258 **/
259
260 BraseroBurnResult
brasero_session_span_next(BraseroSessionSpan * session)261 brasero_session_span_next (BraseroSessionSpan *session)
262 {
263 GSList *tracks;
264 gboolean pushed = FALSE;
265 goffset max_sectors = 0;
266 goffset total_sectors = 0;
267 BraseroSessionSpanPrivate *priv;
268
269 g_return_val_if_fail (BRASERO_IS_SESSION_SPAN (session), BRASERO_BURN_ERR);
270
271 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
272
273 g_return_val_if_fail (priv->track_list != NULL, BRASERO_BURN_ERR);
274
275 max_sectors = brasero_burn_session_get_available_medium_space (BRASERO_BURN_SESSION (session));
276 if (max_sectors <= 0)
277 return BRASERO_BURN_ERR;
278
279 /* NOTE: should we pop here? */
280 if (priv->last_track) {
281 tracks = g_slist_find (priv->track_list, priv->last_track);
282 g_object_unref (priv->last_track);
283 priv->last_track = NULL;
284
285 if (!tracks->next) {
286 priv->track_list = NULL;
287 return BRASERO_BURN_OK;
288 }
289 tracks = tracks->next;
290 }
291 else
292 tracks = priv->track_list;
293
294 for (; tracks; tracks = tracks->next) {
295 BraseroTrack *track;
296 goffset track_blocks = 0;
297
298 track = tracks->data;
299
300 if (BRASERO_IS_TRACK_DATA_CFG (track)) {
301 BraseroTrackData *new_track;
302 BraseroBurnResult result;
303
304 /* NOTE: the case where track_blocks < max_blocks will
305 * be handled by brasero_track_data_cfg_span () */
306
307 /* This track type is the only one to be able to span itself */
308 new_track = brasero_track_data_new ();
309 result = brasero_track_data_cfg_span (BRASERO_TRACK_DATA_CFG (track),
310 max_sectors,
311 new_track);
312 if (result != BRASERO_BURN_RETRY) {
313 g_object_unref (new_track);
314 return result;
315 }
316
317 pushed = TRUE;
318 brasero_burn_session_push_tracks (BRASERO_BURN_SESSION (session));
319 brasero_burn_session_add_track (BRASERO_BURN_SESSION (session),
320 BRASERO_TRACK (new_track),
321 NULL);
322 break;
323 }
324
325 /* This is the common case */
326 brasero_track_get_size (BRASERO_TRACK (track),
327 &track_blocks,
328 NULL);
329
330 /* NOTE: keep the order of tracks */
331 if (track_blocks + total_sectors >= max_sectors) {
332 BRASERO_BURN_LOG ("Reached end of spanned size");
333 break;
334 }
335
336 total_sectors += track_blocks;
337
338 if (!pushed) {
339 BRASERO_BURN_LOG ("Pushing tracks for media spanning");
340 brasero_burn_session_push_tracks (BRASERO_BURN_SESSION (session));
341 pushed = TRUE;
342 }
343
344 BRASERO_BURN_LOG ("Adding tracks");
345 brasero_burn_session_add_track (BRASERO_BURN_SESSION (session), track, NULL);
346
347 if (priv->last_track)
348 g_object_unref (priv->last_track);
349
350 priv->last_track = g_object_ref (track);
351 }
352
353 /* If we pushed anything it means we succeeded */
354 return (pushed? BRASERO_BURN_RETRY:BRASERO_BURN_ERR);
355 }
356
357 /**
358 * brasero_session_span_stop:
359 * @session: a #BraseroSessionSpan
360 *
361 * Ends and cleans a spanning operation started with brasero_session_span_start ().
362 *
363 **/
364
365 void
brasero_session_span_stop(BraseroSessionSpan * session)366 brasero_session_span_stop (BraseroSessionSpan *session)
367 {
368 BraseroSessionSpanPrivate *priv;
369
370 g_return_if_fail (BRASERO_IS_SESSION_SPAN (session));
371
372 priv = BRASERO_SESSION_SPAN_PRIVATE (session);
373
374 if (priv->last_track) {
375 g_object_unref (priv->last_track);
376 priv->last_track = NULL;
377 }
378 else if (priv->track_list) {
379 BraseroTrack *track;
380
381 track = priv->track_list->data;
382 if (BRASERO_IS_TRACK_DATA_CFG (track))
383 brasero_track_data_cfg_span_stop (BRASERO_TRACK_DATA_CFG (track));
384 }
385
386 priv->track_list = NULL;
387 }
388
389 static void
brasero_session_span_init(BraseroSessionSpan * object)390 brasero_session_span_init (BraseroSessionSpan *object)
391 { }
392
393 static void
brasero_session_span_finalize(GObject * object)394 brasero_session_span_finalize (GObject *object)
395 {
396 brasero_session_span_stop (BRASERO_SESSION_SPAN (object));
397 G_OBJECT_CLASS (brasero_session_span_parent_class)->finalize (object);
398 }
399
400 static void
brasero_session_span_class_init(BraseroSessionSpanClass * klass)401 brasero_session_span_class_init (BraseroSessionSpanClass *klass)
402 {
403 GObjectClass* object_class = G_OBJECT_CLASS (klass);
404
405 g_type_class_add_private (klass, sizeof (BraseroSessionSpanPrivate));
406
407 object_class->finalize = brasero_session_span_finalize;
408 }
409
410 /**
411 * brasero_session_span_new:
412 *
413 * Creates a new #BraseroSessionSpan object.
414 *
415 * Return value: a #BraseroSessionSpan object
416 **/
417
418 BraseroSessionSpan *
brasero_session_span_new(void)419 brasero_session_span_new (void)
420 {
421 return g_object_new (BRASERO_TYPE_SESSION_SPAN, NULL);
422 }
423
424