r860: Merge 2.1:
[cinelerra_cv.git] / libmpeg3 / mpeg3cat.c
blob331613b0b397621ebfed2b870fab240b82ac06de
1 /* Concatenate elementary streams */
2 /* Mpeg3cat is useful for extracting elementary streams from program streams. */
5 #include "libmpeg3.h"
6 #include "mpeg3protos.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.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];
19 char *inpaths[argc];
20 int total_infiles = 0;
21 mpeg3_t *in;
22 FILE *out;
23 int out_counter = 0;
24 int current_file, current_output_file = 0, i;
25 unsigned int bits;
26 unsigned char *buffer;
27 long output_size;
28 int result = 0;
29 long total_frames = 0;
30 int do_audio = 0, do_video = 0;
31 int stream = 0;
32 int64_t total_written = 0;
34 if(argc < 2)
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");
40 exit(1);
43 outpath[0] = 0;
44 for(i = 1; i < argc; i++)
46 if(argv[i][0] == '-')
48 if(argv[i][1] != 'a' && argv[i][1] != 'v' && argv[i][1] != 'o')
50 fprintf(stderr, "invalid option %s\n", argv[i]);
51 exit(1);
53 else
54 if(argv[i][1] == 'o')
56 // Check for filename
57 if(i < argc - 1)
59 strcpy(outpath, argv[++i]);
61 else
63 fprintf(stderr, "-o requires an output file\n");
64 exit(1);
67 // Check if file exists
68 if(out = fopen(outpath, "r"))
70 fprintf(stderr, "%s exists.\n", outpath);
71 exit(1);
75 else
77 if(argv[i][1] == 'a') do_audio = 1;
78 else
79 if(argv[i][1] == 'v') do_video = 1;
81 if(argv[i][2] != 0)
83 stream = argv[i][2] - '0';
87 else
89 inpaths[total_infiles++] = argv[i];
93 buffer = malloc(BUFFER_SIZE);
94 if(outpath[0])
96 if(!(out = fopen(outpath, "wb")))
98 fprintf(stderr, "Failed to open %s for writing\n", outpath);
99 exit(1);
102 else
103 out = stdout;
105 for(current_file = 0; current_file < total_infiles; current_file++)
107 strcpy(inpath, inpaths[current_file]);
109 int error = 0;
110 if(!(in = mpeg3_open(inpath, &error)))
112 fprintf(stderr, "Skipping %s\n", inpath);
113 continue;
120 /* output elementary audio stream */
121 if((mpeg3_has_audio(in) && in->is_audio_stream) ||
122 (do_audio && !in->is_audio_stream && !in->is_video_stream))
124 do_audio = 1;
125 /* Add audio stream to end */
126 if(stream >= in->total_astreams)
128 fprintf(stderr, "No audio stream %d\n", stream);
129 exit(1);
132 mpeg3demux_seek_byte(in->atrack[stream]->demuxer, 0);
133 // mpeg3bits_refill(in->atrack[stream]->audio->astream);
134 //printf("mpeg3cat 1\n");
135 while(!mpeg3_read_audio_chunk(in,
136 buffer,
137 &output_size,
138 BUFFER_SIZE,
139 stream))
141 //printf("mpeg3cat 2 0x%x\n", output_size);
142 result = !fwrite(buffer, output_size, 1, out);
143 if(result)
145 perror("fwrite audio chunk");
146 break;
149 //printf("mpeg3cat 3\n");
151 else
152 /* Output elementary video stream */
153 if((mpeg3_has_video(in) && in->is_video_stream) ||
154 (do_video && !in->is_video_stream && !in->is_audio_stream))
156 /* Add video stream to end */
157 int hour, minute, second, frame;
158 long gop_frame;
159 unsigned long code;
160 float carry;
161 int i, offset;
163 if(stream >= in->total_vstreams)
165 fprintf(stderr, "No audio stream %d\n", stream);
166 exit(1);
169 mpeg3demux_seek_byte(in->vtrack[stream]->demuxer, 0);
170 mpeg3bits_refill(in->vtrack[stream]->video->vstream);
171 do_video = 1;
172 while(!mpeg3_read_video_chunk(in,
173 buffer,
174 &output_size,
175 BUFFER_SIZE,
176 stream) &&
177 output_size >= 4)
179 code = (unsigned long)buffer[output_size - 4] << 24;
180 code |= (unsigned long)buffer[output_size - 3] << 16;
181 code |= (unsigned long)buffer[output_size - 2] << 8;
182 code |= (unsigned long)buffer[output_size - 1];
184 /* Got a frame at the end of this buffer. */
185 if(code == MPEG3_PICTURE_START_CODE)
187 total_frames++;
189 else
190 if(code == MPEG3_SEQUENCE_END_CODE)
192 /* Got a sequence end code at the end of this buffer. */
193 output_size -= 4;
196 code = (unsigned long)buffer[0] << 24;
197 code |= (unsigned long)buffer[1] << 16;
198 code |= (unsigned long)buffer[2] << 8;
199 code |= buffer[3];
201 i = 0;
202 offset = 0;
203 if(code == MPEG3_SEQUENCE_START_CODE && current_output_file > 0)
205 /* Skip the sequence start code */
206 i += 4;
207 while(i < output_size &&
208 code != MPEG3_GOP_START_CODE)
210 code <<= 8;
211 code |= buffer[i++];
213 i -= 4;
214 offset = i;
217 /* Search for GOP header to fix */
218 code = (unsigned long)buffer[i++] << 24;
219 code |= (unsigned long)buffer[i++] << 16;
220 code |= (unsigned long)buffer[i++] << 8;
221 code |= buffer[i++];
222 while(i < output_size &&
223 code != MPEG3_GOP_START_CODE)
225 code <<= 8;
226 code |= buffer[i++];
229 if(code == MPEG3_GOP_START_CODE)
231 /* Get the time code */
232 code = (unsigned long)buffer[i] << 24;
233 code |= (unsigned long)buffer[i + 1] << 16;
234 code |= (unsigned long)buffer[i + 2] << 8;
235 code |= (unsigned long)buffer[i + 3];
237 hour = code >> 26 & 0x1f;
238 minute = code >> 20 & 0x3f;
239 second = code >> 13 & 0x3f;
240 frame = code >> 7 & 0x3f;
242 gop_frame = (long)(hour * 3600 * mpeg3_frame_rate(in, stream) +
243 minute * 60 * mpeg3_frame_rate(in, stream) +
244 second * mpeg3_frame_rate(in, stream) +
245 frame);
246 /* fprintf(stderr, "old: %02d:%02d:%02d:%02d ", hour, minute, second, frame); */
247 /* Write a new time code */
248 hour = (long)((float)(total_frames - 1) / mpeg3_frame_rate(in, stream) / 3600);
249 carry = hour * 3600 * mpeg3_frame_rate(in, stream);
250 minute = (long)((float)(total_frames - 1 - carry) / mpeg3_frame_rate(in, stream) / 60);
251 carry += minute * 60 * mpeg3_frame_rate(in, stream);
252 second = (long)((float)(total_frames - 1 - carry) / mpeg3_frame_rate(in, stream));
253 carry += second * mpeg3_frame_rate(in, stream);
254 frame = (total_frames - 1 - carry);
256 buffer[i] = ((code >> 24) & 0x80) | (hour << 2) | (minute >> 4);
257 buffer[i + 1] = ((code >> 16) & 0x08) | ((minute & 0xf) << 4) | (second >> 3);
258 buffer[i + 2] = ((second & 0x7) << 5) | (frame >> 1);
259 buffer[i + 3] = (code & 0x7f) | ((frame & 0x1) << 7);
260 /* fprintf(stderr, "new: %02d:%02d:%02d:%02d\n", hour, minute, second, frame); */
264 /* Test 32 bit overflow */
265 if(outpath[0])
267 if(ftell(out) > 0x7f000000)
269 fclose(out);
270 out_counter++;
271 sprintf(newpath, "%s%03d", outpath, out_counter);
272 if(!(out = fopen(newpath, "wb")))
274 fprintf(stderr, "Couldn't open %s for writing.\n", newpath);
275 exit(1);
280 * fprintf(stderr, "mpeg3cat 5 %02x %02x %02x %02x\n",
281 * (buffer + offset)[0],
282 * (buffer + offset)[1],
283 * (buffer + offset)[2],
284 * (buffer + offset)[3]);
287 /* Write the frame */
288 result = !fwrite(buffer + offset, output_size - offset, 1, out);
289 if(result)
291 perror("fwrite video chunk");
292 break;
296 else
297 /* Output program stream */
298 if(in->is_program_stream)
300 mpeg3_demuxer_t *demuxer = in->vtrack[0]->demuxer;
301 result = 0;
303 /* Append program stream with no changes */
304 demuxer->read_all = 1;
305 mpeg3demux_seek_byte(demuxer, 0);
306 // mpeg3demux_seek_byte(demuxer, 83886080);
309 while(!result)
311 result = mpeg3_seek_phys(demuxer);
314 if(!result)
316 demuxer->data_size = 0;
317 demuxer->video_size = 0;
318 demuxer->audio_size = 0;
319 result = mpeg3demux_read_program(demuxer);
320 if(result)
321 fprintf(stderr, "Hit end of data in %s\n", inpath);
325 // Read again and decrypt it
326 unsigned char *raw_data = malloc(0x10000);
327 int raw_size = 0;
328 if(!result)
330 mpeg3_title_t *title = demuxer->titles[demuxer->current_title];
331 int64_t temp_offset = mpeg3io_tell(title->fs);
332 int64_t decryption_offset = demuxer->last_packet_decryption - demuxer->last_packet_start;
333 raw_size = demuxer->last_packet_end - demuxer->last_packet_start;
335 mpeg3io_seek(title->fs, demuxer->last_packet_start);
336 mpeg3io_read_data(raw_data, raw_size, title->fs);
337 mpeg3io_seek(title->fs, temp_offset);
340 if(decryption_offset > 0 &&
341 decryption_offset < raw_size &&
342 raw_data[decryption_offset] & 0x30)
344 if(mpeg3_decrypt_packet(title->fs->css,
345 raw_data,
348 fprintf(stderr, "get_ps_pes_packet: Decryption not available\n");
349 return 1;
351 raw_data[decryption_offset] &= 0xcf;
355 // Write it
356 if(!result)
358 result = !fwrite(raw_data,
359 raw_size,
361 out);
362 total_written += raw_size;
363 if(result) fprintf(stderr, "%s\n", strerror(errno));
366 free(raw_data);
369 else
370 /* No transport stream support, yet */
372 fprintf(stderr, "Unsupported stream type.\n");
373 mpeg3_close(in);
374 in = 0;
375 continue;
378 mpeg3_close(in);
379 in = 0;
380 current_output_file++;
383 /* Terminate output */
384 if(current_output_file > 0 && do_video)
386 /*fprintf(stderr, "\n"); */
387 /* Write new end of sequence */
388 buffer[0] = MPEG3_SEQUENCE_END_CODE >> 24;
389 buffer[1] = (MPEG3_SEQUENCE_END_CODE >> 16) & 0xff;
390 buffer[2] = (MPEG3_SEQUENCE_END_CODE >> 8) & 0xff;
391 buffer[3] = MPEG3_SEQUENCE_END_CODE & 0xff;
392 result = !fwrite(buffer, 4, 1, out);
394 if(outpath[0]) fclose(out);
396 exit(0);