/* * Copyright (c) 2004-2007 Marcus Overhagen * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include "TransportStreamDemux.h" #include "Packet.h" #include "PacketQueue.h" #define TRACE_TS_DEMUX #ifdef TRACE_TS_DEMUX #define TRACE printf #else #define TRACE(a...) #endif #define CLOCK_TO_USEC(clck) (bigtime_t((clck) + 13) / 27) #define USEC_TO_CLOCK(usec) (bigtime_t((usec) * 27)) TransportStreamDemux::TransportStreamDemux( PacketQueue *vid_queue, PacketQueue *aud_queue, PacketQueue *vid_queue2, PacketQueue *aud_queue2, PacketQueue *mpeg_ts_queue) : fCount(0) , fSystemTime(0) , fPerformanceTime(0) , fLastEndTime(0) , fVidPid(-1) , fAudPid(-1) , fPcrPid(-1) , fVidQueue(vid_queue) , fVidQueue2(vid_queue2) , fAudQueue(aud_queue) , fAudQueue2(aud_queue2) , fMpegTsQueue(mpeg_ts_queue) , fVidPacket(new Packet) , fAudPacket(new Packet) , fVidPacketValid(false) , fAudPacketValid(false) { } TransportStreamDemux::~TransportStreamDemux() { delete fVidPacket; delete fAudPacket; } void TransportStreamDemux::SetPIDs(int vid_pid, int aud_pid, int pcr_pid) { fVidPacket->MakeEmpty(); fAudPacket->MakeEmpty(); fVidPacketValid = false; fAudPacketValid = false; fVidPid = vid_pid; fAudPid = aud_pid; fPcrPid = pcr_pid; } void TransportStreamDemux::ProcessPacket(const mpeg_ts_packet *pkt, bigtime_t start_time) { fCount++; // printf("ProcessPacket %lld\n", fCount); if (pkt->SyncByte() != 0x47) { // fCountInvalid++; printf("packet %lld sync byte error " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", fCount, pkt->header[0], pkt->header[1], pkt->header[2], pkt->header[3], pkt->data[0], pkt->data[1], pkt->data[2], pkt->data[3], pkt->data[4], pkt->data[5], pkt->data[6], pkt->data[7], pkt->data[8], pkt->data[9], pkt->data[10], pkt->data[11]); return; } // if (pkt->TransportError()) { // fCountInvalid++; // printf("invalid packet %lld (transport_error_indicator)\n", fCount); // return; // } int pid = pkt->PID(); if (pid == 0) { ProcessPAT(pkt); return; } if (pid == fPcrPid) { ProcessPCR(pkt, start_time); } if (pid == fAudPid) { ProcessAUD(pkt); } if (pid == fVidPid) { ProcessVID(pkt); } } void TransportStreamDemux::ProcessPAT(const mpeg_ts_packet *pkt) { } void TransportStreamDemux::ProcessPCR(const mpeg_ts_packet *pkt, bigtime_t start_time) { if (!(pkt->AdaptationFieldControl() & 0x2)) // adaptation field present? return; if (pkt->data[0] < 7) // long enough? return; if (!(pkt->data[1] & 0x10)) // PCR present? return; int64 pcr; pcr = (uint64(pkt->data[2]) << 25) | (uint32(pkt->data[3]) << 17) | (uint32(pkt->data[4]) << 9) | (uint32(pkt->data[5]) << 1) | (pkt->data[6] >> 7); pcr *= 300; pcr += (pkt->data[6] & 1) | pkt->data[7]; // printf(" pcr = %lld\n", pcr); // bigtime_t now = system_time(); // printf("system_time %.3f, PCR-time %.3f, delta %.06f, PCR %lld\n", now / 1e6, CLOCK_TO_USEC(pcr) / 1e6, (CLOCK_TO_USEC(pcr) - now) / 1e6, pcr); // atomic_set64(&fCurrentTime, CLOCK_TO_USEC(pcr)); fTimeSourceLocker.Lock(); fSystemTime = start_time; fPerformanceTime = CLOCK_TO_USEC(pcr); fTimeSourceLocker.Unlock(); } void TransportStreamDemux::ProcessAUD(const mpeg_ts_packet *pkt) { // flush old packet if (pkt->PayloadUnitStart()) { if (fAudPacketValid) { Packet *clone = new Packet(*fAudPacket); status_t err = fAudQueue->Insert(fAudPacket); if (err != B_OK) { delete fAudPacket; if (err == B_WOULD_BLOCK) { printf("fAudQueue->Insert failed (would block)\n"); } } err = fAudQueue2->Insert(clone); if (err != B_OK) { delete clone; if (err == B_WOULD_BLOCK) { printf("fAudQueue2->Insert failed (would block)\n"); } } fAudPacket = new Packet; } else { fAudPacket->MakeEmpty(); fAudPacketValid = true; } } int skip; switch (pkt->AdaptationFieldControl()) { case 0: // illegal skip = 184; break; case 1: // payload only skip = 0; break; case 2: // adaptation field only skip = 184; break; case 3: // adaptation field followed by payload skip = pkt->data[0] + 1; if (skip > 184) skip = 184; break; default: skip = 0; // stupid compiler, impossible case } const uint8 *data = pkt->data + skip; int size = 184 - skip; // if (skip != 0) // printf("aud skip %d\n", skip); if (pkt->PayloadUnitStart()) { if (pkt->TransportError()) { printf("error in audio packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); fAudPacketValid = false; return; } if (data[0] || data[1] || data[2] != 0x01 || data[3] <= 0xbf || data[3] >= 0xf0) { printf("invalid audio packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); fAudPacketValid = false; return; } if (data[7] & 0x80) { // PTS int64 pts; int64 dts; pts = (uint64((data[9] >> 1) & 0x7) << 30) | (data[10] << 22) | ((data[11] >> 1) << 15) | (data[12] << 7) | (data[13] >> 1); pts *= 300; // printf("vid pts = %lld\n", pts); if (data[7] & 0x40) { // DTS dts = (uint64((data[14] >> 1) & 0x7) << 30) | (data[15] << 22) | ((data[16] >> 1) << 15) | (data[17] << 7) | (data[18] >> 1); dts *= 300; // printf("aud dts = %lld\n", dts); } else { dts = pts; } fAudPacket->SetTimeStamp(CLOCK_TO_USEC(dts)); } // printf("aud %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", // data[0], data[1], data[2], data[3], data[4], // data[5], data[6], data[7], data[8], data[9]); } fAudPacket->AddData(data, size); } void TransportStreamDemux::ProcessVID(const mpeg_ts_packet *pkt) { // flush old packet // if (pkt->PayloadUnitStart() || pkt->TransportError()) { if (pkt->PayloadUnitStart()) { if (fVidPacketValid) { Packet *clone = new Packet(*fVidPacket); status_t err = fVidQueue->Insert(fVidPacket); if (err != B_OK) { delete fVidPacket; if (err == B_WOULD_BLOCK) { printf("fVidQueue->Insert failed (would block)\n"); } } err = fVidQueue2->Insert(clone); if (err != B_OK) { delete clone; if (err == B_WOULD_BLOCK) { printf("fVidQueue2->Insert failed (would block)\n"); } } fVidPacket = new Packet; } else { fVidPacket->MakeEmpty(); fVidPacketValid = true; } } // if (pkt->TransportError()) { // printf("transport error\n"); // fVidPacketValid = false; // return; // } int skip; switch (pkt->AdaptationFieldControl()) { case 0: // illegal skip = 184; break; case 1: // payload only skip = 0; break; case 2: // adaptation field only skip = 184; break; case 3: // adaptation field followed by payload skip = pkt->data[0] + 1; if (skip > 184) skip = 184; break; default: skip = 0; // stupid compiler, impossible case } const uint8 *data = pkt->data + skip; int size = 184 - skip; // if (skip != 0) // printf("vid skip %d\n", skip); if (pkt->PayloadUnitStart()) { if (pkt->TransportError()) { printf("error in video packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); fVidPacketValid = false; return; } if (data[0] || data[1] || data[2] != 0x01 || data[3] <= 0xbf || data[3] >= 0xf0) { printf("invalid video packet %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]); fVidPacketValid = false; return; } if (data[7] & 0x80) { // PTS int64 pts; int64 dts; pts = (uint64((data[9] >> 1) & 0x7) << 30) | (data[10] << 22) | ((data[11] >> 1) << 15) | (data[12] << 7) | (data[13] >> 1); pts *= 300; // printf("vid pts = %lld\n", pts); if (data[7] & 0x40) { // DTS dts = (uint64((data[14] >> 1) & 0x7) << 30) | (data[15] << 22) | ((data[16] >> 1) << 15) | (data[17] << 7) | (data[18] >> 1); dts *= 300; // printf("vid dts = %lld\n", dts); // printf("dts = %lld, pts = %lld, delta %lld ### dts = %lld, pts = %lld, delta %lld\n", // dts, pts, pts - dts, CLOCK_TO_USEC(dts), CLOCK_TO_USEC(pts), CLOCK_TO_USEC(pts - dts)); } else { dts = pts; } // printf("clocks: dts = %14Ld, pts = %14Ld, delta %8Ld ### usec: dts = %14Ld, pts = %14Ld, delta %8Ld\n", // dts, pts, pts - dts, CLOCK_TO_USEC(dts), CLOCK_TO_USEC(pts), CLOCK_TO_USEC(pts - dts)); fVidPacket->SetTimeStamp(CLOCK_TO_USEC(dts)); } // printf("vid %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", // data[0], data[1], data[2], data[3], data[4], // data[5], data[6], data[7], data[8], data[9]); } fVidPacket->AddData(data, size); } inline void TransportStreamDemux::ProcessData(const void *data, int size, bigtime_t start_time, bigtime_t delta) { const uint8 *d = (const uint8 *)data; if (d[0] != 0x47 && size > 376 && d[188] != 0x47 && d[376] != 0x47) { printf("TransportStreamDemux::ProcessData: input sync error: " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); return; } const mpeg_ts_packet *pkt = (const mpeg_ts_packet *)data; int count = size / 188; for (int i = 0; i < count; i++) { ProcessPacket(pkt++, start_time + (i * delta) / count); } } void TransportStreamDemux::AddData(Packet *packet) { bigtime_t end_time = packet->TimeStamp(); if (fLastEndTime == 0) { #define DEFAULT_DELTA 36270 // need something at the start, 36270 usec is the packet length // in Germany, and should be ok for other countries, too fLastEndTime = end_time - DEFAULT_DELTA; } bigtime_t delta = end_time - fLastEndTime; // sanity check if (delta > (3 * DEFAULT_DELTA)) { printf("TransportStreamDemux::ProcessData: delta %.6f is too large\n", delta / 1E6); fLastEndTime = end_time - DEFAULT_DELTA; delta = DEFAULT_DELTA; } ProcessData(packet->Data(), packet->Size(), fLastEndTime, delta); packet->SetTimeStamp(fLastEndTime); fLastEndTime = end_time; status_t err = fMpegTsQueue->Insert(packet); if (err != B_OK) { delete packet; if (err == B_WOULD_BLOCK) { printf("fMpegTsQueue->Insert failed (would block)\n"); } } }