From 602096d30437df5e8ef77c27eb9bbd93e633d167 Mon Sep 17 00:00:00 2001 From: Mark Calderbank Date: Sun, 7 May 2006 13:24:02 +0000 Subject: [PATCH] Demuxer redesign --- Makefile | 3 +- demuxer.cc | 516 ++++++++++---------------------------------------- demuxer.h | 144 ++++++-------- demuxervdr.cc | 192 +++++++++++++++++++ demuxervdr.h | 43 +++++ player.cc | 48 ++--- player.h | 4 +- 7 files changed, 429 insertions(+), 521 deletions(-) create mode 100644 demuxervdr.cc create mode 100644 demuxervdr.h diff --git a/Makefile b/Makefile index a3e55a3..926f887 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ CROSSLIBS = ../jpeg-6b/libjpeg.a OBJECTS1 = main.o command.o log.o tcp.o dsock.o thread.o timers.o i18n.o \ message.o messagequeue.o \ vdr.o recording.o channel.o rectimer.o event.o directory.o \ - player.o demuxer.o stream.o vfeed.o afeed.o afeedr.o draintarget.o \ + player.o vfeed.o afeed.o afeedr.o \ + demuxer.o demuxervdr.o stream.o draintarget.o \ viewman.o box.o region.o colour.o view.o \ vinfo.o vwallpaper.o vvolume.o vrecordinglist.o vlivebanner.o vmute.o \ vtimerlist.o vtimeredit.o voptionsmenu.o vrecordingmenu.o vquestion.o \ diff --git a/demuxer.cc b/demuxer.cc index c379d93..e251e83 100644 --- a/demuxer.cc +++ b/demuxer.cc @@ -21,6 +21,12 @@ #include "demuxer.h" #include +#if __BYTE_ORDER == __BIG_ENDIAN +#define DEMUXER_SEQ_HEAD 0x000001B3 +#else +#define DEMUXER_SEQ_HEAD 0xB3010000 +#endif + const int Demuxer::FrameRates[9] = { 0, 23, 24, 25, 29, 30, 50, 59, 60 }; Demuxer* Demuxer::instance = NULL; @@ -29,7 +35,7 @@ Demuxer::Demuxer() { if (instance) return; instance = this; - initted = 0; + initted = false; callback = NULL; arcnt = 0; } @@ -50,8 +56,7 @@ int Demuxer::init(Callback* tcallback) if (!initted) { if ( !videostream.init(demuxMemoryV) || - !audiostream.init(demuxMemoryA) || - !(local_frame = (UCHAR *) malloc(0x10000))) + !audiostream.init(demuxMemoryA)) { Log::getInstance()->log("Demuxer", Log::CRIT, "Failed to initialize demuxer"); shutdown(); @@ -64,7 +69,7 @@ int Demuxer::init(Callback* tcallback) } reset(); - initted = 1; + initted = true; callback = tcallback; return 1; } @@ -82,8 +87,7 @@ int Demuxer::shutdown() { videostream.shutdown(); audiostream.shutdown(); - free(local_frame); - initted = 0; + initted = false; return 1; } @@ -91,7 +95,6 @@ void Demuxer::flush() { videostream.flush(); audiostream.flush(); - state_frametype = state_framepos = 0; } void Demuxer::flushAudio() @@ -101,7 +104,7 @@ void Demuxer::flushAudio() void Demuxer::seek() { - vid_seeking = aud_seeking = 1; + vid_seeking = aud_seeking = true; video_pts = audio_pts = 0; } @@ -152,446 +155,137 @@ int Demuxer::writeVideo(DrainTarget* dt) } #endif - -int Demuxer::scan(UCHAR *buf, int len) +Demuxer::PESPacket::PESPacket() { - // Temporarily, just look for the lowest audio stream and return it - int ret = 0; - UCHAR byte; - int zeros = 0; - while (len > 1) - { - // We are searching for a string of bytes (0,0,1). - byte = *(buf++); --len; - - if (byte == 0) - { - ++zeros; continue; - } - if (zeros < 2 || byte != 1) - { - zeros = 0; continue; - } - zeros = 0; - // We have found the pattern (0,0,1). - // Check the next byte for the sub-frame type. - byte = *(buf++); --len; - if (byte >= 0xc0 && byte <= 0xdf) // Audio - if (ret == 0 || ret > byte) ret = byte; - } - return ret; + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x01; + init(0); } -int Demuxer::put(UCHAR* buf, int len) +void Demuxer::PESPacket::init(UCHAR type) { - int ret = 0; // return number of bytes consumed - int parsed = 0; // number of bytes parsed by sub-function - int full; // sub-function sets this to tell us to exit early - inbuf = buf; // Initialize buffer pointer - - while (len) - { - full = 0; - - if (state_frametype == 0) // Search for frame - { - parsed = parse_find_frame(len); - } - else if ((state_frametype >= FRAMETYPE_VID0) && (state_frametype <= FRAMETYPE_VIDMAX)) - { - parsed = parse_video_frame(len, &full); - } - else if ((state_frametype >= FRAMETYPE_AUD0) && (state_frametype <= FRAMETYPE_AUDMAX)) - { - parsed = parse_audio_frame(len, &full); - } - else if (state_frametype == FRAMETYPE_PRIVATE_1) - { - parsed = parse_private1_frame(len, &full); - } - - ret += parsed; len -= parsed; - if (full) // We have to exit early. - break; // out of while loop - } - //Log::getInstance()->log("Demuxer", Log::DEBUG, "Put %d; took %d", ret + len, ret); - return ret; + length = submitted = 0; + size = 6; closed = false; + data[3] = type; + data[4] = data[5] = 0; + packetType = type; } -int Demuxer::parse_find_frame(int len) +int Demuxer::PESPacket::write(UCHAR *buf, int len) { - int ret = 0; // return number of bytes parsed - UCHAR byte; - - // In this function, state_framepos represents - // the number of fixed header bytes found so far. - if (state_framepos > 3 || state_framepos < 0) - { - // ERROR! - state_framepos = 0; - } - - while (len) - { - byte = *inbuf++; - ++ret; --len; - switch (state_framepos) - { - case 0: - case 1: - if (byte == 0) - ++state_framepos; - else - state_framepos = 0; - break; - case 2: - if (byte == 1) - ++state_framepos; - else if (byte != 0) - state_framepos = 0; - break; - default: - state_framepos = 0; // Set initial state for the new frame - - if (byte == 0) - { - state_framepos = 1; // Count this as a first header byte! - } - else if ( ((byte >= FRAMETYPE_VID0) && (byte <= FRAMETYPE_VIDMAX)) - || ((byte >= FRAMETYPE_AUD0) && (byte <= FRAMETYPE_AUDMAX)) - || byte==FRAMETYPE_PRIVATE_1 ) - { - state_frametype = byte; - return ret; - } - - // Not a recognised frame type. Go back to Old Kent Road. - break; - } - } - return ret; + if (closed) return 0; + if (length + len > 0xFFFA) return 0; + memcpy(data+length+6, buf, len); + length += len; + size += len; + data[4] = (length >> 8); + data[5] = (length & 0xFF); + return 1; } -int Demuxer::parse_video_frame(int len, int* full) +int Demuxer::PESPacket::submit() { - int ret = 0; // return number of bytes consumed - int bytes_remaining; + if (submitted >= size) return 0; + if (!closed) parseDetails(); - switch(state_framepos) - { - case 0: // Brand new video frame. Set initial states. - state_stream_fill = 0; state_vid_parsed = 0; - // Create a local copy of the frame header - local_frame[0] = local_frame[1] = 0; local_frame[2] = 1; - local_frame[3] = state_frametype; - // If no video stream has been set, use this one. - if (video_current == -1) video_current = state_frametype; - // Get MSB of frame length and copy to local frame. - frame_length = *inbuf << 8; - local_frame[4] = *inbuf; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - // FALL THROUGH TO NEXT BYTE IN STREAM - case 1: // Get LSB of frame length and copy to local frame. - frame_length += *inbuf; - local_frame[5] = *inbuf; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - } - // We are in the frame data - bytes_remaining = 2 + frame_length - state_framepos; - if (video_current != state_frametype) + closed = true; + Demuxer* dx = Demuxer::getInstance(); + int sent; + if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX) { - // We don't want this frame. Throw it away. - if (len >= bytes_remaining) - { - inbuf += bytes_remaining; - ret += bytes_remaining; - state_frametype = state_framepos = 0; - return ret; - } + if (dx->video_current == -1) dx->video_current = packetType; + if (dx->video_current == packetType && !dx->vid_seeking) + sent = dx->videostream.put(data+submitted, size-submitted); else - { - inbuf += len; ret += len; - state_framepos += len; - return ret; - } - } // No fall through here - - if (bytes_remaining) // There is data yet to copy to local_frame - { - if (len > bytes_remaining) len = bytes_remaining; - memcpy(local_frame + state_framepos + 4, inbuf, len); - inbuf += len; ret += len; state_framepos += len; - if (len < bytes_remaining) // Not all arrived yet - return ret; - parse_video_details(local_frame+6, frame_length); + sent = size-submitted; } - - if (!vid_seeking) + else if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX) { - // We have the whole frame in local_frame. Send it to the stream. - // We still support streams that might not consume all the data. - state_stream_fill += videostream.put(local_frame, - 6 + frame_length - state_stream_fill); - if (state_stream_fill < frame_length + 6) // stream is full! - { - *full = 1; return ret; - } - } - state_frametype = state_framepos = 0; - return ret; -} - -int Demuxer::parse_audio_frame(int len, int* full) -{ - int ret = 0; // return number of bytes consumed - int bytes_remaining; - - switch(state_framepos) - { - case 0: // Brand new audio frame. Set initial states. - state_stream_fill = 0; - // Create a local copy of the frame header - local_frame[0] = local_frame[1] = 0; local_frame[2] = 1; - local_frame[3] = state_frametype; - // If no audio stream has been set, use this one. - if (audio_current == -1) audio_current = state_frametype; - // Get MSB of frame length and copy to local frame. - frame_length = *inbuf << 8; - local_frame[4] = *inbuf; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - // FALL THROUGH TO NEXT BYTE IN STREAM - case 1: // Get LSB of frame length and copy to local frame. - frame_length += *inbuf; - local_frame[5] = *inbuf; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - } - // We are in the frame data - bytes_remaining = 2 + frame_length - state_framepos; - if (audio_current != state_frametype) - { - // We don't want this frame. Throw it away. - if (len >= bytes_remaining) - { - inbuf += bytes_remaining; - ret += bytes_remaining; - state_frametype = state_framepos = 0; - return ret; - } + if (dx->audio_current == -1) dx->audio_current = packetType; + if (dx->audio_current == packetType & !dx->aud_seeking) + sent = dx->audiostream.put(data+submitted, size-submitted); else - { - inbuf += len; ret += len; - state_framepos += len; - return ret; - } - } // No fall through is allowed here - - if (bytes_remaining) // There is data yet to copy to local_frame - { - if (len > bytes_remaining) len = bytes_remaining; - memcpy(local_frame + state_framepos + 4, inbuf, len); - inbuf += len; ret += len; state_framepos += len; - if (len < bytes_remaining) // Not all arrived yet - return ret; - parse_audio_details(local_frame+6, frame_length); + sent = size-submitted; } - - if (!aud_seeking) + else { - // We have the whole frame in local_frame. Send it to the stream. - // We still support streams that might not consume all the data. - state_stream_fill += audiostream.put(local_frame, - 6 + frame_length - state_stream_fill); - if (state_stream_fill < frame_length + 6) // stream is full! - { - *full = 1; return ret; - } + sent = size-submitted; } - state_frametype = state_framepos = 0; - return ret; -} - -int Demuxer::parse_private1_frame(int len, int* full) -{ - int ret = 0; // return number of bytes consumed - int bytes_remaining; - switch(state_framepos) - { - case 0: // Brand new frame. Set initial states. - // Get MSB of frame length and copy to local frame. - frame_length = *inbuf << 8; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - // FALL THROUGH TO NEXT BYTE IN STREAM - case 1: // Get LSB of frame length and copy to local frame. - frame_length += *inbuf; - local_frame[5] = *inbuf; - ++inbuf; ++state_framepos; ++ret; --len; - if (len == 0) return ret; - } - // We are in the frame data - bytes_remaining = 2 + frame_length - state_framepos; - // Temporary - just discard the frame. - if (len >= bytes_remaining) - { - inbuf += bytes_remaining; - ret += bytes_remaining; - state_frametype = state_framepos = 0; - return ret; - } + submitted += sent; + if (submitted < size) // Stream is full. + return 0; else - { - inbuf += len; ret += len; - state_framepos += len; - return ret; - } + return 1; } -void Demuxer::parse_audio_details(UCHAR* buf, int len) +void Demuxer::PESPacket::parseDetails() { - // Extract audio PTS if it exists - if ( buf[1] & 0x80 ) // PTS_DTS_flags indicate that PTS is present + Demuxer* dx = Demuxer::getInstance(); + if (packetType >= PESTYPE_AUD0 && packetType <= PESTYPE_AUDMAX) { - audio_pts = ( (ULLONG)(buf[3] & 0x0E) << 29 ) | - ( (ULLONG)(buf[4]) << 22 ) | - ( (ULLONG)(buf[5] & 0xFE) << 14 ) | - ( (ULLONG)(buf[6]) << 7 ) | - ( (ULLONG)(buf[7] & 0xFE) >> 1 ); - if (aud_seeking && !vid_seeking && audio_pts >= video_pts_seek) + // Extract audio PTS if it exists + if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present { - aud_seeking = 0; - Log::getInstance()->log("Demuxer", Log::DEBUG, - "Leaving audio seek: Audio PTS = %llu", audio_pts); + dx->audio_pts = ( (ULLONG)(data[9] & 0x0E) << 29 ) | + ( (ULLONG)(data[10]) << 22 ) | + ( (ULLONG)(data[11] & 0xFE) << 14 ) | + ( (ULLONG)(data[12]) << 7 ) | + ( (ULLONG)(data[13] & 0xFE) >> 1 ); + if (dx->aud_seeking && !dx->vid_seeking && + dx->audio_pts >= dx->video_pts_seek) + { + dx->aud_seeking = 0; + Log::getInstance()->log("Demuxer", Log::DEBUG, + "Leaving audio sync: Audio PTS = %llu", dx->audio_pts); + } } } -} - -void Demuxer::parse_video_details(UCHAR* buf, int len) -{ - // Extract video PTS if it exists - if ( buf[1] & 0x80 ) // PTS_DTS_flags indicate that PTS is present + else if (packetType >= PESTYPE_VID0 && packetType <= PESTYPE_VIDMAX) { - video_pts = ( (ULLONG)(buf[3] & 0x0E) << 29 ) | - ( (ULLONG)(buf[4]) << 22 ) | - ( (ULLONG)(buf[5] & 0xFE) << 14 ) | - ( (ULLONG)(buf[6]) << 7 ) | - ( (ULLONG)(buf[7] & 0xFE) >> 1 ); - } - // Now, scan for a GOP header and extract video information - UCHAR byte; - int zeros = 0; - while (len >= 8) // 8 is length of a GOP header - { - // We are searching for a string of bytes (0,0,1). - byte = *(buf++); --len; - - if (byte == 0) + // Extract video PTS if it exists + if ( data[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present { - ++zeros; continue; + dx->video_pts = ( (ULLONG)(data[9] & 0x0E) << 29 ) | + ( (ULLONG)(data[10]) << 22 ) | + ( (ULLONG)(data[11] & 0xFE) << 14 ) | + ( (ULLONG)(data[12]) << 7 ) | + ( (ULLONG)(data[13] & 0xFE) >> 1 ); } - if (zeros < 2 || byte != 1) - { - zeros = 0; continue; - } - zeros = 0; - // We have found the pattern (0,0,1). - // Check the next byte for the sub-frame type. - byte = *(buf++); --len; - switch (byte) + + // Now, scan for a sequence header and extract information + UINT pos = 9; // Start searching from byte 9 + while (pos < size - 5) { -/* case 0x00: // Picture header - // 10 bits: temporal reference - // 3 bits: coding type (I/P/B) - // ... - if (len < 2) return; - if ( (buf[1] & 0x38) == 0x08 ) // I-frame - seeking = 0; - buf += 4; // Minimum length of picture header - len -= 4; - break; -*/ - case 0xb3: // Sequence header - // 12 bits: Horizontal size - // 12 bits: Vertical size - // 4 bits: Aspect ratio - // 4 bits: Frame rate code - // 18 bits: Bit rate value - // ... - if (len < 7) return; - horizontal_size = ((int)buf[0] << 4) | ((int)buf[1] >> 4); - vertical_size = (((int)buf[1] & 0xf) << 8) | (int)buf[2]; - setAspectRatio((enum AspectRatio)(buf[3] >> 4)); - frame_rate = buf[3] & 0x0f; - if (frame_rate >= 1 && frame_rate <= 8) - frame_rate = FrameRates[frame_rate]; + UINT pattern = *(UINT*)(data+pos); + if (pattern == DEMUXER_SEQ_HEAD) + { + pos += 4; + if (pos+6 >= size) return; + dx->horizontal_size = ((int)data[pos] << 4) | ((int)data[pos+1] >> 4); + dx->vertical_size = (((int)data[pos+1] & 0xf) << 8) | (int)data[pos+2]; + dx->setAspectRatio((enum AspectRatio)(data[pos+3] >> 4)); + dx->frame_rate = data[pos+3] & 0x0f; + if (dx->frame_rate >= 1 && dx->frame_rate <= 8) + dx->frame_rate = FrameRates[dx->frame_rate]; else - frame_rate = 0; - bit_rate = ((int)buf[4] << 10) | - ((int)buf[5] << 2) | - ((int)buf[6] >> 6); - if (vid_seeking) + dx->frame_rate = 0; + dx->bit_rate = ((int)data[pos+4] << 10) | + ((int)data[pos+5] << 2) | + ((int)data[pos+6] >> 6); + if (dx->vid_seeking) { - vid_seeking = 0; - video_pts_seek = video_pts; + dx->vid_seeking = 0; + dx->video_pts_seek = dx->video_pts; Log::getInstance()->log("Demuxer", Log::DEBUG, - "Entering video seek: Video PTS = %llu", video_pts); + "Entering audio sync: Video PTS = %llu", dx->video_pts); Log::getInstance()->log("Demuxer", Log::DEBUG, - "Entering video seek: Audio PTS = %llu", audio_pts); + "Entering audio sync: Audio PTS = %llu", dx->audio_pts); } - buf += 8; // Minimum length of sequence header - len -= 8; - break; -/* - case 0xb8: // Group header - // We're not going to bother parsing anything. - seeking = 0; - buf += 4; // Minimum length of group header - len -= 4; - break; -*/ - } - } -} - -int Demuxer::findVideoPTS(UCHAR* buf, int len, ULLONG* dest) -{ - while (len >= 14) - { - UINT pattern = *(UINT*)buf; - buf++; len--; -#if __BYTE_ORDER == __BIG_ENDIAN - if (pattern < (0x100 | FRAMETYPE_VID0) || - pattern > (0x100 | FRAMETYPE_VIDMAX)) continue; -#else - if ((pattern & 0xFFFFFF) != 0x010000 || - pattern < ((UINT)FRAMETYPE_VID0 << 24) || - pattern > ((UINT)FRAMETYPE_VIDMAX << 24)) continue; -#endif - if ((buf[5] & 0xC0) != 0x80) continue; - - UINT framelength = ((UINT)buf[3] << 8) | buf[4]; - - if ( buf[6] & 0x80 ) // PTS_DTS_flags indicate that PTS is present - { - if ( (buf[8] & 0x01) != 0x01 || - (buf[10] & 0x01) != 0x01 || - (buf[12] & 0x01) != 0x01) continue; - - *dest = ( (ULLONG)(buf[8] & 0x0E) << 29 ) | - ( (ULLONG)(buf[9]) << 22 ) | - ( (ULLONG)(buf[10] & 0xFE) << 14 ) | - ( (ULLONG)(buf[11]) << 7 ) | - ( (ULLONG)(buf[12] & 0xFE) >> 1 ); - return 1; + return; + } + else pos++; } - - buf += 5; len -= 5; - buf += framelength; len -= framelength; } - // No PTS found. - return 0; } diff --git a/demuxer.h b/demuxer.h index ff7e90b..e02c989 100644 --- a/demuxer.h +++ b/demuxer.h @@ -41,16 +41,32 @@ however, no code was copied verbatim. #include "audio.h" #include "video.h" - class Demuxer { + class PESPacket + { + public: + PESPacket(); + void init(UCHAR type); + int write(UCHAR* buf, int len); + int submit(); + private: + UCHAR data[0x10000]; + UINT length, size; + bool closed; + UCHAR packetType; + UINT submitted; + void parseDetails(); + }; + friend class PESPacket; + public: Demuxer(); - ~Demuxer(); + virtual ~Demuxer(); static Demuxer* getInstance(); int init(Callback* callback); void reset(); - void flush(); + virtual void flush(); void flushAudio(); void seek(); void setVideoStream(int id); @@ -59,13 +75,13 @@ class Demuxer int writeAudio(int fd); int writeVideo(int fd); #else - int writeAudio(DrainTarget* dt); + int writeAudio(DrainTarget* dt); int writeVideo(DrainTarget* dt); #endif - int scan(UCHAR* buf, int len); - int findVideoPTS(UCHAR* buf, int len, ULLONG* dest); - int put(UCHAR* buf, int len); + virtual int scan(UCHAR* buf, int len) = 0; + virtual int findVideoPTS(UCHAR* buf, int len, ULLONG* dest) = 0; + virtual int put(UCHAR* buf, int len) = 0; int getHorizontalSize() { return horizontal_size; } int getVerticalSize() { return vertical_size; } @@ -80,92 +96,21 @@ class Demuxer ASPECT_16_9 = 3 }; - private: + protected: + // General demuxer objects and status indicators static Demuxer* instance; Stream videostream; Stream audiostream; int shutdown(); - int initted; - int vid_seeking; - int aud_seeking; - UCHAR* inbuf; + bool initted; + bool vid_seeking; + bool aud_seeking; + int video_current, audio_current; + // Video stream information void setAspectRatio(enum AspectRatio); Callback* callback; - int video_current, audio_current; - int state_frametype, state_framepos; - int state_stream_fill, state_vid_parsed; - int frame_length; - int parse_find_frame(int len); - int parse_video_frame(int len, int* full); - int parse_audio_frame(int len, int* full); - int parse_private1_frame(int len, int* full); - void parse_video_details(UCHAR* buf, int len); - void parse_audio_details(UCHAR* buf, int len); - - UCHAR* local_frame; - static const int demuxMemoryV = 2097152; - static const int demuxMemoryA = 524288; - static const int FrameRates[9]; - - enum FRAMETYPE - { - FRAMETYPE_PRIVATE_1 = 0xbd, - - FRAMETYPE_AUD0 = 0xc0, - FRAMETYPE_AUD1, - FRAMETYPE_AUD2, - FRAMETYPE_AUD3, - FRAMETYPE_AUD4, - FRAMETYPE_AUD5, - FRAMETYPE_AUD6, - FRAMETYPE_AUD7, - FRAMETYPE_AUD8, - FRAMETYPE_AUD9, - FRAMETYPE_AUD10, - FRAMETYPE_AUD11, - FRAMETYPE_AUD12, - FRAMETYPE_AUD13, - FRAMETYPE_AUD14, - FRAMETYPE_AUD15, - FRAMETYPE_AUD16, - FRAMETYPE_AUD17, - FRAMETYPE_AUD18, - FRAMETYPE_AUD19, - FRAMETYPE_AUD20, - FRAMETYPE_AUD21, - FRAMETYPE_AUD22, - FRAMETYPE_AUD23, - FRAMETYPE_AUD24, - FRAMETYPE_AUD25, - FRAMETYPE_AUD26, - FRAMETYPE_AUD27, - FRAMETYPE_AUD28, - FRAMETYPE_AUD29, - FRAMETYPE_AUD30, - FRAMETYPE_AUD31, - FRAMETYPE_AUDMAX = FRAMETYPE_AUD31, - - FRAMETYPE_VID0 = 0xe0, - FRAMETYPE_VID1, - FRAMETYPE_VID2, - FRAMETYPE_VID3, - FRAMETYPE_VID4, - FRAMETYPE_VID5, - FRAMETYPE_VID6, - FRAMETYPE_VID7, - FRAMETYPE_VID8, - FRAMETYPE_VID9, - FRAMETYPE_VID10, - FRAMETYPE_VID11, - FRAMETYPE_VID12, - FRAMETYPE_VID13, - FRAMETYPE_VID14, - FRAMETYPE_VID15, - FRAMETYPE_VIDMAX = FRAMETYPE_VID15 - }; - int horizontal_size; int vertical_size; enum AspectRatio aspect_ratio; @@ -175,6 +120,35 @@ class Demuxer ULLONG video_pts; ULLONG video_pts_seek; ULLONG audio_pts; + + // Constants + static const int demuxMemoryV = 2097152; + static const int demuxMemoryA = 524288; + static const int FrameRates[9]; + + enum PESTYPE + { + PESTYPE_PRIVATE_1 = 0xBD, + + PESTYPE_AUD0 = 0xC0, + PESTYPE_AUD1, PESTYPE_AUD2, PESTYPE_AUD3, PESTYPE_AUD4, + PESTYPE_AUD5, PESTYPE_AUD6, PESTYPE_AUD7, PESTYPE_AUD8, + PESTYPE_AUD9, PESTYPE_AUD10, PESTYPE_AUD11, PESTYPE_AUD12, + PESTYPE_AUD13, PESTYPE_AUD14, PESTYPE_AUD15, PESTYPE_AUD16, + PESTYPE_AUD17, PESTYPE_AUD18, PESTYPE_AUD19, PESTYPE_AUD20, + PESTYPE_AUD21, PESTYPE_AUD22, PESTYPE_AUD23, PESTYPE_AUD24, + PESTYPE_AUD25, PESTYPE_AUD26, PESTYPE_AUD27, PESTYPE_AUD28, + PESTYPE_AUD29, PESTYPE_AUD30, PESTYPE_AUD31, + PESTYPE_AUDMAX = PESTYPE_AUD31, + + PESTYPE_VID0 = 0xE0, + PESTYPE_VID1, PESTYPE_VID2, PESTYPE_VID3, PESTYPE_VID4, + PESTYPE_VID5, PESTYPE_VID6, PESTYPE_VID7, PESTYPE_VID8, + PESTYPE_VID9, PESTYPE_VID10, PESTYPE_VID11, PESTYPE_VID12, + PESTYPE_VID13, PESTYPE_VID14, PESTYPE_VID15, + PESTYPE_VIDMAX = PESTYPE_VID15 + }; + }; #endif diff --git a/demuxervdr.cc b/demuxervdr.cc new file mode 100644 index 0000000..a278456 --- /dev/null +++ b/demuxervdr.cc @@ -0,0 +1,192 @@ +/* + Copyright 2005-2006 Mark Calderbank + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "demuxervdr.h" +#include + +DemuxerVDR::DemuxerVDR() {} + +void DemuxerVDR::flush() +{ + state = 0; + submitting = false; + Demuxer::flush(); +} + +int DemuxerVDR::scan(UCHAR *buf, int len) +{ + // Temporarily, just look for the lowest audio stream and return it + UCHAR HiByte = PESTYPE_AUDMAX; + int ret = 0; + + while (len >= 4) + { + UINT pattern = *(UINT*)buf; + buf++; len--; + +#if __BYTE_ORDER == __BIG_ENDIAN + if (pattern < (UINT)(0x100 | PESTYPE_AUD0) || + pattern > (UINT)(0x100 | HiByte)) continue; + HiByte = pattern & 0xFF; +#else + if ((pattern & 0xFFFFFF) != 0x010000 || + pattern < ((UINT)PESTYPE_AUD0 << 24) || + pattern > ((UINT)HiByte << 24)) continue; + HiByte = pattern >> 24; +#endif + ret = HiByte; + if (HiByte == PESTYPE_AUD0) break; + } + return ret; +} + +int DemuxerVDR::findVideoPTS(UCHAR* buf, int len, ULLONG* dest) +{ + while (len >= 14) + { + UINT pattern = *(UINT*)buf; + buf++; len--; +#if __BYTE_ORDER == __BIG_ENDIAN + if (pattern < (0x100 | PESTYPE_VID0) || + pattern > (0x100 | PESTYPE_VIDMAX)) continue; +#else + if ((pattern & 0xFFFFFF) != 0x010000 || + pattern < ((UINT)PESTYPE_VID0 << 24) || + pattern > ((UINT)PESTYPE_VIDMAX << 24)) continue; +#endif + if ((buf[5] & 0xC0) != 0x80) continue; + + UINT packetlength = ((UINT)buf[3] << 8) | buf[4]; + + if ( buf[6] & 0x80 ) // PTS_DTS_flags indicate that PTS is present + { + if ( (buf[8] & 0x01) != 0x01 || + (buf[10] & 0x01) != 0x01 || + (buf[12] & 0x01) != 0x01) continue; + + *dest = ( (ULLONG)(buf[8] & 0x0E) << 29 ) | + ( (ULLONG)(buf[9]) << 22 ) | + ( (ULLONG)(buf[10] & 0xFE) << 14 ) | + ( (ULLONG)(buf[11]) << 7 ) | + ( (ULLONG)(buf[12] & 0xFE) >> 1 ); + return 1; + } + + buf += 5; len -= 5; + buf += packetlength; len -= packetlength; + } + // No PTS found. + return 0; +} + +int DemuxerVDR::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + + if (submitting) + { + if (packet.submit() == 0) // Still full! + return ret; + else + submitting = false; + } + + if (state > 0) // We are half way through a PES packet. + { + if (len >= state) // The remainder of the packet is available. + { + packet.write(buf, state); + buf += state; len -= state; ret += state; + state = 0; + if (packet.submit() == 0) // Stream is full. + { + submitting = true; + return ret; + } + } + else // Write what we have, then exit. + { + packet.write(buf, len); + state -= len; + return len; + } + } + + while (len >= 0) + { + switch (state) + { + case 0: + case -1: + if (*buf == 0x00) state--; else state = 0; + break; + case -2: + if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0; + break; + case -3: + if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) || + (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) || + (*buf == PESTYPE_PRIVATE_1)) + { + packet.init(*buf); + state--; + break; + } + else if (*buf == 0x00) + state = -1; + else + state = 0; + break; + case -4: + packetLength = ((UINT)*buf) << 8; + state--; + break; + case -5: + packetLength += *buf; + state--; + break; + } + + buf++; len--; ret++; + + if (state == -6) // Packet header complete + { + if (len >= packetLength) // The entire packet is available. + { + packet.write(buf, packetLength); + buf += packetLength; len -= packetLength; ret += packetLength; + state = 0; + if (packet.submit() == 0) // Stream is full. + { + submitting = true; + return ret; + } + } + else // Write what we have. + { + packet.write(buf, len); + state = packetLength - len; + ret += len; + len = 0; + } + } + } + return ret; +} diff --git a/demuxervdr.h b/demuxervdr.h new file mode 100644 index 0000000..1f2d90e --- /dev/null +++ b/demuxervdr.h @@ -0,0 +1,43 @@ +/* + Copyright 2005-2006 Mark Calderbank + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef DEMUXERVDR_H +#define DEMUXERVDR_H + +#include "demuxer.h" +#include "defines.h" + +class DemuxerVDR : public Demuxer +{ + public: + DemuxerVDR(); + void flush(); + int scan(UCHAR* buf, int len); + int findVideoPTS(UCHAR* buf, int len, ULLONG* dest); + int put(UCHAR* buf, int len); + + private: + int state; + bool submitting; + int packetLength; + PESPacket packet; +}; + +#endif diff --git a/player.cc b/player.cc index ecdaf36..0449c13 100644 --- a/player.cc +++ b/player.cc @@ -63,13 +63,18 @@ Player::Player(MessageQueue* messageQueue, bool tIsRecording, bool tIsRadio) Player::~Player() { if (initted) shutdown(); + if (demuxer != NULL) delete demuxer; } int Player::init() { if (initted) return 0; - if (!demuxer.init(this)) + demuxer = new DemuxerVDR; + + if (demuxer == NULL) return 0; + + if (!demuxer->init(this)) { logger->log("Player", Log::ERR, "Demuxer failed to init"); shutdown(); @@ -105,10 +110,9 @@ int Player::shutdown() video->blank(); audio->stop(); vfeed.stop(); - afeed.stop(); video->reset(); - demuxer.reset(); + demuxer->reset(); feedPosition = 0; } @@ -150,8 +154,8 @@ int Player::play() audio->reset(); video->reset(); - demuxer.reset(); - if (!isRadio) demuxer.seek(); + demuxer->reset(); + if (!isRadio) demuxer->seek(); videoStartup = true; threadStart(); @@ -207,7 +211,7 @@ void Player::stop() audio->stop(); audio->unPause(); video->reset(); - demuxer.reset(); + demuxer->reset(); feedPosition = 0; } @@ -265,8 +269,8 @@ void Player::restartAt(ULLONG timecode) video->stop(); video->reset(); audio->reset(); - demuxer.flush(); - if (!isRadio) demuxer.seek(); + demuxer->flush(); + if (!isRadio) demuxer->seek(); feedPosition = newPosition; videoStartup = true; afeed.start(); @@ -322,8 +326,8 @@ void Player::toggleFastForward() audio->stop(); video->reset(); audio->reset(); - demuxer.flush(); - if (!isRadio) demuxer.seek(); + demuxer->flush(); + if (!isRadio) demuxer->seek(); videoStartup = true; afeed.enable(); @@ -377,8 +381,8 @@ void Player::toggleFastBackward() feedMode = MODE_BACKWARDS; video->reset(); video->play(); - demuxer.flush(); - if (!isRadio) demuxer.seek(); + demuxer->flush(); + if (!isRadio) demuxer->seek(); threadStart(); } @@ -401,7 +405,7 @@ ULLONG Player::getEndTS() void Player::call(void* caller) { - if (caller == &demuxer) + if (caller == demuxer) { logger->log("Player", Log::DEBUG, "Callback from demuxer"); @@ -411,7 +415,7 @@ void Player::call(void* caller) return; } - int dxCurrentAspect = demuxer.getAspectRatio(); + int dxCurrentAspect = demuxer->getAspectRatio(); if (dxCurrentAspect == Demuxer::ASPECT_4_3) { logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV"); @@ -518,8 +522,8 @@ void Player::threadMethod() if (startup) { - int a_stream = demuxer.scan(threadBuffer, thisRead); - demuxer.setAudioStream(a_stream); + int a_stream = demuxer->scan(threadBuffer, thisRead); + demuxer->setAudioStream(a_stream); logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); setStartTS(thisRead); @@ -558,7 +562,7 @@ void Player::threadMethod() if (feedPosition >= blockSize) { feedPosition -= blockSize; - if (!isRadio) demuxer.seek(); + if (!isRadio) demuxer->seek(); } else { @@ -571,14 +575,14 @@ void Player::threadMethod() while(writeLength < thisRead) { - thisWrite = demuxer.put(threadBuffer + writeLength, thisRead - writeLength); + thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); writeLength += thisWrite; // logger->log("Player", Log::DEBUG, "Put %i to demuxer", thisWrite); if (!thisWrite) { // logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!"); - // demuxer is full and cant take anymore + // demuxer is full and can't take any more threadLock(); threadWaitForSignal(); threadUnlock(); @@ -626,12 +630,12 @@ void Player::setStartTS(UINT dataInBuffer) UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead); if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; } if (!tempBuffer) return; - if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &startTS); + if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &startTS); free(tempBuffer); } else { - demuxer.findVideoPTS(threadBuffer, dataInBuffer, &startTS); + demuxer->findVideoPTS(threadBuffer, dataInBuffer, &startTS); } } @@ -643,7 +647,7 @@ void Player::setEndTS() UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead); if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; } if (!tempBuffer) return; - if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &endTS); + if (thisRead) demuxer->findVideoPTS(tempBuffer, thisRead, &endTS); free(tempBuffer); logger->log("Player", Log::DEBUG, "Set end TS"); } diff --git a/player.h b/player.h index 75ff782..6b632b0 100644 --- a/player.h +++ b/player.h @@ -25,7 +25,7 @@ #include "audio.h" #include "video.h" -#include "demuxer.h" +#include "demuxervdr.h" #include "vfeed.h" #include "afeed.h" #include "remote.h" @@ -86,7 +86,7 @@ class Player : public Thread_TYPE, public Callback Log* logger; Audio* audio; Video* video; - Demuxer demuxer; + Demuxer* demuxer; VFeed vfeed; AFeed afeed; -- 2.39.2