MythTV  0.26-pre
iptvrecorder.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00008 #include <unistd.h>
00009 
00010 // MythTV headers
00011 #include "mpegstreamdata.h"
00012 #include "tspacket.h"
00013 #include "iptvchannel.h"
00014 #include "iptvfeederwrapper.h"
00015 #include "iptvrecorder.h"
00016 #include "tv_rec.h"
00017 
00018 #define LOC QString("IPTVRec: ")
00019 
00020 // ============================================================================
00021 // IPTVRecorder : Processes data from RTSPComms and writes it to disk
00022 // ============================================================================
00023 
00024 IPTVRecorder::IPTVRecorder(TVRec *rec, IPTVChannel *channel) :
00025     DTVRecorder(rec),
00026     _channel(channel)
00027 {
00028     _channel->GetFeeder()->AddListener(this);
00029 }
00030 
00031 IPTVRecorder::~IPTVRecorder()
00032 {
00033     StopRecording();
00034     _channel->GetFeeder()->RemoveListener(this);
00035 }
00036 
00037 bool IPTVRecorder::Open(void)
00038 {
00039     LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- begin");
00040 
00041     if (_channel->GetFeeder()->IsOpen())
00042         return true;    // already open
00043 
00044     IPTVChannelInfo chaninfo = _channel->GetCurrentChanInfo();
00045 
00046     if (!chaninfo.isValid())
00047         _error = "Channel Info is invalid";
00048     else if (!_channel->GetFeeder()->Open(chaninfo.m_url))
00049         _error = QString("Failed to open URL %1")
00050             .arg(chaninfo.m_url);
00051 
00052     LOG(VB_RECORD, LOG_INFO, LOC + QString("Open() -- end err(%1)")
00053             .arg(_error));
00054 
00055     return !IsErrored();
00056 }
00057 
00058 void IPTVRecorder::Close(void)
00059 {
00060     LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- begin");
00061     _channel->GetFeeder()->Stop();
00062     _channel->GetFeeder()->Close();
00063     LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
00064 }
00065 
00066 void IPTVRecorder::StopRecording(void)
00067 {
00068     pauseLock.lock();
00069     request_recording = false;
00070     unpauseWait.wakeAll();
00071     pauseLock.unlock();
00072     
00073     // we can't hold the pause lock while we wait for the IPTV feeder to stop
00074     _channel->GetFeeder()->Stop();
00075 }
00076 
00077 void IPTVRecorder::run(void)
00078 {
00079     LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
00080     if (!Open())
00081     {
00082         _error = "Failed to open IPTV stream";
00083         return;
00084     }
00085 
00086     // Start up...
00087     {
00088         QMutexLocker locker(&pauseLock);
00089         request_recording = true;
00090         recording = true;
00091         recordingWait.wakeAll();
00092     }
00093 
00094     // Go into main RTSP loop, feeding data to AddData
00095     _channel->GetFeeder()->Run();
00096 
00097     Close();
00098 
00099     // Finish up...
00100     FinishRecording();
00101     QMutexLocker locker(&pauseLock);
00102     recording = false;
00103     recordingWait.wakeAll();
00104 
00105     LOG(VB_RECORD, LOG_INFO, LOC + "run() -- end");
00106 }
00107 
00108 // ===================================================
00109 // findTSHeader : find a TS Header in flow
00110 // ===================================================
00111 static int IPTVRecorder_findTSHeader(const unsigned char *data,
00112                                         uint dataSize)
00113 {
00114     unsigned int pos = 0;
00115 
00116     while (pos < dataSize)
00117     {
00118         if (data[pos] == 0x47)
00119             return pos;
00120         pos++;
00121     }
00122 
00123     return -1;
00124 }
00125 
00126 // ===================================================
00127 // AddData : feed data from RTSP flow to mythtv
00128 // ===================================================
00129 void IPTVRecorder::AddData(const unsigned char *data, unsigned int dataSize)
00130 {
00131     unsigned int readIndex = 0;
00132 
00133     // data may be compose from more than one packet, loop to consume all data
00134     while (readIndex < dataSize)
00135     {
00136         // If recorder is paused, stop there
00137         if (IsPaused(false))
00138             return;
00139 
00140         // Find the next TS Header in data
00141         int tsPos = IPTVRecorder_findTSHeader(
00142             data + readIndex, dataSize - readIndex);
00143 
00144         // if no TS, something bad happens
00145         if (tsPos == -1)
00146         {
00147             LOG(VB_GENERAL, LOG_ERR, LOC + "No TS header.");
00148             break;
00149         }
00150 
00151         // if TS Header not at start of data, we receive out of sync data
00152         if (tsPos > 0)
00153         {
00154             LOG(VB_GENERAL, LOG_ERR, LOC +
00155                 QString("TS packet at %1, not in sync.").arg(tsPos));
00156         }
00157 
00158         // Check if the next packet in buffer is complete :
00159         // packet size is 188 bytes long
00160         if ((dataSize - tsPos - readIndex) < TSPacket::kSize)
00161         {
00162             LOG(VB_GENERAL, LOG_ERR, LOC +
00163                 "TS packet at stradles end of buffer.");
00164             break;
00165         }
00166 
00167         // Cast current found TS Packet to TSPacket structure
00168         const void *newData = data + tsPos + readIndex;
00169         ProcessTSPacket(*reinterpret_cast<const TSPacket*>(newData));
00170 
00171         // follow to next packet
00172         readIndex += tsPos + TSPacket::kSize;
00173     }
00174 }
00175 
00176 bool IPTVRecorder::ProcessTSPacket(const TSPacket& tspacket)
00177 {
00178     if (!_stream_data)
00179         return true;
00180 
00181     if (tspacket.TransportError() || tspacket.Scrambled())
00182         return true;
00183 
00184     if (tspacket.HasAdaptationField())
00185         _stream_data->HandleAdaptationFieldControl(&tspacket);
00186 
00187     if (tspacket.HasPayload())
00188     {
00189         const unsigned int lpid = tspacket.PID();
00190 
00191         // Pass or reject packets based on PID, and parse info from them
00192         if (lpid == _stream_data->VideoPIDSingleProgram())
00193         {
00194             ProgramMapTable *pmt = _stream_data->PMTSingleProgram();
00195             uint video_stream_type = pmt->StreamType(pmt->FindPID(lpid));
00196 
00197             if (video_stream_type == StreamID::H264Video)
00198                 _buffer_packets = !FindH264Keyframes(&tspacket);
00199             else if (StreamID::IsVideo(video_stream_type))
00200                 _buffer_packets = !FindMPEG2Keyframes(&tspacket);
00201 
00202             if ((video_stream_type != StreamID::H264Video) || _seen_sps)
00203                 BufferedWrite(tspacket);
00204         }
00205         else if (_stream_data->IsAudioPID(lpid))
00206         {
00207             _buffer_packets = !FindAudioKeyframes(&tspacket);
00208             BufferedWrite(tspacket);
00209         }
00210         else if (_stream_data->IsListeningPID(lpid))
00211             _stream_data->HandleTSTables(&tspacket);
00212         else if (_stream_data->IsWritingPID(lpid))
00213             BufferedWrite(tspacket);
00214     }
00215 
00216     return true;
00217 }
00218 
00219 void IPTVRecorder::SetStreamData(void)
00220 {
00221     _stream_data->AddMPEGSPListener(this);
00222 }
00223 
00224 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends