Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / mplexlo / mplex.c
diff --git a/cinelerra-5.1/mplexlo/mplex.c b/cinelerra-5.1/mplexlo/mplex.c
new file mode 100644 (file)
index 0000000..be372d1
--- /dev/null
@@ -0,0 +1,374 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#include "libzmpeg3.h"
+
+#define PACKET_SIZE 2048
+
+class multiplexer_t {
+public:
+       int derivative;
+       unsigned char packet_buffer[PACKET_SIZE];
+       FILE *out_file;
+};
+
+class track_t {
+public:
+       void *operator new(size_t n) {
+               void *t = (void*) new char[n];
+               memset(t,0,n);
+               return t;
+       }
+       void operator delete(void *t,size_t n) {
+               delete[](char*)t;
+       }
+       long bytes_decoded;
+       long frames_decoded;
+       long samples_decoded;
+       mpeg3_t *file;
+       FILE *raw_file;
+       int stream_number;
+       int end_of_data;
+};
+
+int write_pack_header(unsigned char *ptr, 
+               multiplexer_t *mplex, 
+               float seconds,
+               int stream_id,
+               int ac3)
+{
+       int packet_length;
+// PACK START CODE
+       *ptr++ = 0x00;
+       *ptr++ = 0x00;
+       *ptr++ = 0x01;
+       *ptr++ = 0xba;
+
+       if(mplex->derivative == 1)
+       {
+               unsigned long timestamp = (unsigned long)(seconds * 90000);
+               *ptr = 0x20;
+               *ptr++ |= ((timestamp & 0xc0000000) >> 29) | 1;
+               *ptr++ =  (timestamp & 0x3fc00000) >> 22;
+               *ptr++ =  ((timestamp & 0x003f8000) >> 14) | 1;
+               *ptr++ =  (timestamp & 0x00007f80) >> 7;
+               *ptr++ =  ((timestamp & 0x0000007f) << 1) | 1;
+
+               *ptr++ = 0;
+               *ptr++ = 0;
+               *ptr++ = 0;
+       }
+       else
+       if(mplex->derivative == 2)
+       {
+               *ptr = 0x40;
+       }
+
+       *ptr++ = 0x00;
+       *ptr++ = 0x00;
+       *ptr++ = 0x01;
+       if(ac3) 
+               *ptr++ = 0xbd;
+       else
+               *ptr++ = stream_id;
+
+// Packet length
+       packet_length = PACKET_SIZE - (ptr - mplex->packet_buffer) - 2;
+       *ptr++ = (packet_length & 0xff00) >> 8;
+       *ptr++ = packet_length & 0xff;
+
+// Pts/dts flags
+       *ptr++ = 0x0f;
+
+// AC3 stream_id
+       if(ac3)
+       {
+               *ptr++ = stream_id;
+               *ptr++ = 0x00;
+               *ptr++ = 0x00;
+               *ptr++ = 0x00;
+       }
+
+       return PACKET_SIZE - (ptr - mplex->packet_buffer);
+}
+
+int write_packet(track_t *track, 
+               float start_time, 
+               float end_time, 
+               multiplexer_t *mplex,
+               int stream_id,
+               int ac3)
+{
+       int result = 0;
+       
+       long bytes_needed = track->bytes_decoded - ftell(track->raw_file);
+       long current_byte = 0;
+
+       while(current_byte < bytes_needed)
+       {
+// Write packet header
+               float current_time = start_time + (float)current_byte / bytes_needed * (end_time - start_time);
+               int packet_bytes = write_pack_header(mplex->packet_buffer, 
+                       mplex, 
+                       current_time,
+                       stream_id,
+                       ac3);
+               unsigned char *ptr = mplex->packet_buffer + PACKET_SIZE - packet_bytes;
+               /*int bytes_read =*/ fread(ptr, 1, packet_bytes, track->raw_file);
+
+               result = !fwrite(mplex->packet_buffer, PACKET_SIZE, 1, mplex->out_file);
+               current_byte += packet_bytes;
+       }
+       return result;
+}
+
+int main(int argc, char *argv[])
+{
+       int streams = 0;
+       int stream = 0;
+       int result = 0;
+       track_t *atracks[streams + 1];
+       track_t *vtracks[streams + 1];
+       int total_atracks = 0;
+       int total_vtracks = 0;
+       float frame_rate = 30000.0 / 1001.0;
+       float sample_rate = 48000;
+       long frames_decoded = 0;
+       float *audio_temp = new float[1];
+       long audio_temp_allocated = 0;
+       float current_time = 0, previous_time = 0;
+       int video_completed = 0;
+       int audio_completed = 0;
+       int i;
+       int ac3 = 0;
+       multiplexer_t mplex;
+       char **path;
+       char *output_path = 0;
+       int old_percentage = 0;
+       
+       path = new char*[argc];
+       mplex.derivative = 1;
+       mplex.out_file = 0;
+
+       if(argc < 4)
+       {
+               printf("Tiny MPLEX by Heroine Virtual Ltd.\n");
+               printf("Usage: mplex [-a] <stream 1> <stream2> ... <output>\n");
+               printf("        -a use ac3 packet headers\n");
+               exit(1);
+       }
+
+       for(i = 1; i < argc; i++)
+       {
+               if(!strcmp(argv[i], "-a"))
+               {
+                       ac3 = 1;
+               }
+               else
+               if(i == argc - 1)
+               {
+                       output_path = argv[i];
+               }
+               else
+               {
+                       path[stream] = new char[strlen(argv[i])+1];
+                       strcpy(path[stream], argv[i]);
+                       streams++;
+                       stream++;
+               }
+       }
+
+// Open files
+       for(stream = 0; stream < streams; stream++)
+       {
+               int is_audio, is_video;
+               int error_return;
+               mpeg3_t *file = mpeg3_open(path[stream], &error_return);
+
+               if(!file)
+               {
+                       printf("Couldn't open %s\n", path[stream]);
+                       result = 1;
+               }
+               else
+               {
+                       is_audio = mpeg3_has_audio(file);
+                       is_video = mpeg3_has_video(file);
+                       mpeg3_set_cpus(file, 2);
+
+                       if(is_audio && is_video)
+                       {
+                               printf("%s: Can't multiplex a system stream.\n", path[stream]);
+                               result = 1;
+                       }
+                       else
+                       if(is_audio)
+                       {
+                               atracks[total_atracks] = new track_t();
+                               atracks[total_atracks]->file = file;
+                               sample_rate = mpeg3_sample_rate(file, 0);
+                               atracks[total_atracks]->raw_file = fopen(path[stream], "rb");
+                               atracks[total_atracks]->stream_number = total_atracks;
+                               total_atracks++;
+                       }
+                       else
+                       if(is_video)
+                       {
+                               vtracks[total_vtracks] = new track_t();
+                               vtracks[total_vtracks]->file = file;
+                               frame_rate = mpeg3_frame_rate(file, 0);
+                               vtracks[total_vtracks]->raw_file = fopen(path[stream], "rb");
+                               vtracks[total_vtracks]->stream_number = total_vtracks;
+// Get the first frame
+                               mpeg3_skip_video_frame(vtracks[total_vtracks]->file,0);
+                               total_vtracks++;
+                       }
+                       else
+                       {
+                               printf("%s: Has neither audio or video.\n", path[stream]);
+                               result = 1;
+                       }
+               }
+       }
+
+       if(!result)
+       {
+               if(!total_vtracks)
+               {
+                       printf("You must supply at least 1 video track.\n");
+                       result = 1;
+               }
+       }
+
+// Open output
+       if(!result)
+               if(!(mplex.out_file = fopen(output_path, "wb")))
+               {
+                       printf("Couldn't open output file\n");
+                       result = 1;
+               }
+
+// Write multiplexed stream
+       if(!result)
+       {
+               while(!result && !(video_completed && audio_completed))
+               {
+                       previous_time = current_time;
+// Want the frame to come before the audio that goes with it.
+                       for(stream = 0; stream < total_vtracks && !result; stream++)
+                       {
+// Decode a frame and write it
+                               track_t *track = vtracks[stream];
+
+                               if(!track->end_of_data)
+                               {
+                                       int percentage;
+                                       mpeg3_skip_video_frame(track->file,0);
+                                       track->frames_decoded++;
+                                       track->bytes_decoded = mpeg3_video_tell_byte(track->file,0);
+                                       if(track->frames_decoded > frames_decoded)
+                                       {
+                                               frames_decoded = track->frames_decoded;
+                                               current_time = (float)frames_decoded / frame_rate;
+                                       }
+
+                                       result = write_packet(track, 
+                                                                       previous_time, 
+                                                                       current_time, 
+                                                                       &mplex,
+                                                                       0xe0 | track->stream_number,
+                                                                       0);
+                                   track->end_of_data = mpeg3_end_of_video(track->file, 0);
+                                       percentage = (int)(mpeg3_video_tell_byte(track->file,0) * 100 / 
+                                               mpeg3_get_bytes(track->file));
+                                       if(percentage - old_percentage >= 1)
+                                       {
+                                               printf("\t%d%% completed\r", percentage);
+                                               old_percentage = percentage;
+                                       }
+                                       fflush(stdout);
+                               }
+                       }
+
+// Decode audio until the last frame is covered
+                       for(stream = 0; stream < total_atracks && !result; stream++)
+                       {
+                               track_t *track = atracks[stream];
+//printf("mplex decode audio 1\n");
+                               if(!track->end_of_data &&
+                                       (track->samples_decoded < current_time * sample_rate ||
+                                               video_completed))
+                               {
+                                       if(!video_completed)
+                                       {
+                                               long samples_needed = (long)(current_time * sample_rate) - track->samples_decoded;
+                                               if(audio_temp_allocated < samples_needed)
+                                               {
+                                                       delete [] audio_temp;
+                                                       audio_temp = new float[samples_needed];
+                                                       audio_temp_allocated = samples_needed;
+                                               }
+
+                                               mpeg3_read_audio(track->file,
+                                                       audio_temp,
+                                                       0,
+                                                       0,
+                                                       samples_needed,
+                                                       0);
+                                               track->bytes_decoded = mpeg3_audio_tell_byte(track->file,0);
+                                               if(!track->end_of_data) track->bytes_decoded -= 2048;
+                                               track->samples_decoded += samples_needed;
+                                               track->end_of_data = mpeg3_end_of_audio(track->file, 0);
+                                       }
+                                       else
+                                       {
+                                               track->bytes_decoded = mpeg3_get_bytes(track->file);
+                                               track->end_of_data = 1;
+                                       }
+
+//printf("mplex decode audio 2\n");
+                                       result = write_packet(track, 
+                                               previous_time, 
+                                               current_time, 
+                                               &mplex,
+                                               ac3 ? track->stream_number : (0xc0 | track->stream_number),
+                                               ac3);
+                               }
+                       }
+
+                       for(stream = 0; stream < total_vtracks; stream++)
+                       {
+                               if(vtracks[stream]->end_of_data) video_completed++;
+                       }
+                       if(video_completed < total_vtracks) video_completed = 0;
+
+                       for(stream = 0; stream < total_atracks; stream++)
+                       {
+                               if(atracks[stream]->end_of_data) audio_completed++;
+                       }
+                       if(audio_completed < total_atracks) audio_completed = 0;
+               }
+       }
+
+// Close streams
+       for(stream = 0; stream < total_atracks; stream++)
+       {
+               mpeg3_close(atracks[stream]->file);
+               fclose(atracks[stream]->raw_file);
+               delete atracks[stream];
+       }
+
+       for(stream = 0; stream < total_vtracks; stream++)
+       {
+               mpeg3_close(vtracks[stream]->file);
+               fclose(vtracks[stream]->raw_file);
+               delete vtracks[stream];
+       }
+       
+       if(mplex.out_file) fclose(mplex.out_file);
+
+       return result;
+}
+
+