Credit Andrew R for finding the direct copy mods for exr and ppm sequences
[goodguy/cinelerra.git] / cinelerra-5.1 / libzmpeg3 / mpeg3cat.C
1 /* Concatenate elementary streams */
2 /* Mpeg3cat is useful for extracting elementary streams from program streams. */
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include "libzmpeg3.h"
8
9 #define BUFFER_SIZE 0x100000
10
11 unsigned char *output_buffer = 0;
12 int64_t output_buffer_size = 0;
13 int64_t output_scan_offset = 0;
14 FILE *out = 0;
15 int got_start = 0;
16 int do_audio = 0;
17 int do_video = 0;
18
19 // Check for first start code before writing out
20 static int write_output(unsigned char *data, int size, zmpeg3_t *file)
21 {
22 // Already got start code so write data directly
23 // Or user doesn't want to extract an elementary stream
24   if( got_start || (!do_audio && !do_video) || file->is_bd() ) {
25     return fwrite(data, size, 1, out);
26   }
27   else {
28 // Buffer until start code
29     uint32_t zcode = 0xffffffff;
30     int new_allocation = output_buffer_size + size;
31     unsigned char *new_output_buffer = new unsigned char[new_allocation];
32     memcpy(new_output_buffer, output_buffer, output_buffer_size);
33     memcpy(new_output_buffer + output_buffer_size, data, size);
34     if( output_buffer ) delete [] output_buffer;
35     output_buffer = new_output_buffer;
36     output_buffer_size = new_allocation;
37
38     if( output_buffer_size >= 4 ) {
39       if( do_video ) {
40         while( output_scan_offset < output_buffer_size && !got_start ) {
41           zcode = (zcode << 8) | output_buffer[output_scan_offset++];
42           if( zcode == zmpeg3_t::SEQUENCE_START_CODE ) {
43             got_start = 1;
44           }
45         }
46
47         output_scan_offset -= 4;
48       }
49       else {
50 // Only scan for AC3 start code since we can't scan for mp2 start codes.
51 // It must occur in the first 2048 bytes or we give up.
52         while( output_scan_offset < output_buffer_size && 
53                output_scan_offset < 2048 && !got_start ) {
54           zcode = ((zcode & 0xff) << 8) | output_buffer[output_scan_offset++];
55           if( zcode == zmpeg3_t::AC3_START_CODE ) {
56             got_start = 1;
57           }
58         }
59
60         if( got_start )
61           output_scan_offset -= 2;
62         else if( output_scan_offset >= 2048 ) {
63           output_scan_offset = 0;
64           got_start = 1;
65         }
66         else
67           output_scan_offset -= 2;
68       }
69
70       if( got_start ) {
71         return fwrite(output_buffer + output_scan_offset, 
72         output_buffer_size - output_scan_offset, 1, out);
73       }
74     }
75   }
76
77   return 1;
78 }
79
80 int main(int argc, char *argv[])
81 {
82   char inpath[1024], outpath[1024], newpath[1024];
83   char *inpaths[argc];
84   int total_infiles = 0;
85   zmpeg3_t *in;
86   int out_counter = 0;
87   int current_file, current_output_file = 0, i;
88   unsigned char buffer[BUFFER_SIZE];
89   long output_size;
90   int result = 0;
91   int64_t total_frames = 0;
92   int stream = 0;
93   int64_t total_written = 0;
94
95   if( argc < 2 ) {
96     fprintf(stderr, "Concatenate elementary streams or demultiplex a program stream.\n"
97       "Usage: mpeg3cat -[av0123456789] <infile> [infile...] > <outfile>\n\n"
98       "Example: Concatenate 2 video files: mpeg3cat xena1.m2v xena2.m2v > xena.m2v\n"
99       "         Extract audio stream 0: mpeg3cat -a0 xena.vob > war_cry.ac3\n");
100     exit(1);
101   }
102
103   outpath[0] = 0;
104   for( i=1; i < argc; ++i ) {
105     if( argv[i][0] == '-' ) {
106       if( argv[i][1] != 'a' && argv[i][1] != 'v' && argv[i][1] != 'o' ) {
107         fprintf(stderr, "invalid option %s\n", argv[i]);
108         exit(1);
109       }
110       else
111       if( argv[i][1] == 'o' ) {
112 // Check for filename
113         if( i < argc - 1 ) {
114           strcpy(outpath, argv[++i]);
115         }
116         else {
117           fprintf(stderr, "-o requires an output file\n");
118           exit(1);
119         }
120
121 // Check if file exists
122         if( (out = fopen(outpath, "r")) ) {
123           fprintf(stderr, "%s exists.\n", outpath);
124           exit(1);
125         }
126       }
127       else {
128         if( argv[i][1] == 'a' ) do_audio = 1;
129         else if( argv[i][1] == 'v' ) do_video = 1;
130         if( argv[i][2] != 0 ) {
131           stream = argv[i][2] - '0';
132         }
133       }
134     }
135     else {
136       inpaths[total_infiles++] = argv[i];
137     }
138   }
139
140   if( outpath[0] ) {
141     if( !(out = fopen(outpath, "wb")) ) {
142       fprintf(stderr, "Failed to open %s for writing\n", outpath);
143       exit(1);
144     }
145   }
146   else
147     out = stdout;
148
149   for( current_file=0; current_file < total_infiles; ++current_file ) {
150     strcpy(inpath, inpaths[current_file]);
151     int error = 0;
152     if( !(in = mpeg3_open(inpath, &error)) ) {
153       fprintf(stderr, "Skipping %s\n", inpath);
154       continue;
155     }
156
157 /* output elementary audio stream */
158     if( (mpeg3_has_audio(in) && in->is_audio_stream() ) || 
159         (do_audio && !in->is_audio_stream() && !in->is_video_stream())) {
160       do_audio = 1;
161 /* Add audio stream to end */
162       if( stream >= in->total_astreams() ) {
163         fprintf(stderr, "No audio stream %d\n", stream);
164         exit(1);
165       }
166
167       in->atrack[stream]->demuxer->seek_byte(zmpeg3_t::START_BYTE);
168 //      in->atrack[stream]->audio->astream->refill();
169 //printf("mpeg3cat 1\n");
170       while( !mpeg3_read_audio_chunk(in, buffer, 
171                 &output_size, BUFFER_SIZE, stream) ) {
172 //printf("mpeg3cat 2 0x%x\n", output_size);
173         result = !write_output(buffer, output_size, in);
174         if( result ) {
175           perror("write audio chunk");
176           break;
177         }
178       }
179 //printf("mpeg3cat 3\n");
180     }
181 /* Output elementary video stream */
182     else if( (mpeg3_has_video(in) && in->is_video_stream()) ||
183              (do_video && !in->is_video_stream() && !in->is_audio_stream())) {
184 /* Add video stream to end */
185       int64_t hour, minute, second, frame;
186       uint32_t zcode;
187       float carry;
188       int i, offset;
189       
190       if( stream >= in->total_vstreams() ) {
191         fprintf(stderr, "No video stream %d\n", stream);
192         exit(1);
193       }
194
195       in->vtrack[stream]->demuxer->seek_byte(zmpeg3_t::START_BYTE);
196       in->vtrack[stream]->video->vstream->refill();
197       do_video = 1;
198       while( !mpeg3_read_video_chunk(in, buffer, &output_size,
199                  BUFFER_SIZE, stream) && output_size >= 4 ) {
200         zcode = (uint32_t)buffer[output_size - 4] << 24; 
201         zcode |= (uint32_t)buffer[output_size - 3] << 16; 
202         zcode |= (uint32_t)buffer[output_size - 2] << 8; 
203         zcode |= (uint32_t)buffer[output_size - 1]; 
204
205 /* Got a frame at the end of this buffer. */
206         if( zcode == zmpeg3_t::PICTURE_START_CODE ) {
207           total_frames++;
208         }
209         else if( zcode == zmpeg3_t::SEQUENCE_END_CODE ) {
210 /* Got a sequence end code at the end of this buffer. */
211           output_size -= 4;
212         }
213
214         zcode = (uint32_t)buffer[0] << 24;
215         zcode |= (uint32_t)buffer[1] << 16;
216         zcode |= (uint32_t)buffer[2] << 8;
217         zcode |= buffer[3];
218
219         i = 0;
220         offset = 0;
221         if( zcode == zmpeg3_t::SEQUENCE_START_CODE && current_output_file > 0 ) {
222 /* Skip the sequence start code */
223           i += 4;
224           while( i < output_size && zcode != zmpeg3_t::GOP_START_CODE ) {
225             zcode <<= 8;
226             zcode |= buffer[i++];
227           }
228           i -= 4;
229           offset = i;
230         }
231
232 /* Search for GOP header to fix */
233         zcode = (uint32_t)buffer[i++] << 24;
234         zcode |= (uint32_t)buffer[i++] << 16;
235         zcode |= (uint32_t)buffer[i++] << 8;
236         zcode |= buffer[i++];
237         while( i < output_size && zcode != zmpeg3_t::GOP_START_CODE ) {
238           zcode <<= 8;
239           zcode |= buffer[i++];
240         }
241
242         if( zcode == zmpeg3_t::GOP_START_CODE ) {
243 /* Get the time code */
244           zcode = (uint32_t)buffer[i] << 24;
245           zcode |= (uint32_t)buffer[i + 1] << 16;
246           zcode |= (uint32_t)buffer[i + 2] << 8;
247           zcode |= (uint32_t)buffer[i + 3];
248
249           hour = zcode >> 26 & 0x1f;
250           minute = zcode >> 20 & 0x3f;
251           second = zcode >> 13 & 0x3f;
252           frame = zcode >> 7 & 0x3f;
253
254 /*        int64_t gop_frame = (int64_t)(hour * 3600 * mpeg3_frame_rate(in, stream) +
255               minute * 60 * mpeg3_frame_rate(in, stream) +
256               second * mpeg3_frame_rate(in, stream) + 
257               frame); */
258 /* fprintf(stderr, "old: %02d:%02d:%02d:%02d ", hour, minute, second, frame); */
259 /* Write a new time code */
260           hour = (int64_t)((float)(total_frames - 1) / mpeg3_frame_rate(in, stream) / 3600);
261           carry = hour * 3600 * mpeg3_frame_rate(in, stream);
262           minute = (int64_t)((float)(total_frames - 1 - carry) / mpeg3_frame_rate(in, stream) / 60);
263           carry += minute * 60 * mpeg3_frame_rate(in, stream);
264           second = (int64_t)((float)(total_frames - 1 - carry) / mpeg3_frame_rate(in, stream));
265           carry += second * mpeg3_frame_rate(in, stream);
266           frame = (total_frames - 1 - carry);
267
268           buffer[i] = ((zcode >> 24) & 0x80) | (hour << 2) | (minute >> 4);
269           buffer[i + 1] = ((zcode >> 16) & 0x08) | ((minute & 0xf) << 4) | (second >> 3);
270           buffer[i + 2] = ((second & 0x7) << 5) | (frame >> 1);
271           buffer[i + 3] = (zcode & 0x7f) | ((frame & 0x1) << 7);
272 /* fprintf(stderr, "new: %02d:%02d:%02d:%02d\n", hour, minute, second, frame); */
273         }
274
275
276 /* Test 32 bit overflow */
277         if( outpath[0] ) {
278           if( ftell(out) > 0x7f000000 ) {
279             fclose(out);
280             out_counter++;
281             sprintf(newpath, "%s%03d", outpath, out_counter);
282             if( !(out = fopen(newpath, "wb")) ) {
283               fprintf(stderr, "Couldn't open %s for writing.\n", newpath);
284               exit(1);
285             }
286           }
287         }
288 /*
289  * fprintf(stderr, "mpeg3cat 5 %02x %02x %02x %02x\n", 
290  *   (buffer + offset)[0], 
291  *   (buffer + offset)[1],
292  *   (buffer + offset)[2], 
293  *   (buffer + offset)[3]);
294  */
295
296 /* Write the frame */
297         result = !write_output(buffer + offset, output_size - offset, in);
298         if( result ) {
299           perror("write video chunk");
300           break;
301         }
302       }
303     }
304     else
305 /* Output program stream */
306 /* In real life, program streams ended up having discontinuities in time codes */
307 /* so this isn't being maintained anymore. */
308     if( in->is_program_stream() ) {
309       zdemuxer_t *demuxer = in->vtrack[0]->demuxer;
310       result = 0;
311
312 /* Append program stream with no changes */
313       demuxer->read_all = 1;
314       demuxer->seek_byte(zmpeg3_t::START_BYTE);
315
316       while( !result ) {
317         result = demuxer->seek_phys();
318         if( !result ) {
319           demuxer->zdata.size = 0;
320           demuxer->zvideo.size = 0;
321           demuxer->zaudio.size = 0;
322           result = demuxer->read_program();
323           if( result )
324             fprintf(stderr, "Hit end of data in %s\n", inpath);
325         }
326
327
328 // Read again and decrypt it
329         unsigned char raw_data[0x10000];
330         int raw_size = 0;
331         if( !result ) {
332           ztitle_t *title = demuxer->titles[demuxer->current_title];
333           int64_t temp_offset = title->fs->tell();
334           int64_t decryption_offset =
335             demuxer->last_packet_decryption - demuxer->last_packet_start;
336           raw_size = demuxer->last_packet_end - demuxer->last_packet_start;
337
338           title->fs->seek(demuxer->last_packet_start);
339           title->fs->read_data(raw_data, raw_size);
340           title->fs->seek(temp_offset);
341
342           if( decryption_offset > 0 && 
343               decryption_offset < raw_size &&
344               raw_data[decryption_offset] & 0x30 ) {
345             if( title->fs->css.decrypt_packet(raw_data, 0) ) {
346               fprintf(stderr, "get_ps_pes_packet: Decryption not available\n");
347               return 1;
348             }
349             raw_data[decryption_offset] &= 0xcf;
350           }
351         }
352
353 // Write it
354         if( !result ) {
355           result = !write_output(raw_data, raw_size, in);
356           total_written += raw_size;
357           if( result) fprintf(stderr, "write program stream: %s\n", strerror(errno) );
358         }
359       }
360     }
361     else {
362 /* No transport stream support, since these can be catted */
363       fprintf(stderr, "No catting of transport streams.\n");
364       mpeg3_close(in);
365       in = 0;
366       continue;
367     }
368
369     mpeg3_close(in);
370     in = 0;
371     current_output_file++;
372   }
373
374 #ifdef TODO
375 /* Terminate output */
376   if( current_output_file > 0 && do_video ) {
377 /* Write new end of sequence */
378 /* Not very useful */
379     buffer[0] = (zmpeg3_t::SEQUENCE_END_CODE >> 24) & 0xff;
380     buffer[1] = (zmpeg3_t::SEQUENCE_END_CODE >> 16) & 0xff;
381     buffer[2] = (zmpeg3_t::SEQUENCE_END_CODE >>  8) & 0xff;
382     buffer[3] = (zmpeg3_t::SEQUENCE_END_CODE >>  0) & 0xff;
383     result = !write_output(buffer, 4, in);
384   }
385 #endif
386   if( outpath[0]) fclose(out );
387   exit(0);
388 }
389