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