#include #include #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] ... \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; }