📄 mpegsystem.cpp
字号:
#include <stdlib.h> /* for realloc() */#include <string.h> /* for memmove() */#include <errno.h>#include <assert.h>#ifdef WIN32#include <sys/types.h>#include <io.h>#include <winsock.h>#else#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/time.h>#include <sys/select.h>#endif#include "MPEGsystem.h"#include "MPEGstream.h"/* Define this if you want to use a separate thread for stream decoding *///#define USE_SYSTEM_THREADUint8 const PACKET_CODE[] = { 0x00, 0x00, 0x01, 0xba };Uint8 const PACKET_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const END_CODE[] = { 0x00, 0x00, 0x01, 0xb9 };Uint8 const END_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const END2_CODE[] = { 0x00, 0x00, 0x01, 0xb7 };Uint8 const END2_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const VIDEOSTREAM_CODE[] = { 0x00, 0x00, 0x01, 0xe0 };Uint8 const VIDEOSTREAM_MASK[] = { 0xff, 0xff, 0xff, 0xe0 };Uint8 const AUDIOSTREAM_CODE[] = { 0x00, 0x00, 0x01, 0xc0 };Uint8 const AUDIOSTREAM_MASK[] = { 0xff, 0xff, 0xff, 0xc0 };Uint8 const PADSTREAM_CODE[] = { 0x00, 0x00, 0x01, 0xbe };Uint8 const PADSTREAM_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const SYSTEMSTREAM_CODE[] = { 0x00, 0x00, 0x01, 0xbb };Uint8 const SYSTEMSTREAM_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const USERSTREAM_CODE[] = { 0x00, 0x00, 0x01, 0xb2 };Uint8 const USERSTREAM_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const VIDEO_CODE[] = { 0x00, 0x00, 0x01, 0xb3 };Uint8 const VIDEO_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const AUDIO_CODE[] = { 0xff, 0xf0, 0x00, 0x00 };Uint8 const AUDIO_MASK[] = { 0xff, 0xf0, 0x00, 0x00 };Uint8 const GOP_CODE[] = { 0x00, 0x00, 0x01, 0xb8 };Uint8 const GOP_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const PICTURE_CODE[] = { 0x00, 0x00, 0x01, 0x00 };Uint8 const PICTURE_MASK[] = { 0xff, 0xff, 0xff, 0xff };Uint8 const SLICE_CODE[] = { 0x00, 0x00, 0x01, 0x01 };Uint8 const SLICE_MASK[] = { 0xff, 0xff, 0xff, 0x00 };Uint8 const ZERO_CODE[] = { 0x00, 0x00, 0x00, 0x00 };Uint8 const FULL_MASK[] = { 0xff, 0xff, 0xff, 0xff };/* The size is arbitrary but should be sufficient to contain *//* two MPEG packets and reduce disk (or network) access. */// Hiroshi Yamashita notes that the original size was too large#define MPEG_BUFFER_SIZE (16 * 1024)/* The granularity (2^LG2_GRANULARITY) determine what length of read data *//* will be a multiple of, e.g. setting LG2_GRANULARITY to 12 will make *//* read size (when calling the read function in Read method) to be a *//* multiple of 4096 */#define LG2_GRANULARITY 12#define READ_ALIGN(x) (((x) >> LG2_GRANULARITY) << LG2_GRANULARITY)/* This defines the maximum number of buffers that can be preread *//* It is to prevent filling the whole memory with buffers on systems *//* where read is immediate such as in the case of files */#define MPEG_BUFFER_MAX 16/* Timeout before read fails */#define READ_TIME_OUT 1000000/* timestamping */#define FLOAT_0x10000 (double)((Uint32)1 << 16)#define STD_SYSTEM_CLOCK_FREQ 90000L/* This work only on little endian systems *//*#define REV(x) ((((x)&0x000000FF)<<24)| \ (((x)&0x0000FF00)<< 8)| \ (((x)&0x00FF0000)>> 8)| \ (((x)&0xFF000000)>>24))#define MATCH4(x, y, m) (((x) & REV(m)) == REV(y))*/const int audio_frequencies[2][3]={ {44100,48000,32000}, // MPEG 1 {22050,24000,16000} // MPEG 2};const int audio_bitrate[2][3][15]={ // MPEG 1 {{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448}, {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384}, {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320}}, // MPEG 2 {{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256}, {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}, {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}}};/* Match two 4-byte codes */static inline bool Match4(Uint8 const code1[4], Uint8 const code2[4], Uint8 const mask[4]){ return( ((code1[0] & mask[0]) == (code2[0] & mask[0])) && ((code1[1] & mask[1]) == (code2[1] & mask[1])) && ((code1[2] & mask[2]) == (code2[2] & mask[2])) && ((code1[3] & mask[3]) == (code2[3] & mask[3])) );}static inline double read_time_code(Uint8 *pointer){ double timestamp; Uint8 hibit; Uint32 lowbytes; hibit = (pointer[0]>>3)&0x01; lowbytes = (((Uint32)pointer[0] >> 1) & 0x03) << 30; lowbytes |= (Uint32)pointer[1] << 22; lowbytes |= ((Uint32)pointer[2] >> 1) << 15; lowbytes |= (Uint32)pointer[3] << 7; lowbytes |= ((Uint32)pointer[4]) >> 1; timestamp = (double)hibit*FLOAT_0x10000*FLOAT_0x10000+(double)lowbytes; timestamp /= STD_SYSTEM_CLOCK_FREQ; return timestamp;}/* Return true if there is a valid audio header at the beginning of pointer */static inline Uint32 audio_header(Uint8 * pointer, Uint32 * framesize, double * frametime){ Uint32 layer, version, frequency, bitrate, mode, padding, size; if(((pointer[0] & 0xff) != 0xff) || // No sync bits ((pointer[1] & 0xf0) != 0xf0) || // ((pointer[2] & 0xf0) == 0x00) || // Bitrate is 0 ((pointer[2] & 0xf0) == 0xf0) || // Bitrate is 15 ((pointer[2] & 0x0c) == 0x0c) || // Frequency is 3 ((pointer[1] & 0x06) == 0x00)) // Layer is 4 return(0); layer = 4 - (((pointer)[1] >> 1) & 3); version = (((pointer)[1] >> 3) & 1) ^ 1; padding = ((pointer)[2] >> 1) & 1; frequency = audio_frequencies[version][(((pointer)[2] >> 2) & 3)]; bitrate = audio_bitrate[version][layer-1][(((pointer)[2] >> 4) & 15)]; mode = ((pointer)[3] >> 6) & 3; if(layer==1) { size = 12000 * bitrate / frequency; if(frequency==0 && padding) size++; size <<= 2; } else { size = 144000 * bitrate / (frequency<<version); if(padding) size++; } if(framesize) *framesize = size; if(frametime) *frametime = 8.0 * size / (1000. * bitrate); return(4); /* Audio header size */}/* Search the next valid audio header */static inline bool audio_aligned(Uint8 *pointer, Uint32 size){ Uint32 i, s;#if 1 // Sam 10/27 - Don't choke on partially corrupt data size = 4;#endif /* Check on all data available that next headers are aligned too */ for(i = 0; i + 3 < size && audio_header(pointer+i, &s, 0); i+=s); if(i + 3 < size) return(false); else return(true);}/* Return true if there is a valid sequence header at the beginning of pointer */static inline Uint32 sequence_header(Uint8 * pointer, Uint32 size, double * _frametime){ double frametime; Uint32 header_size; header_size = 0; if((header_size+=4) >= size) return(0); if(!Match4(pointer, VIDEO_CODE, VIDEO_MASK)) return(0); /* Not a sequence start code */ /* Parse the sequence header information */ if((header_size+=8) >= size) return(0); switch(pointer[7]&0xF) /* 4 bits of fps */ { case 1: frametime = 1.0/23.97; break; case 2: frametime = 1.0/24.00; break; case 3: frametime = 1.0/25.00; break; case 4: frametime = 1.0/29.97; break; case 5: frametime = 1.0/30.00; break; case 6: frametime = 1.0/50.00; break; case 7: frametime = 1.0/59.94; break; case 8: frametime = 1.0/60.00; break; case 9: frametime = 1.0/15.00; break; default: frametime = 1.0/30.00; break; } if(_frametime) *_frametime = frametime; return(header_size); /* sequence header size */}/* Return true if there is a valid gop header at the beginning of pointer */static inline Uint32 gop_header(Uint8 * pointer, Uint32 size, double * timestamp){ Uint32 header_size; Uint32 hour, min, sec, frame; header_size = 0; if((header_size+=4) >= size) return(0); if(!Match4(pointer, GOP_CODE, GOP_MASK)) return(0); /* Not a gop start code */ /* Parse the gop header information */ hour = (pointer[4] >> 2) & 31; min = ((pointer[4] & 3) << 4) | ((pointer[5] >> 4) & 15); sec = ((pointer[5] & 7) << 3) | ((pointer[6] >> 5) & 7); frame = ((pointer[6] & 31) << 1) | ((pointer[7] >> 7) & 1); if((header_size+=4) >= size) return(0); if(timestamp) *timestamp = sec + 60.*min + 3600.*hour; return(header_size); /* gop header size */}/* Return true if there is a valid picture header at the beginning of pointer */static inline Uint32 picture_header(Uint8 * pointer, Uint32 size){ Uint32 header_size; header_size = 0; if((header_size+=4) >= size) return(0); if(!Match4(pointer, PICTURE_CODE, PICTURE_MASK)) return(0); /* Not a picture start code */ /* Parse the picture header information */ if((header_size+=4) >= size) return(0); return(header_size); /* picture header size */}/* Return true if there is a valid slice header at the beginning of pointer */static inline Uint32 slice_header(Uint8 * pointer, Uint32 size){ Uint32 header_size; header_size = 0; if((header_size+=4) >= size) return(0); if(!(Match4(pointer, SLICE_CODE, SLICE_MASK) && pointer[3] >= 0x01 && pointer[3] <= 0xaf)) return(0); /* Not a slice start code */ return(header_size); /* slice header size */}/* Return true if there is a valid packet header at the beginning of pointer */static inline Uint32 packet_header(Uint8 * pointer, Uint32 size, double * _timestamp){ double timestamp; Uint32 header_size; header_size = 0; if((header_size+=4) >= size) return(0); if(!Match4(pointer, PACKET_CODE, PACKET_MASK)) return(0); /* Not a packet start code */ /* Parse the packet information */ if((header_size+=8) >= size) return(0); timestamp = read_time_code(pointer+4); if(_timestamp) *_timestamp = timestamp; return(header_size); /* packet header size */}/* Return true if there is a valid stream header at the beginning of pointer */static inline Uint32 stream_header(Uint8 * pointer, Uint32 size, Uint32 * _packet_size, Uint8 * _stream_id, double * _stream_timestamp, double timestamp){ Uint32 header_size, packet_size; Uint8 stream_id; double stream_timestamp; header_size = 0; if((header_size += 4) >= size) return(0); if(!Match4(pointer, SYSTEMSTREAM_CODE, SYSTEMSTREAM_MASK) && !Match4(pointer, AUDIOSTREAM_CODE, AUDIOSTREAM_MASK) && !Match4(pointer, VIDEOSTREAM_CODE, VIDEOSTREAM_MASK) && !Match4(pointer, PADSTREAM_CODE, PADSTREAM_MASK) && !Match4(pointer, USERSTREAM_CODE, USERSTREAM_MASK)) return(0); /* Unknown encapsulated stream */ /* Parse the stream packet */ /* Get the stream id, and packet length */ stream_id = pointer[3]; pointer += 4; if((header_size += 2) >= size) return(0); packet_size = (((unsigned short) pointer[0] << 8) | pointer[1]); pointer += 2; /* Skip stuffing bytes */ while ( pointer[0] == 0xff ) { ++pointer; if((++header_size) >= size) return(0); --packet_size; } if ( (pointer[0] & 0x40) == 0x40 ) { pointer += 2; if((header_size += 2) >= size) return(0); packet_size -= 2; } if ( (pointer[0] & 0x20) == 0x20 ) { /* get the PTS */ stream_timestamp = read_time_code(pointer); /* we don't care about DTS */ if ( (pointer[0] & 0x30) == 0x30 ){ pointer += 5; if((header_size += 5) >= size) return(0); packet_size -= 5; } pointer += 4; if((header_size += 4) >= size) return(0); packet_size -= 4; } else if ( pointer[0] != 0x0f && pointer[0] != 0x80) return(0); /* not a valid header */ else stream_timestamp = timestamp; if((++header_size) >= size) return(0); --packet_size; if(_packet_size) *_packet_size = packet_size; if(_stream_id) *_stream_id = stream_id; if(_stream_timestamp) *_stream_timestamp = stream_timestamp; return(header_size);}/* Search the next valid audio header */static inline bool system_aligned(Uint8 *pointer, Uint32 size){ Uint32 i, s; /* Check that packet contains at least one stream */ i = 0; while((s = packet_header(pointer+i, size-i, 0)) != 0) if((i+=s) >= size) return(true); if((s = stream_header(pointer+i, size-i, 0, 0, 0, 0)) != 0) return(true); else return(false);}/* Skip possible zeros at the beggining of the packet */Uint32 skip_zeros(Uint8 * pointer, Uint32 size){ Uint32 header_size; Uint8 const one[4] = {0x00,0x00,0x00,0x01}; if(!size) return(0); header_size = 0; while(Match4(pointer, ZERO_CODE, FULL_MASK)) { pointer++; if((++header_size) >= size - 4) return(0); if(Match4(pointer, one, FULL_MASK)) { pointer++; if((++header_size) >= size - 4) return(0); } } return(header_size);}MPEGsystem::MPEGsystem(SDL_RWops *mpeg_source){ source = mpeg_source; /* Create a new buffer for reading */ read_buffer = new Uint8[MPEG_BUFFER_SIZE]; /* Create a mutex to avoid concurrent access to the stream */ system_mutex = SDL_CreateMutex(); request_wait = SDL_CreateSemaphore(0); /* Invalidate the read buffer */ pointer = read_buffer; read_size = 0; read_total = 0; packet_total = 0; endofstream = errorstream = false; looping = false; frametime = 0.0; stream_timestamp = 0.0; /* Create an empty stream list */ stream_list = (MPEGstream **) malloc(sizeof(MPEGstream *)); stream_list[0] = 0; /* Create the system stream and add it to the list */ if(!get_stream(SYSTEM_STREAMID)) add_stream(new MPEGstream(this, SYSTEM_STREAMID)); timestamp = 0.0; timedrift = 0.0; skip_timestamp = -1; system_thread_running = false; system_thread = 0; /* Search the MPEG for the first header */ if(!seek_first_header()) { errorstream = true; SetError("Could not find the beginning of MPEG data\n"); return; }#ifdef USE_SYSTEM_THREAD /* Start the system thread */ system_thread = SDL_CreateThread(SystemThread, this); /* Wait for the thread to start */ while(!system_thread_running && !Eof()) SDL_Delay(1);#else system_thread_running = true;#endif /* Look for streams */ int tries = 0; do { RequestBuffer(); Wait(); if ( tries++ < 10 ) { if ( exist_stream(VIDEO_STREAMID, 0xF0) && exist_stream(AUDIO_STREAMID, 0xF0) ) { break; } } else { if ( exist_stream(VIDEO_STREAMID, 0xF0) || exist_stream(AUDIO_STREAMID, 0xF0) ) { break; } } } while(!Eof());}MPEGsystem::~MPEGsystem(){ MPEGstream ** list; /* Kill the system thread */ Stop(); SDL_DestroySemaphore(request_wait); SDL_DestroyMutex(system_mutex); /* Delete the streams */ for(list = stream_list; *list; list ++) delete *list; free(stream_list); /* Delete the read buffer */ delete[] read_buffer;}MPEGstream ** MPEGsystem::GetStreamList(){ return(stream_list);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -