#include "demuxer.h"
#include <endian.h>
+#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;
{
if (instance) return;
instance = this;
- initted = 0;
+ initted = false;
callback = NULL;
arcnt = 0;
}
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();
}
reset();
- initted = 1;
+ initted = true;
callback = tcallback;
return 1;
}
{
videostream.shutdown();
audiostream.shutdown();
- free(local_frame);
- initted = 0;
+ initted = false;
return 1;
}
{
videostream.flush();
audiostream.flush();
- state_frametype = state_framepos = 0;
}
void Demuxer::flushAudio()
void Demuxer::seek()
{
- vid_seeking = aud_seeking = 1;
+ vid_seeking = aud_seeking = true;
video_pts = audio_pts = 0;
}
}
#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;
}
#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);
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; }
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;
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
--- /dev/null
+/*
+ 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 <endian.h>
+
+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;
+}
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();
video->blank();
audio->stop();
vfeed.stop();
-
afeed.stop();
video->reset();
- demuxer.reset();
+ demuxer->reset();
feedPosition = 0;
}
audio->reset();
video->reset();
- demuxer.reset();
- if (!isRadio) demuxer.seek();
+ demuxer->reset();
+ if (!isRadio) demuxer->seek();
videoStartup = true;
threadStart();
audio->stop();
audio->unPause();
video->reset();
- demuxer.reset();
+ demuxer->reset();
feedPosition = 0;
}
video->stop();
video->reset();
audio->reset();
- demuxer.flush();
- if (!isRadio) demuxer.seek();
+ demuxer->flush();
+ if (!isRadio) demuxer->seek();
feedPosition = newPosition;
videoStartup = true;
afeed.start();
audio->stop();
video->reset();
audio->reset();
- demuxer.flush();
- if (!isRadio) demuxer.seek();
+ demuxer->flush();
+ if (!isRadio) demuxer->seek();
videoStartup = true;
afeed.enable();
feedMode = MODE_BACKWARDS;
video->reset();
video->play();
- demuxer.flush();
- if (!isRadio) demuxer.seek();
+ demuxer->flush();
+ if (!isRadio) demuxer->seek();
threadStart();
}
void Player::call(void* caller)
{
- if (caller == &demuxer)
+ if (caller == demuxer)
{
logger->log("Player", Log::DEBUG, "Callback from demuxer");
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");
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);
if (feedPosition >= blockSize)
{
feedPosition -= blockSize;
- if (!isRadio) demuxer.seek();
+ if (!isRadio) demuxer->seek();
}
else
{
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();
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);
}
}
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");
}