Index: libs/libmythtv/mpegrecorder.h =================================================================== --- libs/libmythtv/mpegrecorder.h (revision 19056) +++ libs/libmythtv/mpegrecorder.h (working copy) @@ -80,11 +80,13 @@ uint GetFilteredAudioLayer(void) const; uint GetFilteredAudioBitRate(uint audio_layer) const; + void RestartEncoding(void); bool StartEncoding(int fd); bool StopEncoding(int fd); void ResetForNewFile(void); + bool WaitFor_HDPVR(void); void HandleResolutionChanges(void); inline bool CheckCC(uint pid, uint cc); @@ -104,7 +106,6 @@ // State bool recording; bool encoding; - bool needs_resolution; mutable QMutex start_stop_encoding_lock; QMutex recording_wait_lock; QWaitCondition recording_wait; @@ -113,7 +114,7 @@ bool cleartimeonpause; // Encoding info - int width, height; + uint width, height; int bitrate, maxbitrate, streamtype, aspectratio; int audtype, audsamplerate, audbitratel1, audbitratel2, audbitratel3; int audvolume; Index: libs/libmythtv/mpegrecorder.cpp =================================================================== --- libs/libmythtv/mpegrecorder.cpp (revision 19056) +++ libs/libmythtv/mpegrecorder.cpp (working copy) @@ -19,6 +19,7 @@ #include #include #include +#include // avlib headers extern "C" { @@ -86,7 +87,7 @@ requires_special_pause(false), // State recording(false), encoding(false), - needs_resolution(false), start_stop_encoding_lock(QMutex::Recursive), + start_stop_encoding_lock(QMutex::Recursive), recording_wait_lock(), recording_wait(), // Pausing state cleartimeonpause(false), @@ -487,13 +488,21 @@ bool MpegRecorder::SetFormat(int chanfd) { + uint idx; struct v4l2_format vfmt; bzero(&vfmt, sizeof(vfmt)); vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(chanfd, VIDIOC_G_FMT, &vfmt) < 0) + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_G_FMT, &vfmt) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 10) + { VERBOSE(VB_IMPORTANT, LOC_ERR + "Error getting format" + ENO); return false; } @@ -501,8 +510,15 @@ vfmt.fmt.pix.width = width; vfmt.fmt.pix.height = height; - if (ioctl(chanfd, VIDIOC_S_FMT, &vfmt) < 0) + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_S_FMT, &vfmt) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_ERR + "Error setting format" + ENO); return false; } @@ -513,10 +529,19 @@ /// Set audio language mode bool MpegRecorder::SetLanguageMode(int chanfd) { + uint idx; struct v4l2_tuner vt; bzero(&vt, sizeof(struct v4l2_tuner)); - if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) < 0) + + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get audio mode" + ENO); return false; } @@ -549,8 +574,15 @@ success = false; } - if (ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0) + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_S_TUNER, &vt) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set audio mode" + ENO); success = false; } @@ -561,10 +593,19 @@ bool MpegRecorder::SetRecordingVolume(int chanfd) { // Get volume min/max values + uint idx; struct v4l2_queryctrl qctrl; qctrl.id = V4L2_CID_AUDIO_VOLUME; - if (ioctl(chanfd, VIDIOC_QUERYCTRL, &qctrl) < 0) + + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_QUERYCTRL, &qctrl) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get recording volume parameters(max/min)" + ENO + "\n\t\t\tusing default range [0,65535]."); @@ -582,8 +623,15 @@ ctrl.id = V4L2_CID_AUDIO_VOLUME; ctrl.value = ctrl_volume; - if (ioctl(chanfd, VIDIOC_S_CTRL, &ctrl) < 0) + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_S_CTRL, &ctrl) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set recording volume" + ENO + "\n\t\t\t" + "If you are using an AverMedia M179 card this is normal."); @@ -765,6 +813,7 @@ for (uint i = 0; i < ext_ctrls.size(); i++) { + uint idx; struct v4l2_ext_controls ctrls; bzero(&ctrls, sizeof(struct v4l2_ext_controls)); @@ -774,8 +823,15 @@ ctrls.count = 1; ctrls.controls = &ext_ctrls[i]; - if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) + for (idx = 0; idx < 20; ++idx) { + if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { QMutexLocker locker(&control_description_lock); VERBOSE(VB_IMPORTANT, QString("mpegrecorder.cpp:set_ctrls(): ") + QString("Could not set %1 to %2") @@ -814,6 +870,30 @@ { maxbitrate = high_mpeg4peakbitrate; bitrate = high_mpeg4avgbitrate; + + // query supported audio codecs and prefer AC3 + uint idx; + struct v4l2_queryctrl qctrl; + qctrl.id = V4L2_CID_MPEG_AUDIO_ENCODING; + + for (idx = 0; idx < 20; ++idx) + { + if (ioctl(chanfd, VIDIOC_QUERYCTRL, &qctrl) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { + VERBOSE(VB_IMPORTANT, LOC_WARN + + "Unable to get supported audio codecs." + ENO); + } + else + { + if (qctrl.minimum != qctrl.maximum) + add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_ENCODING, + qctrl.maximum); + } } maxbitrate = std::max(maxbitrate, bitrate); @@ -837,19 +917,36 @@ int audioinput = audiodevice.toUInt(&ok); if (ok) { + uint idx; struct v4l2_audio ain; bzero(&ain, sizeof(ain)); ain.index = audioinput; - if (ioctl(chanfd, VIDIOC_ENUMAUDIO, &ain) < 0) + + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_ENUMAUDIO, &ain) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get audio input."); } else { ain.index = audioinput; - if (ioctl(chanfd, VIDIOC_S_AUDIO, &ain) < 0) + + for (idx = 0; idx < 20; ++idx) { + if (ioctl(chanfd, VIDIOC_S_AUDIO, &ain) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + { VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set audio input."); } @@ -1036,17 +1133,26 @@ if (deviceIsMpegFile) elapsedTimer.start(); else if (_device_read_buffer) - _device_read_buffer->Start(); + { + VERBOSE(VB_RECORD, LOC + "Initial startup of recorder"); - needs_resolution = (driver == "hdpvr"); + if (StartEncoding(readfd)) + _device_read_buffer->Start(); + else + { + VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to start recording"); + recording = false; + QMutexLocker locker(&recording_wait_lock); + recording_wait.wakeAll(); + _error = true; + } + } QByteArray vdevice = videodevice.toAscii(); while (encoding && !_error) { if (PauseAndWait(100)) continue; - - HandleResolutionChanges(); if (deviceIsMpegFile) { @@ -1090,35 +1196,7 @@ { VERBOSE(VB_IMPORTANT, LOC_ERR + "Device error detected"); - _device_read_buffer->Stop(); - - QMutexLocker locker(&start_stop_encoding_lock); - - StopEncoding(readfd); - - // Make sure the next things in the file are a PAT & PMT - if (_stream_data->PATSingleProgram() && - _stream_data->PMTSingleProgram()) - { - bool tmp = _wait_for_keyframe_option; - _wait_for_keyframe_option = false; - HandleSingleProgramPAT(_stream_data->PATSingleProgram()); - HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); - _wait_for_keyframe_option = tmp; - } - - if (StartEncoding(readfd)) - { - _device_read_buffer->Start(); - } - else - { - if (0 != close(readfd)) - VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); - - // Force card to be reopened on next iteration.. - readfd = -1; - } + RestartEncoding(); } else if (_device_read_buffer->IsEOF()) { @@ -1216,6 +1294,8 @@ } } + VERBOSE(VB_RECORD, LOC + "StartRecording finishing up"); + if (_device_read_buffer) { if (_device_read_buffer->IsRunning()) @@ -1224,6 +1304,7 @@ delete _device_read_buffer; _device_read_buffer = NULL; } + StopEncoding(readfd); FinishRecording(); @@ -1373,52 +1454,95 @@ if (!paused) { + VERBOSE(VB_RECORD, LOC + "PauseAndWait pause"); + + // Some drivers require streaming to be disabled before + // an input switch and other channel format setting. + if (requires_special_pause) + StopEncoding(readfd); + if (_device_read_buffer) { QMutex drb_lock; drb_lock.lock(); - _device_read_buffer->SetRequestPause(true); - pauseWait.wait(&drb_lock, timeout); } else - { - paused = true; pauseWait.wakeAll(); - } - // Some drivers require streaming to be disabled before - // an input switch and other channel format setting. - if (requires_special_pause) - StopEncoding(readfd); - + paused = true; if (tvrec) tvrec->RecorderPaused(); } unpauseWait.wait(&waitlock, timeout); } - if (!request_pause) + + if (!request_pause && paused) { - if (paused) + VERBOSE(VB_RECORD, LOC + "PauseAndWait unpause"); + + if (driver == "hdpvr") { - // Some drivers require streaming to be disabled before - // an input switch and other channel format setting. - if (requires_special_pause) - StartEncoding(readfd); + m_h264_parser.Reset(); + _wait_for_keyframe_option = true; + _seen_sps = false; - if (_device_read_buffer) - _device_read_buffer->SetRequestPause(false); + // Sleep any less than 1.5 seconds, and the HD-PVR will + // return the old resolution, when the resolution is changing. + usleep(1500 * 1000); + } - if (_stream_data) - _stream_data->Reset(_stream_data->DesiredProgram()); - } + // Some drivers require streaming to be disabled before + // an input switch and other channel format setting. + if (requires_special_pause) + StartEncoding(readfd); + + if (_device_read_buffer) + _device_read_buffer->SetRequestPause(false); + + if (_stream_data) + _stream_data->Reset(_stream_data->DesiredProgram()); + paused = false; } + return paused; } +void MpegRecorder::RestartEncoding(void) +{ + VERBOSE(VB_RECORD, LOC + "RestartEncoding"); + + _device_read_buffer->Stop(); + + QMutexLocker locker(&start_stop_encoding_lock); + + StopEncoding(readfd); + + // Make sure the next things in the file are a PAT & PMT + if (_stream_data->PATSingleProgram() && + _stream_data->PMTSingleProgram()) + { + _wait_for_keyframe_option = false; + HandleSingleProgramPAT(_stream_data->PATSingleProgram()); + HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); + } + + if (StartEncoding(readfd)) + { + _device_read_buffer->Start(); + } + else + { + if (0 != close(readfd)) + VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); + + readfd = -1; + } +} + bool MpegRecorder::StartEncoding(int fd) { QMutexLocker locker(&start_stop_encoding_lock); @@ -1427,13 +1551,22 @@ memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); command.cmd = V4L2_ENC_CMD_START; + if (driver == "hdpvr") + HandleResolutionChanges(); + VERBOSE(VB_RECORD, LOC + "StartEncoding"); - needs_resolution = (driver == "hdpvr"); - for (int idx = 0; idx < 10; ++idx) + for (int idx = 0; idx < 20; ++idx) { if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) { + if (driver == "hdpvr") + { + m_h264_parser.Reset(); + _wait_for_keyframe_option = true; + _seen_sps = false; + } + VERBOSE(VB_RECORD, LOC + "Encoding started"); return true; } @@ -1444,7 +1577,7 @@ return false; } - usleep(250 * 1000); + usleep(100 * 1000); } VERBOSE(VB_IMPORTANT, LOC_ERR + "StartEncoding - giving up" + ENO); @@ -1461,9 +1594,8 @@ VERBOSE(VB_RECORD, LOC + "StopEncoding"); - for (int idx = 0; idx < 10; ++idx) + for (int idx = 0; idx < 20; ++idx) { - if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) { VERBOSE(VB_RECORD, LOC + "Encoding stopped"); @@ -1476,7 +1608,7 @@ return false; } - usleep(250 * 1000); + usleep(100 * 1000); } VERBOSE(VB_IMPORTANT, LOC_ERR + "StopEncoding - giving up" + ENO); @@ -1544,7 +1676,7 @@ void MpegRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt) { if (!pmt) -{ + { return; } @@ -1564,27 +1696,89 @@ DTVRecorder::BufferedWrite(*(reinterpret_cast(&buf[i]))); } +bool MpegRecorder::WaitFor_HDPVR(void) +{ + // After a resolution change, it can take the HD-PVR a few + // seconds before it is usable again. + + // Tell it to start encoding, then wait for it to actually feed us + // some data. + QMutexLocker locker(&start_stop_encoding_lock); + + struct v4l2_encoder_cmd command; + struct pollfd polls; + int idx; + + memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); + command.cmd = V4L2_ENC_CMD_START; + + for (idx = 0; idx < 20; ++idx) + { + if (ioctl(readfd, VIDIOC_ENCODER_CMD, &command) == 0) + break; + usleep(100 * 1000); + } + + if (idx == 20) + return false; + + polls.fd = readfd; + polls.events = POLLIN; + polls.revents = 0; + + for (idx = 0; idx < 10; ++idx) + { + if (poll(&polls, 1, 250) > 0) + break; + } + + if (idx == 10) + return false; + + // HD-PVR should now be "ready" + command.cmd = V4L2_ENC_CMD_STOP; + + for (idx = 0; idx < 20; ++idx) + { + if (ioctl(readfd, VIDIOC_ENCODER_CMD, &command) == 0) + return true; + usleep(100 * 1000); + } + + return false; +} + void MpegRecorder::HandleResolutionChanges(void) { - if (!needs_resolution) - return; - VERBOSE(VB_RECORD, LOC + "Checking Resolution"); struct v4l2_format vfmt; memset(&vfmt, 0, sizeof(vfmt)); vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (driver == "hdpvr") + WaitFor_HDPVR(); + + uint idx; uint pix = 0; + + for (idx = 0; idx < 20; ++idx) + { if (0 == ioctl(chanfd, VIDIOC_G_FMT, &vfmt)) { VERBOSE(VB_RECORD, LOC + QString("Got Resolution %1x%2") .arg(vfmt.fmt.pix.width).arg(vfmt.fmt.pix.height)); pix = vfmt.fmt.pix.width * vfmt.fmt.pix.height; - needs_resolution = false; + break; + } + // Typically takes 0.9 seconds after a resolution change + usleep(100 * 1000); } if (!pix) + { + VERBOSE(VB_RECORD, LOC + "Giving up detecting resolution"); return; // nothing to do, we don't have a resolution yet + } int old_max = maxbitrate, old_avg = bitrate; if (pix <= 768*568) @@ -1609,13 +1803,14 @@ if (old_max == old_avg) { VERBOSE(VB_RECORD, LOC + - QString("Old bitrate %1 CBR").arg(old_avg)); + QString("Old bitrate %1 CBR for %2x%3") + .arg(old_avg).arg(width).arg(height)); } else { VERBOSE(VB_RECORD, LOC + - QString("Old bitrate %1/%2 VBR") - .arg(old_avg).arg(old_max)); + QString("Old bitrate %1/%2 VBR for %3x%4") + .arg(old_avg).arg(old_max).arg(width).arg(height)); } if (maxbitrate == bitrate) @@ -1642,12 +1837,6 @@ maxbitrate * 1000); set_ctrls(readfd, ext_ctrls); + } - - // Restart streaming. Shouldn't be needed? seems to be with current driver. - QMutexLocker locker(&start_stop_encoding_lock); - StopEncoding(readfd); - StartEncoding(readfd); - - needs_resolution = false; }