diff options
Diffstat (limited to 'server/multiplexer.cc')
-rw-r--r-- | server/multiplexer.cc | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/server/multiplexer.cc b/server/multiplexer.cc new file mode 100644 index 0000000..7a8b095 --- /dev/null +++ b/server/multiplexer.cc @@ -0,0 +1,495 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * multiplexer.cc + * + * Wed Aug 31 13:05:18 CEST 2005 + * Copyright 2005 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of MIaV. + * + * MIaV is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MIaV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MIaV; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "config.h" +#include "multiplexer.h" + +#include <netinet/in.h> +#include <math.h> + +#include "util.h" + +#define SIZEOF(x) (sizeof(x)-1) + +#define MASK3 0x7 +#define MASK15 0x7FFF +#define TIMECODE32_30(x) ((x >> 30) & MASK3 ) +#define TIMECODE29_15(x) ((x >> 15) & MASK15) +#define TIMECODE14_0(x) ((x >> 0) & MASK15) + +// Audio index lists +/* +static unsigned int frequency_index[4] = {44100, 48000, 32000, 0}; +//static unsigned int slots [4] = {12, 144, 0, 0}; +//static unsigned int slot_index [4] = {144, 144, 144, 0}; +//static unsigned int sample_index [4] = {384, 1152, 0, 0}; +static unsigned int bitrate_index [4][16] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0}, // Reserved + {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}, // Layer III + {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, // Layer II + {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0} // Layer I +}; +static char layer_index[4][12] = { "Reserved", "Layer III", "Layer II", "Layer I" }; +static char mode_index[4][32] = { "Stereo", "Joint Stereo", "Dual Channel", "Single Channel"}; +static char protection_index[2][32] = { "CRC check enabled", "CRC check disabled" }; +*/ +//static unsigned short int syncword = 0xFFF; + +// Video index lists +/* +#define FORBIDDEN -1.0 +#define RESERVED -2.0 +static double picture_rate_index[16] = { + FORBIDDEN, 23.976, 24.0, 25.0, 29.97, 30.0, 50.0, 59.94, 60, + RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED, RESERVED +}; +*/ +Multiplexer::Multiplexer(File *f, Multicast *m, Info *i, volatile bool *r, + ThreadSafeQueuePriority *video_q, + ThreadSafeQueuePriority *audio_q) +{ + running = r; + file = f; + multicast = m; + info = i; + + frame[TYPE_VIDEO] = NULL; + written[TYPE_VIDEO] = 0.0; + + frame[TYPE_AUDIO] = NULL; + written[TYPE_AUDIO] = 0.0; + + write_audio_packet = 0; + write_system_header = 0; + + audio_header_read = false; + + queue[TYPE_VIDEO] = video_q; + queue[TYPE_AUDIO] = audio_q; + + SCR = 3904;//0x40010003LL;//0x1E80; + +} + +Multiplexer::~Multiplexer() +{ +} + +int Multiplexer::Write(void* data, int size) +{ + int ret; + + if(multicast && multicast->multicast_audio == true) multicast->Write(data, size); + ret = file->Write(data, size); + + return ret; +} + +int Multiplexer::Write(char* data, int size) +{ + return Write((void*)data, size); +} + +int Multiplexer::Write(unsigned long long int val) +{ + int res; + int written = 0; + unsigned long int *h_u = (unsigned long int *)&val; + unsigned long int *h_l = (unsigned long int *)(((char*)&val) + sizeof(unsigned long int)); + + *h_u = htonl(*h_u); + *h_l = htonl(*h_l); + + if((res = Write((void*)h_l, sizeof(*h_l))) < 0) { + return res; + } + written += res; + + if((res = Write((void*)h_u, sizeof(*h_u))) < 0) { + return res; + } + written += res; + + return written; +} + +int Multiplexer::Write(long long int val) +{ + int res; + int written = 0; + unsigned long int *h_u = (unsigned long int *)&val; + unsigned long int *h_l = (unsigned long int *)(((char*)&val) + sizeof(unsigned long int)); + + *h_u = htonl(*h_u); + *h_l = htonl(*h_l); + + if((res = Write((void*)h_l, sizeof(*h_l))) < 0) { + return res; + } + written += res; + + if((res = Write((void*)h_u, sizeof(*h_u))) < 0) { + return res; + } + written += res; + + return written; +} + +int Multiplexer::Write(long int val) +{ + val = htonl(val); + + return Write((char*)&val, sizeof(val)); +} + +int Multiplexer::Write(unsigned long int val) +{ + val = htonl(val); + + return Write((char*)&val, sizeof(val)); +} + +int Multiplexer::Write(int val) +{ + val = htonl(val); + + return Write((char*)&val, sizeof(val)); +} + +int Multiplexer::Write(unsigned int val) +{ + val = htonl(val); + + return Write((char*)&val, sizeof(val)); +} + +int Multiplexer::Write(short int val) +{ + val = htons(val); + + return Write((char*)&val, sizeof(val)); +} + +int Multiplexer::Write(unsigned short int val) +{ + val = htons(val); + + return Write((char*)&val, sizeof(val)); +} + +Frame *Multiplexer::getFrame(StreamType type) +{ + // info->info("Get %s Frame", type==TYPE_AUDIO?"Audio\0":"Video\0"); + + read[type] = 0; + + Frame *frame = queue[type]->pop(); + + // If we multicast without audio, we better write the raw video stream. + if(type == TYPE_VIDEO && multicast && multicast->multicast_audio == false) + multicast->Write(frame->data, frame->size); + + return frame; +} + +int Multiplexer::read_stream(char *buf, unsigned int size, StreamType type) +{ + unsigned int copied = 0; + + while( copied < size ) { + + // If we read the entire frame, prepare to get a new one + if(frame[type] && read[type] == frame[type]->size) { + delete frame[type]; + frame[type] = NULL; + } + + // If no frame is in the buffer, get one from the queue + if(frame[type] == NULL) frame[type] = getFrame(type); + + // check for end of stream + if( frame[type]->endOfFrameStream == true) { + info->info("endOfFrameStream in Multiplexer %s-stream.", type==TYPE_VIDEO?"video\0":"audio\0"); + return copied; + } + + // If a frame exists in the buffer copy it to the output buffer + // (No frame ocurres when *running is set to false) + if( frame[type] ) { + unsigned int doread = (size - copied) < (frame[type]->size - read[type]) ? + size - copied : (frame[type]->size - read[type]); + + //info->info("Requested: %d. Read: %d. Doread: %d. In buffer %d", size, (*read), doread, (*frame)->size); + + memcpy(buf + copied, frame[type]->data + read[type], doread); + read[type] += doread; + copied += doread; + } + } + + return copied; +} + +bool Multiplexer::packet(StreamType type) +{ + char buf[PACKET_SIZE]; + + // Write data + // info->info("\t\t[%sPacket]", type==TYPE_AUDIO?"Audio\0":"Video\0"); + + unsigned short int framesize = read_stream(buf, PACKET_SIZE, type); + + Write((void*)ISO11172_1::packet_start_code_prefix, SIZEOF(ISO11172_1::packet_start_code_prefix)); + switch(type) { + case TYPE_VIDEO: + Write((void*)ISO11172_1::stream_id_video1, SIZEOF(ISO11172_1::stream_id_video1)); + break; + case TYPE_AUDIO: + Write((void*)ISO11172_1::stream_id_audio1, SIZEOF(ISO11172_1::stream_id_audio1)); + break; + } + + ISO11172_1::packet_header header; + header.marker_bit1 = header.marker_bit2 = header.marker_bit3 = 1; + header.padding = 0x2; // Must be 2 + header.stuffing_byte = 0xFF; + header.packet_length = framesize + sizeof(ISO11172_1::packet_header) - sizeof(short); + header.system_clock_reference1 = TIMECODE32_30(SCR); + header.system_clock_reference2 = TIMECODE29_15(SCR); + header.system_clock_reference3 = TIMECODE14_0(SCR); + Write(*((unsigned long long int*)&header)); + + Write(buf, framesize); + + if(framesize != PACKET_SIZE) return false; + + written[type] += (double)PACKET_SIZE / (double)frame[type]->size;//bitrate; + + return true; +} + +/** + * Create and write a packet + */ +bool Multiplexer::packet() +{ + //info->info("\t\tWritten[A]: %f, Written[V]: %f", written[TYPE_AUDIO], written[TYPE_VIDEO]); + + StreamType type; + /* + // New switching mechanism + if(written[TYPE_AUDIO] < written[TYPE_VIDEO]) { + type = TYPE_AUDIO; + } else { + type = TYPE_VIDEO; + } + */ + + // Newer switching mechanism + if(queue[TYPE_AUDIO]->size() > queue[TYPE_VIDEO]->size()) { + type = TYPE_AUDIO; + } else { + type = TYPE_VIDEO; + } + + + if(!packet(type)) { + // Flush the other stream too... + if(type == TYPE_AUDIO) type = TYPE_VIDEO; + else type = TYPE_AUDIO; + while(packet(type)); + return false; + } + return true; + + /* + // Count this up here, we want audio packets in packet 4, 9, ... NOT 0, 3, ... + + write_audio_packet++; + if(write_audio_packet % AUDIO_PACKET_FREQUENCY == 0) { + packet(TYPE_AUDIO); + } else { + packet(TYPE_VIDEO); + } + */ +} + +/** + * Create and write the system header + */ +void Multiplexer::system_header() +{ + // info->info("\t\t[System Header]"); + + // system_header_start_code (32 bits) + Write((void*)ISO11172_1::system_header_start_code, SIZEOF(ISO11172_1::system_header_start_code)); + + ISO11172_1::system_header header; + + header.marker_bit1 = header.marker_bit2 = header.marker_bit3 = 1; + + header.header_length = 8 - 2 + (NUM_TYPES * 3); + // (sizeof(header) - sizeof(header.header_length)) + + // NUM_TYPES * sizeof(ISO11172_1::stream_description); + header.rate_bound = 3521; // FIXME: Taken from the example! + header.audio_bound = 1; // Only 1 audio stream + header.fixed_flag = 1; // Fixed bitrate (0 indicates vbr) + header.CSPS_flag = 1; // Standarts compliant? (yes: see lame_set_strict_ISO in liblame_wrapper.cc) + header.system_audio_clock_flag = 1; // FIXME: What excactly is this?? + header.system_video_clock_flag = 1; // FIXME: What excactly is this?? + header.video_bound = 1; // Only 1 video stream + header.reserved_byte = 0xFF; // Must be 0xFF + Write(*((unsigned long long int*)&header)); + + ISO11172_1::stream_description audio_stream_description; + audio_stream_description.stream_id = 0xC0; + audio_stream_description.market_bits = 0x3; + audio_stream_description.STD_buffer_bound_scale = 0; // Must be 0 for audio streams + audio_stream_description.STD_buffer_size_bound = 32; // Buffer must be 32 * 128 bytes + Write(*((unsigned long int*)&audio_stream_description)); + + ISO11172_1::stream_description video_stream_description; + video_stream_description.stream_id = 0xE3; + video_stream_description.market_bits = 0x3; + video_stream_description.STD_buffer_bound_scale = 1; // Must be 1 for video streams + video_stream_description.STD_buffer_size_bound = 46; // Buffer must be 32 * 1024 bytes + Write(*((unsigned long int*)&video_stream_description)); +} + +/** + * Create and write a pack + */ +bool Multiplexer::pack() +{ + // info->info("\t[Pack"); + + Write((void*)ISO11172_1::pack_start_code, SIZEOF(ISO11172_1::pack_start_code)); + + ISO11172_1::pack_header header; + // Set marker bits to 1 + header.marker_bit1 = + header.marker_bit2 = + header.marker_bit3 = + header.marker_bit4 = + header.marker_bit5 = 1; + + header.padding = 0x2; + + unsigned int video_data_rate; + unsigned int audio_data_rate; + + if(frame[TYPE_AUDIO]) audio_data_rate = frame[TYPE_AUDIO]->bitrate; + else audio_data_rate = 112000; + + if(frame[TYPE_VIDEO]) video_data_rate = frame[TYPE_VIDEO]->bitrate; + else video_data_rate = 1100000; + + unsigned int Rmux = ISO11172_1::Rmux(video_data_rate, + audio_data_rate, + 20, // packet_header_size, + 12, // pack_header_size, + PACKETS_PER_PACK, // packets_per_pack, + PACKET_SIZE);// packet_data_size) + + header.mux_rate = Rmux; + //0x1B82; + + SCR = ISO11172_1::SCR(SCR, + 12, //pack_header_size, + PACKETS_PER_PACK, //packets_per_pack, + PACKET_SIZE, //packet_data_size, + Rmux); + + // SCR = 0x40010003LL; + + header.system_clock_reference1 = TIMECODE32_30(SCR); + header.system_clock_reference2 = TIMECODE29_15(SCR); + header.system_clock_reference3 = TIMECODE14_0(SCR); + /* + info->info("timecode All: %lld, 1: %lld, 2: %lld, 3: %lld", + SCR, + (unsigned long long int)header.system_clock_reference1, + (unsigned long long int)header.system_clock_reference2, + (unsigned long long int)header.system_clock_reference3 + ); + */ + Write(*((unsigned long long int*)&header)); + + if(write_system_header % SYSTEM_HEADER_FREQUENCY == 0) system_header(); + // Count this up here, we want a system header in pack 0, 5, ... NOT 4, 9, ... + write_system_header++; + + for(int cnt = 0; cnt < PACKETS_PER_PACK; cnt++) + if(!packet()) return false; + + // info->info("\t]"); + + return true; +} + +/** + * + */ +void Multiplexer::iso11172_stream() +{ + // info->info("[iso11172_stream"); + + while(pack()); + + // info->info("]"); + // info->info("[iso11172_end_code]"); + Write((void*)ISO11172_1::end_code, SIZEOF(ISO11172_1::end_code)); + + /* + info->info("false && false = %d", false && false); + info->info("true && false = %d", true && false); + info->info("true && true = %d", true && true); + */ +} + +//#define BYPASS TYPE_VIDEO +//#define BYPASS TYPE_AUDIO +void Multiplexer::multiplex() +{ +#ifdef BYPASS + + int frmsz; + char buf[1024]; + do { + frmsz = read_stream(buf, sizeof(buf), BYPASS); + info->info("Wrote %d bytes", frmsz); + Write(buf, frmsz); + } while(frmsz == sizeof(buf)); + return; + +#else/*BYPASS*/ + + iso11172_stream(); + +#endif/*BYPASS*/ +} |