1 /* Concatenate elementary streams */
2 /* Mpeg3cat is useful for extracting elementary streams from program streams. */
6 #include "mpeg3protos.h"
13 #define MPEG3_SEQUENCE_START_CODE 0x000001b3
14 #define BUFFER_SIZE 1000000
16 int main(int argc
, char *argv
[])
18 char inpath
[1024], outpath
[1024], newpath
[1024];
20 int total_infiles
= 0;
24 int current_file
, current_output_file
= 0, i
;
26 unsigned char *buffer
;
29 long total_frames
= 0;
30 int do_audio
= 0, do_video
= 0;
32 int64_t total_written
= 0;
36 fprintf(stderr
, "Concatenate elementary streams or demultiplex a program stream.\n"
37 "Usage: mpeg3cat -[av0123456789] <infile> [infile...] > <outfile>\n\n"
38 "Example: Concatenate 2 video files: mpeg3cat xena1.m2v xena2.m2v > xena.m2v\n"
39 " Extract audio stream 0: mpeg3cat -a0 xena.vob > war_cry.ac3\n");
44 for(i
= 1; i
< argc
; i
++)
48 if(argv
[i
][1] != 'a' && argv
[i
][1] != 'v' && argv
[i
][1] != 'o')
50 fprintf(stderr
, "invalid option %s\n", argv
[i
]);
59 strcpy(outpath
, argv
[++i
]);
63 fprintf(stderr
, "-o requires an output file\n");
67 // Check if file exists
68 if(out
= fopen(outpath
, "r"))
70 fprintf(stderr
, "%s exists.\n", outpath
);
77 if(argv
[i
][1] == 'a') do_audio
= 1;
79 if(argv
[i
][1] == 'v') do_video
= 1;
83 stream
= argv
[i
][2] - 48;
89 inpaths
[total_infiles
++] = argv
[i
];
93 buffer
= malloc(BUFFER_SIZE
);
96 if(!(out
= fopen(outpath
, "wb")))
98 fprintf(stderr
, "Failed to open %s for writing\n", outpath
);
105 for(current_file
= 0; current_file
< total_infiles
; current_file
++)
107 strcpy(inpath
, inpaths
[current_file
]);
109 if(!(in
= mpeg3_open(inpath
)))
111 fprintf(stderr
, "Skipping %s\n", inpath
);
119 //fprintf(stderr, "%d %d %d %d\n", in->is_transport_stream, in->is_program_stream, in->is_audio_stream, in->is_video_stream);
120 if((mpeg3_has_audio(in
) && in
->is_audio_stream
) ||
121 (do_audio
&& !in
->is_audio_stream
&& !in
->is_video_stream
))
124 /* Add audio stream to end */
125 mpeg3demux_seek_byte(in
->atrack
[stream
]->demuxer
, 0);
126 // mpeg3bits_refill(in->atrack[stream]->audio->astream);
127 //printf("mpeg3cat 1\n");
128 while(!mpeg3_read_audio_chunk(in
,
134 //printf("mpeg3cat 2\n");
135 result
= !fwrite(buffer
, output_size
, 1, out
);
138 perror("fwrite audio chunk");
142 //printf("mpeg3cat 3\n");
145 if((mpeg3_has_video(in
) && in
->is_video_stream
) ||
146 (do_video
&& !in
->is_video_stream
&& !in
->is_audio_stream
))
148 /* Add video stream to end */
149 int hour
, minute
, second
, frame
;
155 mpeg3demux_seek_byte(in
->vtrack
[stream
]->demuxer
, 0);
156 mpeg3bits_refill(in
->vtrack
[stream
]->video
->vstream
);
158 while(!mpeg3_read_video_chunk(in
,
165 code
= (unsigned long)buffer
[output_size
- 4] << 24;
166 code
|= (unsigned long)buffer
[output_size
- 3] << 16;
167 code
|= (unsigned long)buffer
[output_size
- 2] << 8;
168 code
|= (unsigned long)buffer
[output_size
- 1];
170 /* Got a frame at the end of this buffer. */
171 if(code
== MPEG3_PICTURE_START_CODE
)
176 if(code
== MPEG3_SEQUENCE_END_CODE
)
178 /* Got a sequence end code at the end of this buffer. */
182 code
= (unsigned long)buffer
[0] << 24;
183 code
|= (unsigned long)buffer
[1] << 16;
184 code
|= (unsigned long)buffer
[2] << 8;
189 if(code
== MPEG3_SEQUENCE_START_CODE
&& current_output_file
> 0)
191 /* Skip the sequence start code */
193 while(i
< output_size
&&
194 code
!= MPEG3_GOP_START_CODE
)
203 /* Search for GOP header to fix */
204 code
= (unsigned long)buffer
[i
++] << 24;
205 code
|= (unsigned long)buffer
[i
++] << 16;
206 code
|= (unsigned long)buffer
[i
++] << 8;
208 while(i
< output_size
&&
209 code
!= MPEG3_GOP_START_CODE
)
215 if(code
== MPEG3_GOP_START_CODE
)
217 /* Get the time code */
218 code
= (unsigned long)buffer
[i
] << 24;
219 code
|= (unsigned long)buffer
[i
+ 1] << 16;
220 code
|= (unsigned long)buffer
[i
+ 2] << 8;
221 code
|= (unsigned long)buffer
[i
+ 3];
223 hour
= code
>> 26 & 0x1f;
224 minute
= code
>> 20 & 0x3f;
225 second
= code
>> 13 & 0x3f;
226 frame
= code
>> 7 & 0x3f;
228 gop_frame
= (long)(hour
* 3600 * mpeg3_frame_rate(in
, stream
) +
229 minute
* 60 * mpeg3_frame_rate(in
, stream
) +
230 second
* mpeg3_frame_rate(in
, stream
) +
232 /* fprintf(stderr, "old: %02d:%02d:%02d:%02d ", hour, minute, second, frame); */
233 /* Write a new time code */
234 hour
= (long)((float)(total_frames
- 1) / mpeg3_frame_rate(in
, stream
) / 3600);
235 carry
= hour
* 3600 * mpeg3_frame_rate(in
, stream
);
236 minute
= (long)((float)(total_frames
- 1 - carry
) / mpeg3_frame_rate(in
, stream
) / 60);
237 carry
+= minute
* 60 * mpeg3_frame_rate(in
, stream
);
238 second
= (long)((float)(total_frames
- 1 - carry
) / mpeg3_frame_rate(in
, stream
));
239 carry
+= second
* mpeg3_frame_rate(in
, stream
);
240 frame
= (total_frames
- 1 - carry
);
242 buffer
[i
] = ((code
>> 24) & 0x80) | (hour
<< 2) | (minute
>> 4);
243 buffer
[i
+ 1] = ((code
>> 16) & 0x08) | ((minute
& 0xf) << 4) | (second
>> 3);
244 buffer
[i
+ 2] = ((second
& 0x7) << 5) | (frame
>> 1);
245 buffer
[i
+ 3] = (code
& 0x7f) | ((frame
& 0x1) << 7);
246 /* fprintf(stderr, "new: %02d:%02d:%02d:%02d\n", hour, minute, second, frame); */
250 /* Test 32 bit overflow */
253 if(ftell(out
) > 0x7f000000)
257 sprintf(newpath
, "%s%03d", outpath
, out_counter
);
258 if(!(out
= fopen(newpath
, "wb")))
260 fprintf(stderr
, "Couldn't open %s for writing.\n", newpath
);
266 * fprintf(stderr, "mpeg3cat 5 %02x %02x %02x %02x\n",
267 * (buffer + offset)[0],
268 * (buffer + offset)[1],
269 * (buffer + offset)[2],
270 * (buffer + offset)[3]);
273 /* Write the frame */
274 result
= !fwrite(buffer
+ offset
, output_size
- offset
, 1, out
);
277 perror("fwrite video chunk");
283 if(in
->is_program_stream
)
285 mpeg3_demuxer_t
*demuxer
= in
->vtrack
[0]->demuxer
;
288 /* Append program stream with no changes */
289 demuxer
->read_all
= 1;
290 mpeg3demux_seek_byte(demuxer
, 0);
295 result
= mpeg3_seek_phys(demuxer
);
300 demuxer
->data_size
= 0;
301 demuxer
->video_size
= 0;
302 demuxer
->audio_size
= 0;
303 result
= mpeg3demux_read_program(demuxer
);
305 fprintf(stderr
, "Hit end of data in %s\n", inpath
);
309 // Read again and decrypt it
310 unsigned char *raw_data
= malloc(0x10000);
314 mpeg3_title_t
*title
= demuxer
->titles
[demuxer
->current_title
];
315 int64_t temp_offset
= mpeg3io_tell(title
->fs
);
316 int64_t decryption_offset
= demuxer
->last_packet_decryption
- demuxer
->last_packet_start
;
317 raw_size
= demuxer
->last_packet_end
- demuxer
->last_packet_start
;
319 mpeg3io_seek(title
->fs
, demuxer
->last_packet_start
);
320 mpeg3io_read_data(raw_data
, raw_size
, title
->fs
);
321 mpeg3io_seek(title
->fs
, temp_offset
);
324 if(decryption_offset
> 0 &&
325 decryption_offset
< raw_size
&&
326 raw_data
[decryption_offset
] & 0x30)
328 if(mpeg3_decrypt_packet(title
->fs
->css
,
332 fprintf(stderr
, "get_ps_pes_packet: Decryption not available\n");
335 raw_data
[decryption_offset
] &= 0xcf;
342 result
= !fwrite(raw_data
,
346 total_written
+= raw_size
;
347 if(result
) fprintf(stderr
, "%s\n", strerror(errno
));
355 fprintf(stderr
, "Unsupported stream type.\n");
363 current_output_file
++;
366 /* Terminate output */
367 if(current_output_file
> 0 && do_video
)
369 /*fprintf(stderr, "\n"); */
370 /* Write new end of sequence */
371 buffer
[0] = MPEG3_SEQUENCE_END_CODE
>> 24;
372 buffer
[1] = (MPEG3_SEQUENCE_END_CODE
>> 16) & 0xff;
373 buffer
[2] = (MPEG3_SEQUENCE_END_CODE
>> 8) & 0xff;
374 buffer
[3] = MPEG3_SEQUENCE_END_CODE
& 0xff;
375 result
= !fwrite(buffer
, 4, 1, out
);
377 if(outpath
[0]) fclose(out
);