1 /* OGMRip - A library for DVD ripping and encoding
2  * Copyright (C) 2004-2012 Olivier Rolland <billl@users.sourceforge.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "ogmrip-container.h"
24 #include "ogmrip-fs.h"
25 #include "ogmrip-mplayer.h"
26 #include "ogmrip-plugin.h"
27 #include "ogmrip-version.h"
28 
29 #include "ogmjob-exec.h"
30 #include "ogmjob-queue.h"
31 
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <glib/gi18n-lib.h>
37 #include <glib/gstdio.h>
38 
39 #define PROGRAM "mp4box"
40 
41 #define OGMRIP_TYPE_MP4           (ogmrip_mp4_get_type ())
42 #define OGMRIP_MP4(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), OGMRIP_TYPE_MP4, OGMRipMp4))
43 #define OGMRIP_MP4_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), OGMRIP_TYPE_MP4, OGMRipMp4Class))
44 #define OGMRIP_IS_MP4(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OGMRIP_TYPE_MP4))
45 #define OGMRIP_IS_MP4_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE ((klass), OGMRIP_TYPE_MP4))
46 #define OGMRIP_MP4_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), OGMRIP_TYPE_MP4, OGMRipMp4Class))
47 
48 typedef struct _OGMRipMp4      OGMRipMp4;
49 typedef struct _OGMRipMp4Class OGMRipMp4Class;
50 
51 struct _OGMRipMp4
52 {
53   OGMRipContainer parent_instance;
54 
55   guint nstreams;
56   guint streams;
57   guint old_percent;
58 
59   guint nsplits;
60   guint splits;
61   guint split_percent;
62 };
63 
64 struct _OGMRipMp4Class
65 {
66   OGMRipContainerClass parent_class;
67 };
68 
69 GType ogmrip_mp4_get_type (void);
70 static gint ogmrip_mp4_run (OGMJobSpawn *spawn);
71 
72 static void
ogmrip_mp4_append_audio_file(OGMRipContainer * mp4,const gchar * filename,gint format,gint language,GPtrArray * argv)73 ogmrip_mp4_append_audio_file (OGMRipContainer *mp4, const gchar *filename,
74     gint format, gint language, GPtrArray *argv)
75 {
76   struct stat buf;
77 
78   if (g_stat (filename, &buf) == 0 && buf.st_size > 0)
79   {
80     const gchar *fmt;
81 
82     switch (format)
83     {
84       case OGMRIP_FORMAT_AAC:
85         fmt = "aac";
86         break;
87       case OGMRIP_FORMAT_MP3:
88         fmt = "mp3";
89         break;
90       case OGMRIP_FORMAT_VORBIS:
91         fmt = "ogg";
92         break;
93       case OGMRIP_FORMAT_AC3:
94       case OGMRIP_FORMAT_COPY:
95         fmt = "ac3";
96         break;
97       default:
98         fmt = NULL;
99         break;
100     }
101 
102     if (fmt)
103     {
104       const gchar *iso639_2 = NULL;
105 
106       g_ptr_array_add (argv, g_strdup ("-add"));
107       if (language > -1)
108         iso639_2 = ogmdvd_get_language_iso639_2 (language);
109       if (iso639_2)
110         g_ptr_array_add (argv, g_strdup_printf ("%s:fmt=%s:lang=%s:group=1:#audio", filename, fmt, iso639_2));
111       else
112         g_ptr_array_add (argv, g_strdup_printf ("%s:fmt=%s:group=1:#audio", filename, fmt));
113     }
114   }
115 }
116 
117 static void
ogmrip_mp4_append_subp_file(OGMRipContainer * mp4,const gchar * filename,gint format,gint language,GPtrArray * argv)118 ogmrip_mp4_append_subp_file (OGMRipContainer *mp4, const gchar *filename,
119     gint format, gint language, GPtrArray *argv)
120 {
121   struct stat buf;
122 
123   if (g_stat (filename, &buf) == 0 && buf.st_size > 0)
124   {
125     const gchar *fmt;
126 
127     switch (format)
128     {
129       case OGMRIP_FORMAT_SRT:
130         fmt = "srt";
131         break;
132       case OGMRIP_FORMAT_VOBSUB:
133         fmt = "vobsub";
134         break;
135       default:
136         fmt = NULL;
137         break;
138     }
139 
140     if (fmt)
141     {
142       const gchar *iso639_2 = NULL;
143 
144       g_ptr_array_add (argv, g_strdup ("-add"));
145       if (language > -1)
146         iso639_2 = ogmdvd_get_language_iso639_2 (language);
147       if (iso639_2)
148         g_ptr_array_add (argv, g_strdup_printf ("%s:fmt=%s:lang=%s", filename, fmt, iso639_2));
149       else
150         g_ptr_array_add (argv, g_strdup_printf ("%s:fmt=%s", filename, fmt));
151     }
152   }
153 }
154 
155 static void
ogmrip_mp4_foreach_audio(OGMRipContainer * mp4,OGMRipCodec * codec,guint demuxer,gint language,GPtrArray * argv)156 ogmrip_mp4_foreach_audio (OGMRipContainer *mp4,
157     OGMRipCodec *codec, guint demuxer, gint language, GPtrArray *argv)
158 {
159   const gchar *input;
160   gint format;
161 
162   input = ogmrip_codec_get_output (codec);
163   format = ogmrip_plugin_get_audio_codec_format (G_TYPE_FROM_INSTANCE (codec));
164 
165   ogmrip_mp4_append_audio_file (mp4, input, format, language, argv);
166 }
167 
168 static void
ogmrip_mp4_foreach_subp(OGMRipContainer * mp4,OGMRipCodec * codec,guint demuxer,gint language,GPtrArray * argv)169 ogmrip_mp4_foreach_subp (OGMRipContainer *mp4,
170     OGMRipCodec *codec, guint demuxer, gint language, GPtrArray *argv)
171 {
172   const gchar *input;
173   gint format;
174 
175   input = ogmrip_codec_get_output (codec);
176   format = ogmrip_plugin_get_subp_codec_format (G_TYPE_FROM_INSTANCE (codec));
177 
178   ogmrip_mp4_append_subp_file (mp4, input, format, language, argv);
179 }
180 
181 static void
ogmrip_mp4_foreach_chapters(OGMRipContainer * mp4,OGMRipCodec * codec,guint demuxer,gint language,GPtrArray * argv)182 ogmrip_mp4_foreach_chapters (OGMRipContainer *mp4,
183     OGMRipCodec *codec, guint demuxer, gint language, GPtrArray *argv)
184 {
185   const gchar *input;
186   struct stat buf;
187 
188   input = ogmrip_codec_get_output (codec);
189   if (g_stat (input, &buf) == 0 && buf.st_size > 0)
190   {
191     g_ptr_array_add (argv, g_strdup ("-chap"));
192     g_ptr_array_add (argv, g_strdup (input));
193   }
194 }
195 
196 static void
ogmrip_mp4_foreach_file(OGMRipContainer * mp4,OGMRipFile * file,GPtrArray * argv)197 ogmrip_mp4_foreach_file (OGMRipContainer *mp4, OGMRipFile *file, GPtrArray *argv)
198 {
199   gchar *filename;
200 
201   filename = ogmrip_file_get_filename (file);
202   if (filename)
203   {
204     gint format, language;
205 
206     format = ogmrip_file_get_format (file);
207     language = ogmrip_file_get_language (file);
208 
209     switch (ogmrip_file_get_type (file))
210     {
211       case OGMRIP_FILE_TYPE_AUDIO:
212         ogmrip_mp4_append_audio_file (mp4, filename, format, language, argv);
213         break;
214       case OGMRIP_FILE_TYPE_SUBP:
215         ogmrip_mp4_append_subp_file (mp4, filename, format, language, argv);
216         break;
217       default:
218         g_assert_not_reached ();
219         break;
220     }
221   }
222   g_free (filename);
223 }
224 
225 static gdouble
ogmrip_mp4_get_output_fps(OGMRipCodec * codec)226 ogmrip_mp4_get_output_fps (OGMRipCodec *codec)
227 {
228   guint output_rate_numerator, output_rate_denominator;
229 
230   if (ogmrip_codec_get_telecine (codec) || ogmrip_codec_get_progressive (codec))
231   {
232     output_rate_numerator = 24000;
233     output_rate_denominator = 1001;
234   }
235   else
236     ogmrip_codec_get_framerate (codec, &output_rate_numerator, &output_rate_denominator);
237 
238   return output_rate_numerator / (gdouble) (output_rate_denominator * ogmrip_codec_get_framestep (codec));
239 }
240 
241 static gchar **
ogmrip_mp4box_extract_command(OGMRipVideoCodec * video)242 ogmrip_mp4box_extract_command (OGMRipVideoCodec *video)
243 {
244   GPtrArray *argv;
245   const gchar *filename;
246 
247   argv = g_ptr_array_new ();
248   g_ptr_array_add (argv, g_strdup (PROGRAM));
249   g_ptr_array_add (argv, g_strdup ("-aviraw"));
250   g_ptr_array_add (argv, g_strdup ("video"));
251 
252   filename = ogmrip_codec_get_output (OGMRIP_CODEC (video));
253   g_ptr_array_add (argv, g_strdup (filename));
254 
255   g_ptr_array_add (argv, NULL);
256 
257   return (gchar **) g_ptr_array_free (argv, FALSE);
258 }
259 
260 static gdouble
ogmrip_mp4box_extract_watch(OGMJobExec * exec,const gchar * buffer,OGMRipContainer * mp4)261 ogmrip_mp4box_extract_watch (OGMJobExec *exec, const gchar *buffer, OGMRipContainer *mp4)
262 {
263   gchar *sep;
264   guint percent;
265 
266   if ((sep = strrchr (buffer, '(')) && sscanf (sep, "(%u/100)", &percent) == 1)
267     return percent / 100.0;
268 
269   return -1.0;
270 }
271 
272 static gchar **
ogmrip_mencoder_extract_command(OGMRipVideoCodec * video,const gchar * output)273 ogmrip_mencoder_extract_command (OGMRipVideoCodec *video, const gchar *output)
274 {
275   GPtrArray *argv;
276   const gchar *filename;
277 
278   argv = g_ptr_array_new ();
279   g_ptr_array_add (argv, g_strdup ("mencoder"));
280   g_ptr_array_add (argv, g_strdup ("-nocache"));
281   g_ptr_array_add (argv, g_strdup ("-noskip"));
282 
283   if (MPLAYER_CHECK_VERSION (1,0,3,0))
284   {
285     g_ptr_array_add (argv, g_strdup ("-noconfig"));
286     g_ptr_array_add (argv, g_strdup ("all"));
287   }
288 
289   g_ptr_array_add (argv, g_strdup ("-mc"));
290   g_ptr_array_add (argv, g_strdup ("0"));
291 
292   g_ptr_array_add (argv, g_strdup ("-nosound"));
293 
294   if (ogmrip_check_mplayer_nosub ())
295     g_ptr_array_add (argv, g_strdup ("-nosub"));
296 
297   g_ptr_array_add (argv, g_strdup ("-ovc"));
298   g_ptr_array_add (argv, g_strdup ("copy"));
299 
300   g_ptr_array_add (argv, g_strdup ("-of"));
301   g_ptr_array_add (argv, g_strdup ("lavf"));
302   g_ptr_array_add (argv, g_strdup ("-lavfopts"));
303   g_ptr_array_add (argv, g_strdup ("format=mp4"));
304 
305   g_ptr_array_add (argv, g_strdup ("-o"));
306   g_ptr_array_add (argv, g_strdup (output));
307 
308   filename = ogmrip_codec_get_output (OGMRIP_CODEC (video));
309   g_ptr_array_add (argv, g_strdup (filename));
310 
311   g_ptr_array_add (argv, NULL);
312 
313   return (gchar **) g_ptr_array_free (argv, FALSE);
314 }
315 
316 static gint
ogmrip_mp4_get_n_audio_files(OGMRipContainer * mp4)317 ogmrip_mp4_get_n_audio_files (OGMRipContainer *mp4)
318 {
319   GSList *files, *file;
320   guint n_audio = 0;
321 
322   files = ogmrip_container_get_files (mp4);
323   for (file = files; file; file = file->next)
324   {
325     if (ogmrip_file_get_type (OGMRIP_FILE (file->data)) == OGMRIP_FILE_TYPE_AUDIO)
326       n_audio ++;
327   }
328 
329   g_slist_free (files);
330 
331   return n_audio;
332 }
333 
334 static gint
ogmrip_mp4_get_n_subp_files(OGMRipContainer * mp4)335 ogmrip_mp4_get_n_subp_files (OGMRipContainer *mp4)
336 {
337   GSList *files, *file;
338   guint n_subp = 0;
339 
340   files = ogmrip_container_get_files (mp4);
341   for (file = files; file; file = file->next)
342   {
343     if (ogmrip_file_get_type (OGMRIP_FILE (file->data)) == OGMRIP_FILE_TYPE_SUBP)
344       n_subp ++;
345   }
346 
347   g_slist_free (files);
348 
349   return n_subp;
350 }
351 
352 static gchar **
ogmrip_mp4_create_command(OGMRipContainer * mp4,const gchar * input,const gchar * output)353 ogmrip_mp4_create_command (OGMRipContainer *mp4, const gchar *input, const gchar *output)
354 {
355   GPtrArray *argv;
356   OGMRipVideoCodec *video;
357   const gchar *label, *fmt = NULL;
358   gchar fps[8];
359 
360   if ((video = ogmrip_container_get_video (mp4)))
361   {
362     gint format;
363 
364     format = ogmrip_plugin_get_video_codec_format (G_TYPE_FROM_INSTANCE (video));
365     switch (format)
366     {
367       case OGMRIP_FORMAT_MPEG4:
368         fmt = "mpeg4-video";
369         break;
370       case OGMRIP_FORMAT_MPEG2:
371         fmt = "mpeg2-video";
372         break;
373       case OGMRIP_FORMAT_H264:
374         fmt = "h264";
375         break;
376       case OGMRIP_FORMAT_THEORA:
377         fmt = "ogg";
378         break;
379       default:
380         fmt = NULL;
381         break;
382     }
383 
384     if (!fmt)
385       return NULL;
386   }
387 
388   argv = g_ptr_array_new ();
389   g_ptr_array_add (argv, g_strdup (PROGRAM));
390 
391   if (ogmrip_container_get_n_audio (mp4) + ogmrip_mp4_get_n_audio_files (mp4) <= 1 &&
392       ogmrip_container_get_n_subp (mp4) + ogmrip_mp4_get_n_subp_files (mp4) < 1)
393     g_ptr_array_add (argv, g_strdup ("-isma"));
394 
395   g_ptr_array_add (argv, g_strdup ("-nodrop"));
396   g_ptr_array_add (argv, g_strdup ("-new"));
397 
398   g_ptr_array_add (argv, g_strdup ("-brand"));
399   g_ptr_array_add (argv, g_strdup ("mp42"));
400 
401   g_ptr_array_add (argv, g_strdup ("-tmp"));
402   g_ptr_array_add (argv, g_strdup (ogmrip_fs_get_tmp_dir ()));
403 
404   label = ogmrip_container_get_label (mp4);
405   if (label)
406   {
407     g_ptr_array_add (argv, g_strdup ("-itags"));
408     g_ptr_array_add (argv, g_strdup_printf ("name=%s", label));
409   }
410 
411   if (fmt)
412   {
413     if (!input)
414       input = ogmrip_codec_get_output (OGMRIP_CODEC (video));
415 
416     g_ascii_formatd (fps, 8, "%.3f",
417         ogmrip_mp4_get_output_fps (OGMRIP_CODEC (video)));
418 
419     g_ptr_array_add (argv, g_strdup ("-add"));
420     g_ptr_array_add (argv, g_strdup_printf ("%s:fmt=%s:fps=%s#video", input, fmt, fps));
421   }
422 
423   ogmrip_container_foreach_audio (mp4,
424       (OGMRipContainerCodecFunc) ogmrip_mp4_foreach_audio, argv);
425   ogmrip_container_foreach_subp (mp4,
426       (OGMRipContainerCodecFunc) ogmrip_mp4_foreach_subp, argv);
427   ogmrip_container_foreach_chapters (mp4,
428       (OGMRipContainerCodecFunc) ogmrip_mp4_foreach_chapters, argv);
429   ogmrip_container_foreach_file (mp4,
430       (OGMRipContainerFileFunc) ogmrip_mp4_foreach_file, argv);
431 
432   g_ptr_array_add (argv, g_strdup (output));
433 
434   g_ptr_array_add (argv, NULL);
435 
436   return (gchar **) g_ptr_array_free (argv, FALSE);
437 }
438 
439 static gdouble
ogmrip_mp4_create_watch(OGMJobExec * exec,const gchar * buffer,OGMRipMp4 * mp4)440 ogmrip_mp4_create_watch (OGMJobExec *exec, const gchar *buffer, OGMRipMp4 *mp4)
441 {
442   guint percent;
443   gchar *sep;
444 
445   if ((sep = strrchr (buffer, '(')) && sscanf (sep, "(%u/100)", &percent) == 1)
446   {
447     if (percent < mp4->old_percent)
448       mp4->streams ++;
449 
450     mp4->old_percent = percent;
451 
452     return mp4->streams / (gdouble) mp4->nstreams + percent / (mp4->nstreams * 100.0);
453   }
454 
455   return -1.0;
456 }
457 
458 static gchar **
ogmrip_mp4_split_command(OGMRipContainer * mp4,const gchar * input)459 ogmrip_mp4_split_command (OGMRipContainer *mp4, const gchar *input)
460 {
461   GPtrArray *argv;
462   guint tsize;
463 
464   argv = g_ptr_array_new ();
465   g_ptr_array_add (argv, g_strdup (PROGRAM));
466 
467   g_ptr_array_add (argv, g_strdup ("-tmp"));
468   g_ptr_array_add (argv, g_strdup (ogmrip_fs_get_tmp_dir ()));
469 
470   ogmrip_container_get_split (OGMRIP_CONTAINER (mp4), NULL, &tsize);
471   g_ptr_array_add (argv, g_strdup ("-splits"));
472   g_ptr_array_add (argv, g_strdup_printf ("%d", tsize));
473 
474   g_ptr_array_add (argv, g_strdup (input));
475 
476   g_ptr_array_add (argv, NULL);
477 
478   return (gchar **) g_ptr_array_free (argv, FALSE);
479 }
480 
481 static gdouble
ogmrip_mp4_split_watch(OGMJobExec * exec,const gchar * buffer,OGMRipMp4 * mp4)482 ogmrip_mp4_split_watch (OGMJobExec *exec, const gchar *buffer, OGMRipMp4 *mp4)
483 {
484   gchar *sep;
485   guint percent;
486 
487   if ((sep = strrchr (buffer, '(')) && sscanf (sep, "(%u/100)", &percent) == 1)
488   {
489     if (g_str_has_prefix (buffer, "Splitting:"))
490     {
491       mp4->split_percent = percent;
492 
493       return (percent + 100 * mp4->splits) / (100.0 * (mp4->nsplits + 1));
494     }
495     else if (g_str_has_prefix (buffer, "ISO File Writing:"))
496     {
497       if (percent < mp4->split_percent)
498         mp4->splits ++;
499 
500       return (percent + mp4->split_percent + 100 * mp4->splits) / (100.0 * (mp4->nsplits + 1));
501     }
502   }
503 
504   return -1.0;
505 }
506 
G_DEFINE_TYPE(OGMRipMp4,ogmrip_mp4,OGMRIP_TYPE_CONTAINER)507 G_DEFINE_TYPE (OGMRipMp4, ogmrip_mp4, OGMRIP_TYPE_CONTAINER)
508 
509 static void
510 ogmrip_mp4_class_init (OGMRipMp4Class *klass)
511 {
512   OGMJobSpawnClass *spawn_class;
513 
514   spawn_class = OGMJOB_SPAWN_CLASS (klass);
515   spawn_class->run = ogmrip_mp4_run;
516 }
517 
518 static void
ogmrip_mp4_init(OGMRipMp4 * mp4)519 ogmrip_mp4_init (OGMRipMp4 *mp4)
520 {
521 }
522 
523 static gchar *
ogmrip_mp4_get_h264_filename(OGMRipVideoCodec * video)524 ogmrip_mp4_get_h264_filename (OGMRipVideoCodec *video)
525 {
526   const gchar *name;
527   gchar *dot, *filename;
528 
529   name = ogmrip_codec_get_output (OGMRIP_CODEC (video));
530   dot = strrchr (name, '.');
531 
532   filename = g_new0 (gchar, dot - name + 12);
533   strncpy (filename, name, dot - name);
534   strcat (filename, "_video.h264");
535 
536   return filename;
537 }
538 
539 static void
ogmrip_mp4_get_n_vobsub(OGMRipContainer * container,OGMRipCodec * codec,guint demuxer,gint language,gint * nvobsub)540 ogmrip_mp4_get_n_vobsub (OGMRipContainer *container, OGMRipCodec *codec, guint demuxer, gint language, gint *nvobsub)
541 {
542   if (ogmrip_plugin_get_subp_codec_format (G_TYPE_FROM_INSTANCE (codec)) == OGMRIP_FORMAT_VOBSUB)
543     (*nvobsub) ++;
544 }
545 
546 static gint
ogmrip_mp4_run(OGMJobSpawn * spawn)547 ogmrip_mp4_run (OGMJobSpawn *spawn)
548 {
549   OGMJobSpawn *queue, *child;
550   OGMRipVideoCodec *video;
551   OGMRipMp4 *mp4;
552 
553   gchar **argv, *filename = NULL;
554   const gchar *output;
555 
556   gint result = OGMJOB_RESULT_ERROR;
557 
558   g_return_val_if_fail (OGMRIP_IS_MP4 (spawn), OGMJOB_RESULT_ERROR);
559 
560   mp4 = OGMRIP_MP4 (spawn);
561 
562   output = ogmrip_container_get_output (OGMRIP_CONTAINER (spawn));
563   ogmrip_container_get_split (OGMRIP_CONTAINER (spawn), &mp4->nsplits, NULL);
564 
565   queue = ogmjob_queue_new ();
566   ogmjob_container_add (OGMJOB_CONTAINER (spawn), queue);
567   g_object_unref (queue);
568 
569   video = ogmrip_container_get_video (OGMRIP_CONTAINER (spawn));
570   if (ogmrip_plugin_get_video_codec_format (G_TYPE_FROM_INSTANCE (video)) == OGMRIP_FORMAT_H264)
571   {
572     gboolean global_header = FALSE;
573 
574     if (g_object_class_find_property (G_OBJECT_GET_CLASS (video), "global_header"))
575       g_object_get (video, "global_header", &global_header, NULL);
576 
577     if (global_header)
578     {
579       filename = ogmrip_fs_mktemp ("video.XXXXXX", NULL);
580 
581       argv = ogmrip_mencoder_extract_command (video, filename);
582       if (!argv)
583       {
584         g_free (filename);
585         return OGMJOB_RESULT_ERROR;
586       }
587 
588       child = ogmjob_exec_newv (argv);
589       ogmjob_exec_add_watch_full (OGMJOB_EXEC (child), (OGMJobWatch) ogmrip_mencoder_container_watch, spawn, TRUE, FALSE, FALSE);
590     }
591     else
592     {
593       argv = ogmrip_mp4box_extract_command (video);
594       if (!argv)
595         return OGMJOB_RESULT_ERROR;
596 
597       child = ogmjob_exec_newv (argv);
598       ogmjob_exec_add_watch_full (OGMJOB_EXEC (child), (OGMJobWatch) ogmrip_mp4box_extract_watch, spawn, TRUE, FALSE, FALSE);
599 
600       filename = ogmrip_mp4_get_h264_filename (video);
601     }
602 
603     ogmjob_container_add (OGMJOB_CONTAINER (queue), child);
604     g_object_unref (child);
605   }
606 
607   argv = ogmrip_mp4_create_command (OGMRIP_CONTAINER (spawn), filename, output);
608   if (argv)
609   {
610     gint nvobsub = 0;
611 
612     ogmrip_container_foreach_subp (OGMRIP_CONTAINER (spawn), (OGMRipContainerCodecFunc) ogmrip_mp4_get_n_vobsub, &nvobsub);
613 
614     mp4->old_percent = 0;
615     mp4->nstreams = 2 + ogmrip_container_get_n_audio (OGMRIP_CONTAINER (spawn)) + nvobsub;
616     mp4->streams = 0;
617 
618     child = ogmjob_exec_newv (argv);
619     ogmjob_exec_add_watch_full (OGMJOB_EXEC (child), (OGMJobWatch) ogmrip_mp4_create_watch, spawn, TRUE, FALSE, FALSE);
620     ogmjob_container_add (OGMJOB_CONTAINER (queue), child);
621     g_object_unref (child);
622 
623     if (mp4->nsplits > 1 && result == OGMJOB_RESULT_SUCCESS)
624     {
625       argv = ogmrip_mp4_split_command (OGMRIP_CONTAINER (spawn), output);
626       if (argv)
627       {
628         mp4->split_percent = 0;
629         mp4->splits = 0;
630 
631         child = ogmjob_exec_newv (argv);
632         ogmjob_exec_add_watch_full (OGMJOB_EXEC (child), (OGMJobWatch) ogmrip_mp4_split_watch, spawn, TRUE, FALSE, FALSE);
633         ogmjob_container_add (OGMJOB_CONTAINER (queue), child);
634         g_object_unref (child);
635       }
636     }
637 
638     result = OGMJOB_SPAWN_CLASS (ogmrip_mp4_parent_class)->run (spawn);
639   }
640 
641   ogmjob_container_remove (OGMJOB_CONTAINER (spawn), queue);
642 
643   if (filename)
644     ogmrip_fs_unref (filename, TRUE);
645 
646   if (mp4->nsplits > 1)
647     ogmrip_fs_unref (g_strdup (output), TRUE);
648 
649   return result;
650 }
651 
652 static OGMRipContainerPlugin mp4_plugin =
653 {
654   NULL,
655   G_TYPE_NONE,
656   "mp4",
657   N_("Mpeg-4 Media (MP4)"),
658   FALSE,
659   TRUE,
660   G_MAXINT,
661   G_MAXINT,
662   NULL
663 };
664 
665 static gint formats[] =
666 {
667   OGMRIP_FORMAT_MPEG4,
668   OGMRIP_FORMAT_MPEG2,
669   OGMRIP_FORMAT_H264,
670   OGMRIP_FORMAT_THEORA,
671   OGMRIP_FORMAT_AAC,
672   OGMRIP_FORMAT_MP3,
673   OGMRIP_FORMAT_VORBIS,
674   OGMRIP_FORMAT_SRT,
675   OGMRIP_FORMAT_VOBSUB,
676   -1,
677   -1,
678   -1
679 };
680 
681 OGMRipContainerPlugin *
ogmrip_init_plugin(GError ** error)682 ogmrip_init_plugin (GError **error)
683 {
684   gchar *output;
685   gint major_version = 0, minor_version = 0, micro_version = 0;
686 
687   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
688 
689   if (!g_spawn_command_line_sync (PROGRAM " -version", &output, NULL, NULL, NULL))
690   {
691     g_set_error (error, OGMRIP_PLUGIN_ERROR, OGMRIP_PLUGIN_ERROR_REQ, _("MP4Box is missing"));
692     return NULL;
693   }
694 
695   if (g_str_has_prefix (output, "MP4Box - GPAC version "))
696   {
697     gchar *end;
698 
699     errno = 0;
700     major_version = strtoul (output + 22, &end, 10);
701     if (!errno && *end == '.')
702       minor_version = strtoul (end + 1, NULL, 10);
703     if (!errno && *end == '.')
704       micro_version = strtoul (end + 1, NULL, 10);
705   }
706   g_free (output);
707 
708   if ((major_version > 0) ||
709       (major_version == 0 && minor_version > 4) ||
710       (major_version == 0 && minor_version == 4 && micro_version >= 5))
711   {
712     guint i = 0;
713 
714     while (formats[i] != -1)
715       i++;
716 
717     formats[i] = OGMRIP_FORMAT_AC3;
718     formats[i+1] = OGMRIP_FORMAT_COPY;
719   }
720 
721   mp4_plugin.type = OGMRIP_TYPE_MP4;
722   mp4_plugin.formats = formats;
723 
724   return &mp4_plugin;
725 }
726 
727