Lines 3-8
Link Here
|
3 |
// C headers |
3 |
// C headers |
4 |
#include <ctime> |
4 |
#include <ctime> |
5 |
|
5 |
|
|
|
6 |
// C++ headers |
7 |
#include <algorithm> |
8 |
#include <vector> |
9 |
using namespace std; |
10 |
|
6 |
// POSIX headers |
11 |
// POSIX headers |
7 |
#include <pthread.h> |
12 |
#include <pthread.h> |
8 |
#include <fcntl.h> |
13 |
#include <fcntl.h> |
Lines 15-23
Link Here
|
15 |
#include <sys/ioctl.h> |
20 |
#include <sys/ioctl.h> |
16 |
#include <sys/time.h> |
21 |
#include <sys/time.h> |
17 |
|
22 |
|
18 |
#include <algorithm> |
|
|
19 |
using namespace std; |
20 |
|
21 |
// avlib headers |
23 |
// avlib headers |
22 |
extern "C" { |
24 |
extern "C" { |
23 |
#include "../libavcodec/avcodec.h" |
25 |
#include "../libavcodec/avcodec.h" |
Lines 73-85
Link Here
|
73 |
"Square", "4:3", "16:9", "2.21:1", 0 |
75 |
"Square", "4:3", "16:9", "2.21:1", 0 |
74 |
}; |
76 |
}; |
75 |
|
77 |
|
76 |
const unsigned int MpegRecorder::kBuildBufferMaxSize = 1024 * 1024; |
|
|
77 |
|
78 |
MpegRecorder::MpegRecorder(TVRec *rec) : |
78 |
MpegRecorder::MpegRecorder(TVRec *rec) : |
79 |
RecorderBase(rec), |
79 |
DTVRecorder(rec), |
80 |
// Debugging variables |
80 |
// Debugging variables |
81 |
deviceIsMpegFile(false), |
81 |
deviceIsMpegFile(false), |
82 |
bufferSize(4096), |
|
|
83 |
// Driver info |
82 |
// Driver info |
84 |
card(QString::null), driver(QString::null), |
83 |
card(QString::null), driver(QString::null), |
85 |
version(0), usingv4l2(false), |
84 |
version(0), usingv4l2(false), |
Lines 87-97
Link Here
|
87 |
requires_special_pause(false), |
86 |
requires_special_pause(false), |
88 |
// State |
87 |
// State |
89 |
recording(false), encoding(false), |
88 |
recording(false), encoding(false), |
90 |
errored(false), |
89 |
needs_resolution(false), start_stop_encoding_lock(true), |
|
|
90 |
recording_wait_lock(), recording_wait(), |
91 |
// Pausing state |
91 |
// Pausing state |
92 |
cleartimeonpause(false), |
92 |
cleartimeonpause(false), |
93 |
// Number of frames written |
|
|
94 |
framesWritten(0), |
95 |
// Encoding info |
93 |
// Encoding info |
96 |
width(720), height(480), |
94 |
width(720), height(480), |
97 |
bitrate(4500), maxbitrate(6000), |
95 |
bitrate(4500), maxbitrate(6000), |
Lines 100-114
Link Here
|
100 |
audbitratel1(14), audbitratel2(14), |
98 |
audbitratel1(14), audbitratel2(14), |
101 |
audbitratel3(10), |
99 |
audbitratel3(10), |
102 |
audvolume(80), language(0), |
100 |
audvolume(80), language(0), |
|
|
101 |
low_mpeg4avgbitrate(4500), low_mpeg4peakbitrate(6000), |
102 |
medium_mpeg4avgbitrate(9000), medium_mpeg4peakbitrate(13500), |
103 |
high_mpeg4avgbitrate(13500), high_mpeg4peakbitrate(20200), |
103 |
// Input file descriptors |
104 |
// Input file descriptors |
104 |
chanfd(-1), readfd(-1), |
105 |
chanfd(-1), readfd(-1), |
105 |
// Keyframe tracking inforamtion |
106 |
_device_read_buffer(NULL), |
106 |
keyframedist(15), gopset(false), |
107 |
// TS packet handling |
107 |
leftovers(0), lastpackheaderpos(0), |
108 |
_stream_data(NULL) |
108 |
lastseqstart(0), numgops(0), |
|
|
109 |
// buffer used for ... |
110 |
buildbuffer(new unsigned char[kBuildBufferMaxSize + 1]), |
111 |
buildbuffersize(0) |
112 |
{ |
109 |
{ |
113 |
SetPositionMapType(MARK_GOP_START); |
110 |
SetPositionMapType(MARK_GOP_START); |
114 |
} |
111 |
} |
Lines 116-126
Link Here
|
116 |
MpegRecorder::~MpegRecorder() |
113 |
MpegRecorder::~MpegRecorder() |
117 |
{ |
114 |
{ |
118 |
TeardownAll(); |
115 |
TeardownAll(); |
119 |
delete [] buildbuffer; |
|
|
120 |
} |
116 |
} |
121 |
|
117 |
|
122 |
void MpegRecorder::TeardownAll(void) |
118 |
void MpegRecorder::TeardownAll(void) |
123 |
{ |
119 |
{ |
|
|
120 |
StopRecording(); |
121 |
|
124 |
if (chanfd >= 0) |
122 |
if (chanfd >= 0) |
125 |
{ |
123 |
{ |
126 |
close(chanfd); |
124 |
close(chanfd); |
Lines 191-196
Link Here
|
191 |
} |
189 |
} |
192 |
else if (opt == "mpeg2audvolume") |
190 |
else if (opt == "mpeg2audvolume") |
193 |
audvolume = value; |
191 |
audvolume = value; |
|
|
192 |
else if (opt.right(16) == "_mpeg4avgbitrate") |
193 |
{ |
194 |
if (opt.left(3) == "low") |
195 |
low_mpeg4avgbitrate = value; |
196 |
else if (opt.left(6) == "medium") |
197 |
medium_mpeg4avgbitrate = value; |
198 |
else if (opt.left(4) == "high") |
199 |
high_mpeg4avgbitrate = value; |
200 |
else |
201 |
RecorderBase::SetOption(opt, value); |
202 |
} |
203 |
else if (opt.right(17) == "_mpeg4peakbitrate") |
204 |
{ |
205 |
if (opt.left(3) == "low") |
206 |
low_mpeg4peakbitrate = value; |
207 |
else if (opt.left(6) == "medium") |
208 |
medium_mpeg4peakbitrate = value; |
209 |
else if (opt.left(4) == "high") |
210 |
high_mpeg4peakbitrate = value; |
211 |
else |
212 |
RecorderBase::SetOption(opt, value); |
213 |
} |
194 |
else |
214 |
else |
195 |
RecorderBase::SetOption(opt, value); |
215 |
RecorderBase::SetOption(opt, value); |
196 |
} |
216 |
} |
Lines 287-292
Link Here
|
287 |
SetOption("videodevice", videodev); |
307 |
SetOption("videodevice", videodev); |
288 |
} |
308 |
} |
289 |
|
309 |
|
|
|
310 |
SetOption("audiodevice", audiodev); |
311 |
|
290 |
SetOption("tvformat", gContext->GetSetting("TVFormat")); |
312 |
SetOption("tvformat", gContext->GetSetting("TVFormat")); |
291 |
SetOption("vbiformat", gContext->GetSetting("VbiFormat")); |
313 |
SetOption("vbiformat", gContext->GetSetting("VbiFormat")); |
292 |
|
314 |
|
Lines 305-312
Link Here
|
305 |
|
327 |
|
306 |
SetIntOption(profile, "width"); |
328 |
SetIntOption(profile, "width"); |
307 |
SetIntOption(profile, "height"); |
329 |
SetIntOption(profile, "height"); |
|
|
330 |
|
331 |
SetIntOption(profile, "low_mpeg4avgbitrate"); |
332 |
SetIntOption(profile, "low_mpeg4peakbitrate"); |
333 |
SetIntOption(profile, "medium_mpeg4avgbitrate"); |
334 |
SetIntOption(profile, "medium_mpeg4peakbitrate"); |
335 |
SetIntOption(profile, "high_mpeg4avgbitrate"); |
336 |
SetIntOption(profile, "high_mpeg4peakbitrate"); |
308 |
} |
337 |
} |
309 |
|
338 |
|
|
|
339 |
// same as the base class, it just doesn't complain if an option is missing |
340 |
void MpegRecorder::SetIntOption(RecordingProfile *profile, const QString &name) |
341 |
{ |
342 |
const Setting *setting = profile->byName(name); |
343 |
if (setting) |
344 |
SetOption(name, setting->getValue().toInt()); |
345 |
} |
346 |
|
347 |
// same as the base class, it just doesn't complain if an option is missing |
348 |
void MpegRecorder::SetStrOption(RecordingProfile *profile, const QString &name) |
349 |
{ |
350 |
const Setting *setting = profile->byName(name); |
351 |
if (setting) |
352 |
SetOption(name, setting->getValue()); |
353 |
} |
354 |
|
310 |
bool MpegRecorder::OpenMpegFileAsInput(void) |
355 |
bool MpegRecorder::OpenMpegFileAsInput(void) |
311 |
{ |
356 |
{ |
312 |
chanfd = readfd = open(videodevice.ascii(), O_RDONLY); |
357 |
chanfd = readfd = open(videodevice.ascii(), O_RDONLY); |
Lines 324-329
Link Here
|
324 |
bool MpegRecorder::OpenV4L2DeviceAsInput(void) |
369 |
bool MpegRecorder::OpenV4L2DeviceAsInput(void) |
325 |
{ |
370 |
{ |
326 |
chanfd = open(videodevice.ascii(), O_RDWR); |
371 |
chanfd = open(videodevice.ascii(), O_RDWR); |
|
|
372 |
// open implicitly starts encoding, so we need the lock.. |
373 |
QMutexLocker locker(&start_stop_encoding_lock); |
374 |
|
327 |
if (chanfd < 0) |
375 |
if (chanfd < 0) |
328 |
{ |
376 |
{ |
329 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device. " + ENO); |
377 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device. " + ENO); |
Lines 334-348
Link Here
|
334 |
{ |
382 |
{ |
335 |
if (driver == "ivtv") |
383 |
if (driver == "ivtv") |
336 |
{ |
384 |
{ |
|
|
385 |
bufferSize = 4096; |
337 |
usingv4l2 = (version >= IVTV_KERNEL_VERSION(0, 8, 0)); |
386 |
usingv4l2 = (version >= IVTV_KERNEL_VERSION(0, 8, 0)); |
338 |
has_v4l2_vbi = (version >= IVTV_KERNEL_VERSION(0, 3, 8)); |
387 |
has_v4l2_vbi = (version >= IVTV_KERNEL_VERSION(0, 3, 8)); |
339 |
has_buggy_vbi = true; |
388 |
has_buggy_vbi = true; |
340 |
requires_special_pause = |
389 |
requires_special_pause = |
341 |
(version >= IVTV_KERNEL_VERSION(0, 10, 0)); |
390 |
(version >= IVTV_KERNEL_VERSION(0, 10, 0)); |
342 |
} |
391 |
} |
|
|
392 |
else if (driver == "hdpvr") |
393 |
{ |
394 |
bufferSize = 1500 * TSPacket::SIZE; |
395 |
usingv4l2 = true; |
396 |
requires_special_pause = true; |
397 |
|
398 |
bzero(_stream_id, sizeof(_stream_id)); |
399 |
bzero(_pid_status, sizeof(_pid_status)); |
400 |
memset(_continuity_counter, 0xff, sizeof(_continuity_counter)); |
401 |
} |
343 |
else |
402 |
else |
344 |
{ |
403 |
{ |
345 |
VERBOSE(VB_IMPORTANT, "\n\nNot ivtv driver??\n\n"); |
404 |
VERBOSE(VB_IMPORTANT, "\n\nNot ivtv or hdpvr driver??\n\n"); |
346 |
usingv4l2 = has_v4l2_vbi = true; |
405 |
usingv4l2 = has_v4l2_vbi = true; |
347 |
has_buggy_vbi = requires_special_pause = false; |
406 |
has_buggy_vbi = requires_special_pause = false; |
348 |
} |
407 |
} |
Lines 352-357
Link Here
|
352 |
"has_buggy_vbi(%3)") |
411 |
"has_buggy_vbi(%3)") |
353 |
.arg(usingv4l2).arg(has_v4l2_vbi).arg(has_buggy_vbi)); |
412 |
.arg(usingv4l2).arg(has_v4l2_vbi).arg(has_buggy_vbi)); |
354 |
|
413 |
|
|
|
414 |
|
415 |
if ((driver != "hdpvr") && !SetFormat(chanfd)) |
416 |
return false; |
417 |
|
418 |
if (driver != "hdpvr") |
419 |
{ |
420 |
SetLanguageMode(chanfd); // we don't care if this fails... |
421 |
SetRecordingVolume(chanfd); // we don't care if this fails... |
422 |
} |
423 |
|
424 |
bool ok = true; |
425 |
if (usingv4l2) |
426 |
ok = SetV4L2DeviceOptions(chanfd); |
427 |
else |
428 |
{ |
429 |
ok = SetIVTVDeviceOptions(chanfd); |
430 |
if (!ok) |
431 |
usingv4l2 = ok = SetV4L2DeviceOptions(chanfd); |
432 |
} |
433 |
|
434 |
if (!ok) |
435 |
return false; |
436 |
|
437 |
SetVBIOptions(chanfd); |
438 |
|
439 |
readfd = open(videodevice.ascii(), O_RDWR | O_NONBLOCK); |
440 |
|
441 |
if (readfd < 0) |
442 |
{ |
443 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device." + ENO); |
444 |
return false; |
445 |
} |
446 |
|
447 |
if (_device_read_buffer) |
448 |
{ |
449 |
if (_device_read_buffer->IsRunning()) |
450 |
_device_read_buffer->Stop(); |
451 |
|
452 |
delete _device_read_buffer; |
453 |
_device_read_buffer = NULL; |
454 |
} |
455 |
|
456 |
_device_read_buffer = new DeviceReadBuffer(this); |
457 |
|
458 |
if (!_device_read_buffer) |
459 |
{ |
460 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer"); |
461 |
_error = true; |
462 |
return false; |
463 |
} |
464 |
|
465 |
if (!_device_read_buffer->Setup(videodevice.ascii(), readfd)) |
466 |
{ |
467 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer"); |
468 |
_error = true; |
469 |
return false; |
470 |
} |
471 |
|
472 |
VERBOSE(VB_RECORD, LOC + "DRB ready"); |
473 |
|
474 |
return true; |
475 |
} |
476 |
|
477 |
|
478 |
bool MpegRecorder::SetFormat(int chanfd) |
479 |
{ |
355 |
struct v4l2_format vfmt; |
480 |
struct v4l2_format vfmt; |
356 |
bzero(&vfmt, sizeof(vfmt)); |
481 |
bzero(&vfmt, sizeof(vfmt)); |
357 |
|
482 |
|
Lines 372-385
Link Here
|
372 |
return false; |
497 |
return false; |
373 |
} |
498 |
} |
374 |
|
499 |
|
375 |
// Set audio language mode |
500 |
return true; |
376 |
bool do_audmode_set = true; |
501 |
} |
|
|
502 |
|
503 |
/// Set audio language mode |
504 |
bool MpegRecorder::SetLanguageMode(int chanfd) |
505 |
{ |
377 |
struct v4l2_tuner vt; |
506 |
struct v4l2_tuner vt; |
378 |
bzero(&vt, sizeof(struct v4l2_tuner)); |
507 |
bzero(&vt, sizeof(struct v4l2_tuner)); |
379 |
if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) < 0) |
508 |
if (ioctl(chanfd, VIDIOC_G_TUNER, &vt) < 0) |
380 |
{ |
509 |
{ |
381 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get audio mode" + ENO); |
510 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get audio mode" + ENO); |
382 |
do_audmode_set = false; |
511 |
return false; |
383 |
} |
512 |
} |
384 |
|
513 |
|
385 |
switch (language) |
514 |
switch (language) |
Lines 401-418
Link Here
|
401 |
} |
530 |
} |
402 |
|
531 |
|
403 |
int audio_layer = GetFilteredAudioLayer(); |
532 |
int audio_layer = GetFilteredAudioLayer(); |
404 |
if (do_audmode_set && (2 == language) && (1 == audio_layer)) |
533 |
bool success = true; |
|
|
534 |
if ((2 == language) && (1 == audio_layer)) |
405 |
{ |
535 |
{ |
406 |
VERBOSE(VB_GENERAL, "Dual audio mode incompatible with Layer I audio." |
536 |
VERBOSE(VB_GENERAL, "Dual audio mode incompatible with Layer I audio." |
407 |
"\n\t\t\tFalling back to Main Language"); |
537 |
"\n\t\t\tFalling back to Main Language"); |
408 |
vt.audmode = V4L2_TUNER_MODE_LANG1; |
538 |
vt.audmode = V4L2_TUNER_MODE_LANG1; |
|
|
539 |
success = false; |
409 |
} |
540 |
} |
410 |
|
541 |
|
411 |
if (do_audmode_set && ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0) |
542 |
if (ioctl(chanfd, VIDIOC_S_TUNER, &vt) < 0) |
412 |
{ |
543 |
{ |
413 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set audio mode" + ENO); |
544 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to set audio mode" + ENO); |
|
|
545 |
success = false; |
414 |
} |
546 |
} |
415 |
|
547 |
|
|
|
548 |
return success; |
549 |
} |
550 |
|
551 |
bool MpegRecorder::SetRecordingVolume(int chanfd) |
552 |
{ |
416 |
// Get volume min/max values |
553 |
// Get volume min/max values |
417 |
struct v4l2_queryctrl qctrl; |
554 |
struct v4l2_queryctrl qctrl; |
418 |
qctrl.id = V4L2_CID_AUDIO_VOLUME; |
555 |
qctrl.id = V4L2_CID_AUDIO_VOLUME; |
Lines 440-467
Link Here
|
440 |
VERBOSE(VB_IMPORTANT, LOC_WARN + |
577 |
VERBOSE(VB_IMPORTANT, LOC_WARN + |
441 |
"Unable to set recording volume" + ENO + "\n\t\t\t" + |
578 |
"Unable to set recording volume" + ENO + "\n\t\t\t" + |
442 |
"If you are using an AverMedia M179 card this is normal."); |
579 |
"If you are using an AverMedia M179 card this is normal."); |
443 |
} |
|
|
444 |
|
445 |
bool ok = true; |
446 |
if (usingv4l2) |
447 |
ok = SetV4L2DeviceOptions(chanfd); |
448 |
else |
449 |
{ |
450 |
ok = SetIVTVDeviceOptions(chanfd); |
451 |
if (!ok) |
452 |
usingv4l2 = ok = SetV4L2DeviceOptions(chanfd); |
453 |
} |
454 |
|
455 |
if (!ok) |
456 |
return false; |
580 |
return false; |
457 |
|
|
|
458 |
SetVBIOptions(chanfd); |
459 |
|
460 |
readfd = open(videodevice.ascii(), O_RDWR | O_NONBLOCK); |
461 |
if (readfd < 0) |
462 |
{ |
463 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Can't open video device." + ENO); |
464 |
return false; |
465 |
} |
581 |
} |
466 |
|
582 |
|
467 |
return true; |
583 |
return true; |
Lines 578-584
Link Here
|
578 |
return false; |
694 |
return false; |
579 |
} |
695 |
} |
580 |
|
696 |
|
581 |
keyframedist = (ivtvcodec.framerate) ? 12 : keyframedist; |
697 |
_keyframedist = (ivtvcodec.framerate) ? 12 : _keyframedist; |
582 |
|
698 |
|
583 |
return true; |
699 |
return true; |
584 |
} |
700 |
} |
Lines 602-684
Link Here
|
602 |
} |
718 |
} |
603 |
} |
719 |
} |
604 |
|
720 |
|
605 |
bool MpegRecorder::SetV4L2DeviceOptions(int chanfd) |
721 |
static void add_ext_ctrl(vector<struct v4l2_ext_control> &ctrl_list, |
|
|
722 |
uint32_t id, int32_t value) |
606 |
{ |
723 |
{ |
607 |
static const uint kNumControls = 7; |
724 |
struct v4l2_ext_control tmp_ctrl; |
608 |
struct v4l2_ext_controls ctrls; |
725 |
bzero(&tmp_ctrl, sizeof(struct v4l2_ext_control)); |
609 |
struct v4l2_ext_control ext_ctrl[kNumControls]; |
726 |
tmp_ctrl.id = id; |
610 |
QString control_description[kNumControls] = |
727 |
tmp_ctrl.value = value; |
|
|
728 |
ctrl_list.push_back(tmp_ctrl); |
729 |
} |
730 |
|
731 |
static void set_ctrls(int fd, vector<struct v4l2_ext_control> &ext_ctrls) |
732 |
{ |
733 |
static QMutex control_description_lock; |
734 |
static QMap<uint32_t,QString> control_description; |
735 |
|
736 |
control_description_lock.lock(); |
737 |
if (control_description.isEmpty()) |
611 |
{ |
738 |
{ |
612 |
"Audio Sampling Frequency", |
739 |
control_description[V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ] = |
613 |
"Video Aspect ratio", |
740 |
"Audio Sampling Frequency"; |
614 |
"Audio Encoding", |
741 |
control_description[V4L2_CID_MPEG_VIDEO_ASPECT] = |
615 |
"Audio L2 Bitrate", |
742 |
"Video Aspect ratio"; |
616 |
"Video Peak Bitrate", |
743 |
control_description[V4L2_CID_MPEG_AUDIO_ENCODING] = |
617 |
"Video Average Bitrate", |
744 |
"Audio Encoding"; |
618 |
"MPEG Stream type", |
745 |
control_description[V4L2_CID_MPEG_AUDIO_L2_BITRATE] = |
619 |
}; |
746 |
"Audio L2 Bitrate"; |
|
|
747 |
control_description[V4L2_CID_MPEG_VIDEO_BITRATE_PEAK] = |
748 |
"Video Peak Bitrate"; |
749 |
control_description[V4L2_CID_MPEG_VIDEO_BITRATE] = |
750 |
"Video Average Bitrate"; |
751 |
control_description[V4L2_CID_MPEG_STREAM_TYPE] = |
752 |
"MPEG Stream type"; |
753 |
control_description[V4L2_CID_MPEG_VIDEO_BITRATE_MODE] = |
754 |
"MPEG Bitrate mode"; |
755 |
} |
756 |
control_description_lock.unlock(); |
620 |
|
757 |
|
|
|
758 |
for (uint i = 0; i < ext_ctrls.size(); i++) |
759 |
{ |
760 |
struct v4l2_ext_controls ctrls; |
761 |
bzero(&ctrls, sizeof(struct v4l2_ext_controls)); |
762 |
|
763 |
int value = ext_ctrls[i].value; |
764 |
|
765 |
ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; |
766 |
ctrls.count = 1; |
767 |
ctrls.controls = &ext_ctrls[i]; |
768 |
|
769 |
if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) |
770 |
{ |
771 |
QMutexLocker locker(&control_description_lock); |
772 |
VERBOSE(VB_IMPORTANT, QString("mpegrecorder.cpp:set_ctrls(): ") + |
773 |
QString("Could not set %1 to %2") |
774 |
.arg(control_description[ext_ctrls[i].id]).arg(value) + |
775 |
ENO); |
776 |
} |
777 |
} |
778 |
} |
779 |
|
780 |
bool MpegRecorder::SetV4L2DeviceOptions(int chanfd) |
781 |
{ |
782 |
vector<struct v4l2_ext_control> ext_ctrls; |
783 |
|
621 |
// Set controls |
784 |
// Set controls |
622 |
bzero(&ctrls, sizeof(struct v4l2_ext_controls)); |
785 |
if (driver != "hdpvr") |
623 |
bzero(&ext_ctrl, sizeof(struct v4l2_ext_control) * kNumControls); |
786 |
{ |
|
|
787 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, |
788 |
GetFilteredAudioSampleRate()); |
789 |
|
790 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_ASPECT, |
791 |
aspectratio - 1); |
624 |
|
792 |
|
625 |
uint audio_layer = GetFilteredAudioLayer(); |
793 |
uint audio_layer = GetFilteredAudioLayer(); |
626 |
uint audbitrate = GetFilteredAudioBitRate(audio_layer); |
794 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_ENCODING, |
|
|
795 |
audio_layer - 1); |
796 |
|
797 |
uint audbitrate = GetFilteredAudioBitRate(audio_layer); |
798 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_AUDIO_L2_BITRATE, |
799 |
audbitrate - 1); |
627 |
|
800 |
|
628 |
ext_ctrl[0].id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ; |
801 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_STREAM_TYPE, |
629 |
ext_ctrl[0].value = GetFilteredAudioSampleRate(); |
802 |
streamtype_ivtv_to_v4l2(GetFilteredStreamType())); |
630 |
|
803 |
|
631 |
ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_ASPECT; |
804 |
} |
632 |
ext_ctrl[1].value = aspectratio - 1; |
805 |
else |
|
|
806 |
{ |
807 |
maxbitrate = high_mpeg4peakbitrate; |
808 |
bitrate = high_mpeg4avgbitrate; |
809 |
} |
810 |
maxbitrate = std::max(maxbitrate, bitrate); |
633 |
|
811 |
|
634 |
ext_ctrl[2].id = V4L2_CID_MPEG_AUDIO_ENCODING; |
812 |
if (driver == "hdpvr") |
635 |
ext_ctrl[2].value = audio_layer - 1; |
813 |
{ |
|
|
814 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, |
815 |
(maxbitrate == bitrate) ? |
816 |
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR : |
817 |
V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); |
818 |
} |
636 |
|
819 |
|
637 |
ext_ctrl[3].id = V4L2_CID_MPEG_AUDIO_L2_BITRATE; |
820 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE, |
638 |
ext_ctrl[3].value = audbitrate - 1; |
821 |
bitrate * 1000); |
639 |
|
822 |
|
640 |
ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK; |
823 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, |
641 |
ext_ctrl[4].value = maxbitrate * 1000; |
824 |
maxbitrate * 1000); |
642 |
|
825 |
|
643 |
ext_ctrl[5].id = V4L2_CID_MPEG_VIDEO_BITRATE; |
826 |
set_ctrls(chanfd, ext_ctrls); |
644 |
ext_ctrl[5].value = (bitrate = min(bitrate, maxbitrate)) * 1000; |
|
|
645 |
|
827 |
|
646 |
ext_ctrl[6].id = V4L2_CID_MPEG_STREAM_TYPE; |
828 |
bool ok; |
647 |
ext_ctrl[6].value = streamtype_ivtv_to_v4l2(GetFilteredStreamType()); |
829 |
int audioinput = audiodevice.toUInt(&ok); |
|
|
830 |
if (ok) |
831 |
{ |
832 |
struct v4l2_audio ain; |
833 |
bzero(&ain, sizeof(ain)); |
834 |
ain.index = audioinput; |
835 |
if (ioctl(chanfd, VIDIOC_ENUMAUDIO, &ain) < 0) |
836 |
{ |
837 |
VERBOSE(VB_IMPORTANT, LOC_WARN + |
838 |
"Unable to get audio input."); |
839 |
} |
840 |
else |
841 |
{ |
842 |
ain.index = audioinput; |
843 |
if (ioctl(chanfd, VIDIOC_S_AUDIO, &ain) < 0) |
844 |
{ |
845 |
VERBOSE(VB_IMPORTANT, LOC_WARN + |
846 |
"Unable to set audio input."); |
847 |
} |
848 |
} |
849 |
} |
648 |
|
850 |
|
649 |
for (uint i = 0; i < kNumControls; i++) |
851 |
if (driver != "hdpvr") |
650 |
{ |
852 |
{ |
651 |
int value = ext_ctrl[i].value; |
853 |
// Get GOP size in frames |
|
|
854 |
struct v4l2_ext_control ext_ctrl; |
855 |
struct v4l2_ext_controls ctrls; |
652 |
|
856 |
|
|
|
857 |
bzero(&ext_ctrl, sizeof(struct v4l2_ext_control)); |
858 |
bzero(&ctrls, sizeof(struct v4l2_ext_controls)); |
859 |
|
860 |
ext_ctrl.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; |
861 |
ext_ctrl.value = 0; |
862 |
|
653 |
ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; |
863 |
ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; |
654 |
ctrls.count = 1; |
864 |
ctrls.count = 1; |
655 |
ctrls.controls = ext_ctrl + i; |
865 |
ctrls.controls = &ext_ctrl; |
656 |
|
866 |
|
657 |
if (ioctl(chanfd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) |
867 |
if (ioctl(chanfd, VIDIOC_G_EXT_CTRLS, &ctrls) < 0) |
658 |
{ |
868 |
{ |
659 |
VERBOSE(VB_IMPORTANT, LOC_ERR + |
869 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get " |
660 |
QString("Could not set %1 to %2") |
870 |
"V4L2_CID_MPEG_VIDEO_GOP_SIZE, defaulting to 12" + ENO); |
661 |
.arg(control_description[i]).arg(value) + ENO); |
871 |
ext_ctrl.value = 12; |
662 |
} |
872 |
} |
663 |
} |
|
|
664 |
|
873 |
|
665 |
// Get controls |
874 |
_keyframedist = ext_ctrl.value; |
666 |
ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; |
|
|
667 |
ext_ctrl[0].value = 0; |
668 |
|
669 |
ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; |
670 |
ctrls.count = 1; |
671 |
ctrls.controls = ext_ctrl; |
672 |
|
673 |
if (ioctl(chanfd, VIDIOC_G_EXT_CTRLS, &ctrls) < 0) |
674 |
{ |
675 |
VERBOSE(VB_IMPORTANT, LOC_WARN + "Unable to get " |
676 |
"V4L2_CID_MPEG_VIDEO_GOP_SIZE, defaulting to 12" + ENO); |
677 |
ext_ctrl[0].value = 12; |
678 |
} |
875 |
} |
679 |
|
876 |
|
680 |
keyframedist = ext_ctrl[0].value; |
|
|
681 |
|
682 |
return true; |
877 |
return true; |
683 |
} |
878 |
} |
684 |
|
879 |
|
Lines 687-692
Link Here
|
687 |
if (!vbimode) |
882 |
if (!vbimode) |
688 |
return true; |
883 |
return true; |
689 |
|
884 |
|
|
|
885 |
if (driver == "hdpvr") |
886 |
return true; |
887 |
|
690 |
if (has_buggy_vbi) |
888 |
if (has_buggy_vbi) |
691 |
{ |
889 |
{ |
692 |
cout<<" *********************** WARNING ***********************"<<endl; |
890 |
cout<<" *********************** WARNING ***********************"<<endl; |
Lines 809-829
Link Here
|
809 |
{ |
1007 |
{ |
810 |
if (!Open()) |
1008 |
if (!Open()) |
811 |
{ |
1009 |
{ |
812 |
errored = true; |
1010 |
_error = true; |
813 |
return; |
1011 |
return; |
814 |
} |
1012 |
} |
815 |
|
1013 |
|
816 |
if (!SetupRecording()) |
1014 |
bool has_select = true; |
|
|
1015 |
|
1016 |
#if defined(__FreeBSD__) |
1017 |
// HACK. FreeBSD PVR150/500 driver doesn't currently support select() |
1018 |
has_select = false; |
1019 |
#endif |
1020 |
|
1021 |
_start_code = 0xffffffff; |
1022 |
_last_gop_seen = 0; |
1023 |
_frames_written_count = 0; |
1024 |
|
1025 |
if (driver == "hdpvr") |
817 |
{ |
1026 |
{ |
818 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to setup recording."); |
1027 |
SetPositionMapType(MARK_GOP_BYFRAME); |
819 |
errored = true; |
1028 |
|
820 |
return; |
1029 |
int progNum = 1; |
|
|
1030 |
MPEGStreamData *sd = new MPEGStreamData(progNum, true); |
1031 |
sd->SetRecordingType(_recording_type); |
1032 |
SetStreamData(sd); |
1033 |
|
1034 |
_stream_data->AddAVListener(this); |
1035 |
_stream_data->AddWritingListener(this); |
1036 |
|
1037 |
// Make sure the first things in the file are a PAT & PMT |
1038 |
_wait_for_keyframe_option = false; |
1039 |
HandleSingleProgramPAT(_stream_data->PATSingleProgram()); |
1040 |
HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); |
1041 |
_wait_for_keyframe_option = true; |
821 |
} |
1042 |
} |
|
|
1043 |
else |
1044 |
{ |
1045 |
SetPositionMapType(MARK_GOP_START); |
1046 |
} |
822 |
|
1047 |
|
823 |
encoding = true; |
1048 |
encoding = true; |
824 |
recording = true; |
1049 |
recording = true; |
825 |
unsigned char *buffer = new unsigned char[bufferSize + 1]; |
1050 |
unsigned char *buffer = new unsigned char[bufferSize + 1]; |
826 |
int ret; |
1051 |
int len; |
|
|
1052 |
uint remainder = 0; |
827 |
|
1053 |
|
828 |
MythTimer elapsedTimer; |
1054 |
MythTimer elapsedTimer; |
829 |
float elapsed; |
1055 |
float elapsed; |
Lines 833-1056
Link Here
|
833 |
|
1059 |
|
834 |
if (deviceIsMpegFile) |
1060 |
if (deviceIsMpegFile) |
835 |
elapsedTimer.start(); |
1061 |
elapsedTimer.start(); |
|
|
1062 |
else if (_device_read_buffer) |
1063 |
_device_read_buffer->Start(); |
836 |
|
1064 |
|
837 |
while (encoding) |
1065 |
needs_resolution = (driver == "hdpvr"); |
|
|
1066 |
|
1067 |
while (encoding && !_error) |
838 |
{ |
1068 |
{ |
839 |
if (PauseAndWait(100)) |
1069 |
if (PauseAndWait(100)) |
840 |
continue; |
1070 |
continue; |
|
|
1071 |
HandleResolutionChanges(); |
841 |
|
1072 |
|
842 |
if ((deviceIsMpegFile) && (framesWritten)) |
1073 |
if (deviceIsMpegFile) |
843 |
{ |
1074 |
{ |
844 |
elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; |
1075 |
if (GetFramesWritten()) |
845 |
while ((framesWritten / elapsed) > 30) |
|
|
846 |
{ |
1076 |
{ |
847 |
usleep(50000); |
|
|
848 |
elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; |
1077 |
elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; |
|
|
1078 |
while ((GetFramesWritten() / elapsed) > 30) |
1079 |
{ |
1080 |
usleep(50000); |
1081 |
elapsed = (elapsedTimer.elapsed() / 1000.0) + 1; |
1082 |
} |
849 |
} |
1083 |
} |
850 |
} |
1084 |
} |
|
|
1085 |
else |
1086 |
{ |
1087 |
if (readfd < 0) |
1088 |
{ |
1089 |
if (!Open()) |
1090 |
{ |
1091 |
_error = true; |
1092 |
return; |
1093 |
} |
851 |
|
1094 |
|
852 |
if (readfd < 0) |
1095 |
if (readfd < 0) |
853 |
readfd = open(videodevice.ascii(), O_RDWR); |
1096 |
{ |
|
|
1097 |
VERBOSE(VB_IMPORTANT, LOC_ERR + |
1098 |
QString("Failed to open device '%1'") |
1099 |
.arg(videodevice)); |
1100 |
continue; |
1101 |
} |
1102 |
} |
1103 |
} |
854 |
|
1104 |
|
855 |
tv.tv_sec = 5; |
1105 |
if (_device_read_buffer) |
856 |
tv.tv_usec = 0; |
|
|
857 |
FD_ZERO(&rdset); |
858 |
FD_SET(readfd, &rdset); |
859 |
|
860 |
#if defined(__FreeBSD__) |
861 |
// HACK. FreeBSD PVR150/500 driver doesn't currently support select() |
862 |
#else |
863 |
switch (select(readfd + 1, &rdset, NULL, NULL, &tv)) |
864 |
{ |
1106 |
{ |
865 |
case -1: |
1107 |
len = _device_read_buffer->Read( |
866 |
if (errno == EINTR) |
1108 |
&(buffer[remainder]), bufferSize - remainder); |
867 |
continue; |
|
|
868 |
|
1109 |
|
869 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Select error" + ENO); |
1110 |
// Check for DRB errors |
870 |
continue; |
1111 |
if (_device_read_buffer->IsErrored()) |
|
|
1112 |
{ |
1113 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Device error detected"); |
871 |
|
1114 |
|
872 |
case 0: |
1115 |
_device_read_buffer->Stop(); |
873 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "select timeout - " |
|
|
874 |
"ivtv driver has stopped responding"); |
875 |
|
1116 |
|
876 |
if (close(readfd) != 0) |
1117 |
QMutexLocker locker(&start_stop_encoding_lock); |
|
|
1118 |
|
1119 |
StopEncoding(readfd); |
1120 |
|
1121 |
// Make sure the next things in the file are a PAT & PMT |
1122 |
if (_stream_data->PATSingleProgram() && |
1123 |
_stream_data->PMTSingleProgram()) |
877 |
{ |
1124 |
{ |
878 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); |
1125 |
bool tmp = _wait_for_keyframe_option; |
|
|
1126 |
_wait_for_keyframe_option = false; |
1127 |
HandleSingleProgramPAT(_stream_data->PATSingleProgram()); |
1128 |
HandleSingleProgramPMT(_stream_data->PMTSingleProgram()); |
1129 |
_wait_for_keyframe_option = tmp; |
879 |
} |
1130 |
} |
880 |
|
1131 |
|
881 |
readfd = -1; // Force PVR card to be reopened on next iteration |
1132 |
if (StartEncoding(readfd)) |
882 |
continue; |
1133 |
{ |
|
|
1134 |
_device_read_buffer->Start(); |
1135 |
} |
1136 |
else |
1137 |
{ |
1138 |
if (0 != close(readfd)) |
1139 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO); |
883 |
|
1140 |
|
884 |
default: break; |
1141 |
// Force card to be reopened on next iteration.. |
|
|
1142 |
readfd = -1; |
1143 |
} |
1144 |
} |
1145 |
else if (_device_read_buffer->IsEOF()) |
1146 |
{ |
1147 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Device EOF detected"); |
1148 |
_error = true; |
1149 |
} |
885 |
} |
1150 |
} |
886 |
#endif |
1151 |
else |
|
|
1152 |
{ |
1153 |
if (has_select) |
1154 |
{ |
1155 |
tv.tv_sec = 5; |
1156 |
tv.tv_usec = 0; |
1157 |
FD_ZERO(&rdset); |
1158 |
FD_SET(readfd, &rdset); |
887 |
|
1159 |
|
888 |
ret = read(readfd, buffer, bufferSize); |
1160 |
switch (select(readfd + 1, &rdset, NULL, NULL, &tv)) |
|
|
1161 |
{ |
1162 |
case -1: |
1163 |
if (errno == EINTR) |
1164 |
continue; |
889 |
|
1165 |
|
890 |
if ((ret == 0) && |
1166 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "Select error" + ENO); |
891 |
(deviceIsMpegFile)) |
1167 |
continue; |
892 |
{ |
|
|
893 |
close(readfd); |
894 |
readfd = open(videodevice.ascii(), O_RDONLY); |
895 |
|
1168 |
|
896 |
if (readfd >= 0) |
1169 |
case 0: |
897 |
ret = read(readfd, buffer, bufferSize); |
1170 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "select timeout - " |
898 |
if (ret <= 0) |
1171 |
"driver has stopped responding"); |
|
|
1172 |
|
1173 |
if (close(readfd) != 0) |
1174 |
{ |
1175 |
VERBOSE(VB_IMPORTANT, LOC_ERR + |
1176 |
"Close error" + ENO); |
1177 |
} |
1178 |
|
1179 |
// Force card to be reopened on next iteration.. |
1180 |
readfd = -1; |
1181 |
continue; |
1182 |
|
1183 |
default: |
1184 |
break; |
1185 |
} |
1186 |
} |
1187 |
|
1188 |
len = read(readfd, &(buffer[remainder]), bufferSize - remainder); |
1189 |
|
1190 |
if (len < 0 && !has_select) |
899 |
{ |
1191 |
{ |
900 |
encoding = false; |
1192 |
usleep(25 * 1000); |
901 |
continue; |
1193 |
continue; |
902 |
} |
1194 |
} |
|
|
1195 |
|
1196 |
if ((len == 0) && (deviceIsMpegFile)) |
1197 |
{ |
1198 |
close(readfd); |
1199 |
readfd = open(videodevice.ascii(), O_RDONLY); |
1200 |
|
1201 |
if (readfd >= 0) |
1202 |
{ |
1203 |
len = read(readfd, |
1204 |
&(buffer[remainder]), bufferSize - remainder); |
1205 |
} |
1206 |
|
1207 |
if (len <= 0) |
1208 |
{ |
1209 |
encoding = false; |
1210 |
continue; |
1211 |
} |
1212 |
} |
1213 |
else if (len < 0 && errno != EAGAIN) |
1214 |
{ |
1215 |
VERBOSE(VB_IMPORTANT, |
1216 |
LOC_ERR + QString("error reading from: %1") |
1217 |
.arg(videodevice) + ENO); |
1218 |
continue; |
1219 |
} |
903 |
} |
1220 |
} |
904 |
else if (ret < 0 && errno != EAGAIN) |
1221 |
|
|
|
1222 |
if (len > 0) |
905 |
{ |
1223 |
{ |
906 |
VERBOSE(VB_IMPORTANT, LOC_ERR + QString("error reading from: %1") |
1224 |
len += remainder; |
907 |
.arg(videodevice) + ENO); |
|
|
908 |
|
1225 |
|
909 |
continue; |
1226 |
if (driver == "hdpvr") { |
|
|
1227 |
remainder = _stream_data->ProcessData(buffer, len); |
1228 |
int start_remain = len - remainder; |
1229 |
if (remainder && (start_remain >= remainder)) |
1230 |
memcpy(buffer, buffer+start_remain, remainder); |
1231 |
else if (remainder) |
1232 |
memmove(buffer, buffer+start_remain, remainder); |
1233 |
} |
1234 |
else |
1235 |
{ |
1236 |
FindPSKeyFrames(buffer, len); |
1237 |
} |
910 |
} |
1238 |
} |
911 |
else if (ret > 0) |
|
|
912 |
{ |
913 |
ProcessData(buffer, ret); |
914 |
} |
915 |
} |
1239 |
} |
916 |
|
1240 |
|
|
|
1241 |
if (_device_read_buffer) |
1242 |
{ |
1243 |
if (_device_read_buffer->IsRunning()) |
1244 |
_device_read_buffer->Stop(); |
1245 |
|
1246 |
delete _device_read_buffer; |
1247 |
_device_read_buffer = NULL; |
1248 |
} |
1249 |
StopEncoding(readfd); |
1250 |
|
917 |
FinishRecording(); |
1251 |
FinishRecording(); |
918 |
|
1252 |
|
919 |
delete[] buffer; |
1253 |
delete[] buffer; |
|
|
1254 |
SetStreamData(NULL); |
920 |
recording = false; |
1255 |
recording = false; |
|
|
1256 |
QMutexLocker locker(&recording_wait_lock); |
1257 |
recording_wait.wakeAll(); |
921 |
} |
1258 |
} |
922 |
|
1259 |
|
923 |
bool MpegRecorder::SetupRecording(void) |
1260 |
bool MpegRecorder::ProcessTSPacket(const TSPacket &tspacket_real) |
924 |
{ |
1261 |
{ |
925 |
leftovers = 0xFFFFFFFF; |
1262 |
const uint pid = tspacket_real.PID(); |
926 |
numgops = 0; |
|
|
927 |
lastseqstart = 0; |
928 |
return true; |
929 |
} |
930 |
|
1263 |
|
931 |
void MpegRecorder::FinishRecording(void) |
1264 |
TSPacket *tspacket_fake = NULL; |
932 |
{ |
1265 |
if ((driver == "hdpvr") && (pid == 0x1001)) // PCRPID for HD-PVR |
933 |
ringBuffer->WriterFlush(); |
|
|
934 |
|
935 |
if (curRecording) |
936 |
{ |
1266 |
{ |
937 |
curRecording->SetFilesize(ringBuffer->GetRealFileSize()); |
1267 |
tspacket_fake = tspacket_real.CreateClone(); |
938 |
SavePositionMap(true); |
1268 |
uint cc = (_continuity_counter[pid] == 0xFF) ? |
|
|
1269 |
0 : (_continuity_counter[pid] + 1) & 0xf; |
1270 |
tspacket_fake->SetContinuityCounter(cc); |
939 |
} |
1271 |
} |
940 |
positionMapLock.lock(); |
|
|
941 |
positionMap.clear(); |
942 |
positionMapDelta.clear(); |
943 |
positionMapLock.unlock(); |
944 |
} |
945 |
|
1272 |
|
946 |
#define PACK_HEADER 0x000001BA |
1273 |
const TSPacket *tspacket = (tspacket_fake) ? |
947 |
#define GOP_START 0x000001B8 |
1274 |
tspacket_fake : &tspacket_real; |
948 |
#define SEQ_START 0x000001B3 |
|
|
949 |
#define SLICE_MIN 0x00000101 |
950 |
#define SLICE_MAX 0x000001af |
951 |
|
1275 |
|
952 |
void MpegRecorder::ProcessData(unsigned char *buffer, int len) |
1276 |
// Check continuity counter |
953 |
{ |
1277 |
if ((pid != 0x1fff) && !CheckCC(pid, tspacket->ContinuityCounter())) |
954 |
unsigned char *bufptr = buffer, *bufstart = buffer; |
1278 |
{ |
955 |
unsigned int state = leftovers, v = 0; |
1279 |
VERBOSE(VB_RECORD, LOC + |
956 |
int leftlen = len; |
1280 |
QString("PID 0x%1 discontinuity detected").arg(pid,0,16)); |
|
|
1281 |
_continuity_error_count++; |
1282 |
} |
957 |
|
1283 |
|
958 |
while (bufptr < buffer + len) |
1284 |
// Only write the packet |
|
|
1285 |
// if audio/video key-frames have been found |
1286 |
if (!(_wait_for_keyframe_option && _first_keyframe < 0)) |
959 |
{ |
1287 |
{ |
960 |
v = *bufptr++; |
1288 |
_buffer_packets = true; |
961 |
if (state == 0x000001) |
|
|
962 |
{ |
963 |
state = ((state << 8) | v) & 0xFFFFFF; |
964 |
|
965 |
if (state == PACK_HEADER) |
966 |
{ |
967 |
long long startpos = ringBuffer->GetWritePosition(); |
968 |
startpos += buildbuffersize + bufptr - bufstart - 4; |
969 |
lastpackheaderpos = startpos; |
970 |
|
1289 |
|
971 |
int curpos = bufptr - bufstart - 4; |
1290 |
BufferedWrite(*tspacket); |
972 |
if (curpos < 0) |
1291 |
} |
973 |
{ |
|
|
974 |
// header was split |
975 |
buildbuffersize += curpos; |
976 |
if (buildbuffersize > 0) |
977 |
ringBuffer->Write(buildbuffer, buildbuffersize); |
978 |
|
1292 |
|
979 |
buildbuffersize = 4; |
1293 |
if (tspacket_fake) |
980 |
memcpy(buildbuffer, &state, 4); |
1294 |
delete tspacket_fake; |
981 |
|
1295 |
|
982 |
leftlen = leftlen - curpos + 4; |
1296 |
return true; |
983 |
bufstart = bufptr; |
1297 |
} |
984 |
} |
|
|
985 |
else |
986 |
{ |
987 |
// header was entirely in this packet |
988 |
memcpy(buildbuffer + buildbuffersize, bufstart, curpos); |
989 |
buildbuffersize += curpos; |
990 |
bufstart += curpos; |
991 |
leftlen -= curpos; |
992 |
|
1298 |
|
993 |
if (buildbuffersize > 0) |
1299 |
bool MpegRecorder::ProcessVideoTSPacket(const TSPacket &tspacket) |
994 |
ringBuffer->Write(buildbuffer, buildbuffersize); |
1300 |
{ |
|
|
1301 |
_buffer_packets = !FindH264Keyframes(&tspacket); |
1302 |
if (!_seen_sps) |
1303 |
return true; |
995 |
|
1304 |
|
996 |
buildbuffersize = 0; |
1305 |
return ProcessAVTSPacket(tspacket); |
997 |
} |
1306 |
} |
998 |
} |
|
|
999 |
|
1307 |
|
1000 |
if (state == SEQ_START) |
1308 |
bool MpegRecorder::ProcessAudioTSPacket(const TSPacket &tspacket) |
1001 |
{ |
1309 |
{ |
1002 |
lastseqstart = lastpackheaderpos; |
1310 |
_buffer_packets = !FindAudioKeyframes(&tspacket); |
1003 |
} |
1311 |
return ProcessAVTSPacket(tspacket); |
|
|
1312 |
} |
1004 |
|
1313 |
|
1005 |
if (state == GOP_START && lastseqstart == lastpackheaderpos) |
1314 |
/// Common code for processing either audio or video packets |
1006 |
{ |
1315 |
bool MpegRecorder::ProcessAVTSPacket(const TSPacket &tspacket) |
1007 |
framesWritten = numgops * keyframedist; |
1316 |
{ |
1008 |
numgops++; |
1317 |
const uint pid = tspacket.PID(); |
1009 |
HandleKeyframe(); |
1318 |
|
1010 |
} |
1319 |
// Check continuity counter |
1011 |
} |
1320 |
if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter())) |
1012 |
else |
1321 |
{ |
1013 |
state = ((state << 8) | v) & 0xFFFFFF; |
1322 |
VERBOSE(VB_RECORD, LOC + |
|
|
1323 |
QString("PID 0x%1 discontinuity detected").arg(pid,0,16)); |
1324 |
_continuity_error_count++; |
1014 |
} |
1325 |
} |
1015 |
|
1326 |
|
1016 |
leftovers = state; |
1327 |
// Sync recording start to first keyframe |
|
|
1328 |
if (_wait_for_keyframe_option && _first_keyframe < 0) |
1329 |
return true; |
1017 |
|
1330 |
|
1018 |
if (buildbuffersize + leftlen > kBuildBufferMaxSize) |
1331 |
// Sync streams to the first Payload Unit Start Indicator |
|
|
1332 |
// _after_ first keyframe iff _wait_for_keyframe_option is true |
1333 |
if (!(_pid_status[pid] & kPayloadStartSeen) && tspacket.HasPayload()) |
1019 |
{ |
1334 |
{ |
1020 |
ringBuffer->Write(buildbuffer, buildbuffersize); |
1335 |
if (!tspacket.PayloadStart()) |
1021 |
buildbuffersize = 0; |
1336 |
return true; // not payload start - drop packet |
|
|
1337 |
|
1338 |
VERBOSE(VB_RECORD, |
1339 |
QString("PID 0x%1 Found Payload Start").arg(pid,0,16)); |
1340 |
|
1341 |
_pid_status[pid] |= kPayloadStartSeen; |
1022 |
} |
1342 |
} |
1023 |
|
1343 |
|
1024 |
// copy remaining.. |
1344 |
BufferedWrite(tspacket); |
1025 |
memcpy(buildbuffer + buildbuffersize, bufstart, leftlen); |
1345 |
|
1026 |
buildbuffersize += leftlen; |
1346 |
return true; |
1027 |
} |
1347 |
} |
1028 |
|
1348 |
|
1029 |
void MpegRecorder::StopRecording(void) |
1349 |
void MpegRecorder::StopRecording(void) |
1030 |
{ |
1350 |
{ |
1031 |
encoding = false; |
1351 |
QMutexLocker locker(&recording_wait_lock); |
|
|
1352 |
if (encoding) { |
1353 |
encoding = false; |
1354 |
recording_wait.wait(&recording_wait_lock); |
1355 |
} |
1032 |
} |
1356 |
} |
1033 |
|
1357 |
|
1034 |
void MpegRecorder::ResetForNewFile(void) |
1358 |
void MpegRecorder::ResetForNewFile(void) |
1035 |
{ |
1359 |
{ |
1036 |
errored = false; |
1360 |
DTVRecorder::ResetForNewFile(); |
1037 |
framesWritten = 0; |
|
|
1038 |
numgops = 0; |
1039 |
lastseqstart = lastpackheaderpos = 0; |
1040 |
|
1361 |
|
1041 |
positionMap.clear(); |
1362 |
bzero(_stream_id, sizeof(_stream_id)); |
1042 |
positionMapDelta.clear(); |
1363 |
bzero(_pid_status, sizeof(_pid_status)); |
|
|
1364 |
memset(_continuity_counter, 0xff, sizeof(_continuity_counter)); |
1043 |
} |
1365 |
} |
1044 |
|
1366 |
|
1045 |
void MpegRecorder::Reset(void) |
1367 |
void MpegRecorder::Reset(void) |
1046 |
{ |
1368 |
{ |
|
|
1369 |
VERBOSE(VB_RECORD, LOC + "Reset(void)"); |
1047 |
ResetForNewFile(); |
1370 |
ResetForNewFile(); |
1048 |
|
1371 |
|
1049 |
leftovers = 0xFFFFFFFF; |
1372 |
_start_code = 0xffffffff; |
1050 |
buildbuffersize = 0; |
|
|
1051 |
|
1373 |
|
1052 |
if (curRecording) |
1374 |
if (curRecording) |
1053 |
curRecording->ClearPositionMap(MARK_GOP_START); |
1375 |
{ |
|
|
1376 |
curRecording->ClearPositionMap( |
1377 |
(driver == "hdpvr") ? MARK_GOP_BYFRAME : MARK_GOP_START); |
1378 |
} |
1379 |
if (_stream_data) |
1380 |
_stream_data->Reset(_stream_data->DesiredProgram()); |
1054 |
} |
1381 |
} |
1055 |
|
1382 |
|
1056 |
void MpegRecorder::Pause(bool clear) |
1383 |
void MpegRecorder::Pause(bool clear) |
Lines 1064-1152
Link Here
|
1064 |
{ |
1391 |
{ |
1065 |
if (request_pause) |
1392 |
if (request_pause) |
1066 |
{ |
1393 |
{ |
|
|
1394 |
QMutex waitlock; |
1395 |
waitlock.lock(); |
1396 |
|
1067 |
if (!paused) |
1397 |
if (!paused) |
1068 |
{ |
1398 |
{ |
1069 |
if (requires_special_pause) |
1399 |
if (_device_read_buffer) |
1070 |
{ |
1400 |
{ |
1071 |
// Some ivtv drivers require streaming to be disabled before |
1401 |
QMutex drb_lock; |
1072 |
// an input switch and other channel format setting. |
1402 |
drb_lock.lock(); |
1073 |
struct v4l2_encoder_cmd command; |
1403 |
|
1074 |
memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); |
1404 |
_device_read_buffer->SetRequestPause(true); |
1075 |
command.cmd = V4L2_ENC_CMD_STOP; |
1405 |
|
1076 |
ioctl(readfd, VIDIOC_ENCODER_CMD, &command); |
1406 |
pauseWait.wait(&drb_lock, timeout); |
1077 |
} |
1407 |
} |
|
|
1408 |
else |
1409 |
{ |
1410 |
paused = true; |
1411 |
pauseWait.wakeAll(); |
1412 |
} |
1078 |
|
1413 |
|
1079 |
paused = true; |
1414 |
// Some drivers require streaming to be disabled before |
1080 |
pauseWait.wakeAll(); |
1415 |
// an input switch and other channel format setting. |
|
|
1416 |
if (requires_special_pause) |
1417 |
StopEncoding(readfd); |
1418 |
|
1081 |
if (tvrec) |
1419 |
if (tvrec) |
1082 |
tvrec->RecorderPaused(); |
1420 |
tvrec->RecorderPaused(); |
1083 |
} |
1421 |
} |
|
|
1422 |
|
1084 |
unpauseWait.wait(timeout); |
1423 |
unpauseWait.wait(timeout); |
1085 |
} |
1424 |
} |
1086 |
if (!request_pause) |
1425 |
if (!request_pause) |
1087 |
{ |
1426 |
{ |
1088 |
if (paused) |
1427 |
if (paused) |
1089 |
{ |
1428 |
{ |
|
|
1429 |
// Some drivers require streaming to be disabled before |
1430 |
// an input switch and other channel format setting. |
1090 |
if (requires_special_pause) |
1431 |
if (requires_special_pause) |
1091 |
{ |
1432 |
StartEncoding(readfd); |
1092 |
// Some ivtv drivers require streaming to be disabled before |
1433 |
|
1093 |
// an input switch and other channel format setting. |
1434 |
if (_device_read_buffer) |
1094 |
struct v4l2_encoder_cmd command; |
1435 |
_device_read_buffer->SetRequestPause(false); |
1095 |
command.cmd = V4L2_ENC_CMD_START; |
1436 |
|
1096 |
ioctl(readfd, VIDIOC_ENCODER_CMD, &command); |
1437 |
if (_stream_data) |
1097 |
} |
1438 |
_stream_data->Reset(_stream_data->DesiredProgram()); |
1098 |
} |
1439 |
} |
1099 |
paused = false; |
1440 |
paused = false; |
1100 |
} |
1441 |
} |
1101 |
return paused; |
1442 |
return paused; |
1102 |
} |
1443 |
} |
1103 |
|
1444 |
|
1104 |
long long MpegRecorder::GetKeyframePosition(long long desired) |
1445 |
bool MpegRecorder::StartEncoding(int fd) |
1105 |
{ |
1446 |
{ |
1106 |
QMutexLocker locker(&positionMapLock); |
1447 |
QMutexLocker locker(&start_stop_encoding_lock); |
1107 |
long long ret = -1; |
|
|
1108 |
|
1448 |
|
1109 |
if (positionMap.find(desired) != positionMap.end()) |
1449 |
struct v4l2_encoder_cmd command; |
1110 |
ret = positionMap[desired]; |
1450 |
memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); |
|
|
1451 |
command.cmd = V4L2_ENC_CMD_START; |
1111 |
|
1452 |
|
1112 |
return ret; |
1453 |
VERBOSE(VB_RECORD, LOC + "StartEncoding"); |
|
|
1454 |
needs_resolution = (driver == "hdpvr"); |
1455 |
|
1456 |
for (int idx = 0; idx < 10; ++idx) |
1457 |
{ |
1458 |
if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) |
1459 |
{ |
1460 |
VERBOSE(VB_RECORD, LOC + "Encoding started"); |
1461 |
return true; |
1462 |
} |
1463 |
|
1464 |
if (errno != EAGAIN) |
1465 |
{ |
1466 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "StartEncoding" + ENO); |
1467 |
return false; |
1468 |
} |
1469 |
|
1470 |
usleep(250 * 1000); |
1471 |
} |
1472 |
|
1473 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "StartEncoding - giving up" + ENO); |
1474 |
return false; |
1113 |
} |
1475 |
} |
1114 |
|
1476 |
|
1115 |
// documented in recorderbase.h |
1477 |
bool MpegRecorder::StopEncoding(int fd) |
1116 |
void MpegRecorder::SetNextRecording(const ProgramInfo *progInf, RingBuffer *rb) |
|
|
1117 |
{ |
1478 |
{ |
1118 |
// First we do some of the time consuming stuff we can do now |
1479 |
QMutexLocker locker(&start_stop_encoding_lock); |
1119 |
SavePositionMap(true); |
|
|
1120 |
ringBuffer->WriterFlush(); |
1121 |
if (curRecording) |
1122 |
curRecording->SetFilesize(ringBuffer->GetRealFileSize()); |
1123 |
|
1480 |
|
1124 |
// Then we set the next info |
1481 |
struct v4l2_encoder_cmd command; |
|
|
1482 |
memset(&command, 0, sizeof(struct v4l2_encoder_cmd)); |
1483 |
command.cmd = V4L2_ENC_CMD_STOP; |
1484 |
|
1485 |
VERBOSE(VB_RECORD, LOC + "StopEncoding"); |
1486 |
|
1487 |
for (int idx = 0; idx < 10; ++idx) |
1125 |
{ |
1488 |
{ |
1126 |
QMutexLocker locker(&nextRingBufferLock); |
1489 |
|
1127 |
nextRecording = NULL; |
1490 |
if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0) |
1128 |
if (progInf) |
1491 |
{ |
1129 |
nextRecording = new ProgramInfo(*progInf); |
1492 |
VERBOSE(VB_RECORD, LOC + "Encoding stopped"); |
1130 |
nextRingBuffer = rb; |
1493 |
return true; |
|
|
1494 |
} |
1495 |
|
1496 |
if (errno != EAGAIN) |
1497 |
{ |
1498 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "StopEncoding" + ENO); |
1499 |
return false; |
1500 |
} |
1501 |
|
1502 |
usleep(250 * 1000); |
1131 |
} |
1503 |
} |
|
|
1504 |
|
1505 |
VERBOSE(VB_IMPORTANT, LOC_ERR + "StopEncoding - giving up" + ENO); |
1506 |
return false; |
1132 |
} |
1507 |
} |
1133 |
|
1508 |
|
1134 |
/** \fn MpegRecorder::HandleKeyframe(void) |
1509 |
void MpegRecorder::SetStreamData(MPEGStreamData *data) |
1135 |
* \brief This save the current frame to the position maps |
|
|
1136 |
* and handles ringbuffer switching. |
1137 |
*/ |
1138 |
void MpegRecorder::HandleKeyframe(void) |
1139 |
{ |
1510 |
{ |
1140 |
// Add key frame to position map |
1511 |
VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- begin"); |
1141 |
positionMapLock.lock(); |
1512 |
|
1142 |
if (!positionMap.contains(numgops)) |
1513 |
if (data == _stream_data) |
1143 |
{ |
1514 |
{ |
1144 |
positionMapDelta[numgops] = lastpackheaderpos; |
1515 |
VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 0"); |
1145 |
positionMap[numgops] = lastpackheaderpos; |
1516 |
|
|
|
1517 |
return; |
1146 |
} |
1518 |
} |
1147 |
positionMapLock.unlock(); |
|
|
1148 |
|
1519 |
|
1149 |
// Perform ringbuffer switch if needed. |
1520 |
MPEGStreamData *old_data = _stream_data; |
1150 |
CheckForRingBufferSwitch(); |
1521 |
_stream_data = data; |
|
|
1522 |
if (old_data) |
1523 |
delete old_data; |
1524 |
|
1525 |
if (data) |
1526 |
{ |
1527 |
data->AddMPEGSPListener(this); |
1528 |
data->SetDesiredProgram(1); |
1529 |
} |
1530 |
|
1531 |
VERBOSE(VB_RECORD, LOC + "SetStreamData("<<data<<") -- end 1"); |
1151 |
} |
1532 |
} |
1152 |
|
1533 |
|
|
|
1534 |
void MpegRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat) |
1535 |
{ |
1536 |
if (!pat) |
1537 |
{ |
1538 |
VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)"); |
1539 |
return; |
1540 |
} |
1541 |
|
1542 |
if (!ringBuffer) |
1543 |
return; |
1544 |
|
1545 |
// uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; |
1546 |
|
1547 |
uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf; |
1548 |
pat->tsheader()->SetContinuityCounter(next_cc); |
1549 |
DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader()))); |
1550 |
|
1551 |
// uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; |
1552 |
|
1553 |
#if 0 |
1554 |
if (posB[0] + posB[1] * TSPacket::SIZE > |
1555 |
posA[0] + posA[1] * TSPacket::SIZE) |
1556 |
{ |
1557 |
VERBOSE(VB_RECORD, LOC + "Wrote PAT @" |
1558 |
<< posA[0] << " + " << (posA[1] * TSPacket::SIZE)); |
1559 |
} |
1560 |
else |
1561 |
{ |
1562 |
VERBOSE(VB_RECORD, LOC + "Saw PAT but did not write to disk yet"); |
1563 |
} |
1564 |
#endif |
1565 |
} |
1566 |
|
1567 |
void MpegRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt) |
1568 |
{ |
1569 |
if (!pmt) |
1570 |
{ |
1571 |
return; |
1572 |
} |
1573 |
|
1574 |
// collect stream types for H.264 (MPEG-4 AVC) keyframe detection |
1575 |
for (uint i = 0; i < pmt->StreamCount(); i++) |
1576 |
_stream_id[pmt->StreamPID(i)] = pmt->StreamType(i); |
1577 |
|
1578 |
if (!ringBuffer) |
1579 |
return; |
1580 |
|
1581 |
unsigned char buf[8 * 1024]; |
1582 |
uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf; |
1583 |
pmt->tsheader()->SetContinuityCounter(next_cc); |
1584 |
uint size = pmt->WriteAsTSPackets(buf, next_cc); |
1585 |
|
1586 |
uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; |
1587 |
|
1588 |
for (uint i = 0; i < size ; i += TSPacket::SIZE) |
1589 |
DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i]))); |
1590 |
|
1591 |
uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; |
1592 |
|
1593 |
#if 0 |
1594 |
if (posB[0] + posB[1] * TSPacket::SIZE > |
1595 |
posA[0] + posA[1] * TSPacket::SIZE) |
1596 |
{ |
1597 |
VERBOSE(VB_RECORD, LOC + "Wrote PMT @" |
1598 |
<< posA[0] << " + " << (posA[1] * TSPacket::SIZE)); |
1599 |
} |
1600 |
else |
1601 |
{ |
1602 |
VERBOSE(VB_RECORD, LOC + "Saw PMT but did not write to disk yet"); |
1603 |
} |
1604 |
#endif |
1605 |
} |
1606 |
|
1607 |
void MpegRecorder::HandleResolutionChanges(void) |
1608 |
{ |
1609 |
if (!needs_resolution) |
1610 |
return; |
1611 |
|
1612 |
VERBOSE(VB_RECORD, LOC + "Checking Resolution"); |
1613 |
struct v4l2_format vfmt; |
1614 |
memset(&vfmt, 0, sizeof(vfmt)); |
1615 |
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
1616 |
|
1617 |
uint pix = 0; |
1618 |
if (0 == ioctl(chanfd, VIDIOC_G_FMT, &vfmt)) |
1619 |
{ |
1620 |
VERBOSE(VB_RECORD, LOC + QString("Got Resolution %1x%2") |
1621 |
.arg(vfmt.fmt.pix.width).arg(vfmt.fmt.pix.height)); |
1622 |
pix = vfmt.fmt.pix.width * vfmt.fmt.pix.height; |
1623 |
needs_resolution = false; |
1624 |
} |
1625 |
|
1626 |
if (!pix) |
1627 |
return; // nothing to do, we don't have a resolution yet |
1628 |
|
1629 |
int old_max = maxbitrate, old_avg = bitrate; |
1630 |
if (pix <= 768*568) |
1631 |
{ |
1632 |
maxbitrate = low_mpeg4peakbitrate; |
1633 |
bitrate = low_mpeg4avgbitrate; |
1634 |
} |
1635 |
else if (pix >= 1920*1080) |
1636 |
{ |
1637 |
maxbitrate = high_mpeg4peakbitrate; |
1638 |
bitrate = high_mpeg4avgbitrate; |
1639 |
} |
1640 |
else |
1641 |
{ |
1642 |
maxbitrate = medium_mpeg4peakbitrate; |
1643 |
bitrate = medium_mpeg4avgbitrate; |
1644 |
} |
1645 |
maxbitrate = std::max(maxbitrate, bitrate); |
1646 |
|
1647 |
if ((old_max != maxbitrate) || (old_avg != bitrate)) |
1648 |
{ |
1649 |
if (old_max == old_avg) |
1650 |
{ |
1651 |
VERBOSE(VB_RECORD, LOC + |
1652 |
QString("Old bitrate %1 CBR").arg(old_avg)); |
1653 |
} |
1654 |
else |
1655 |
{ |
1656 |
VERBOSE(VB_RECORD, LOC + |
1657 |
QString("Old bitrate %1/%2 VBR") |
1658 |
.arg(old_avg).arg(old_max)); |
1659 |
} |
1660 |
|
1661 |
if (maxbitrate == bitrate) |
1662 |
{ |
1663 |
VERBOSE(VB_RECORD, LOC + QString("New bitrate %1 kbps CBR") |
1664 |
.arg(bitrate)); |
1665 |
} |
1666 |
else |
1667 |
{ |
1668 |
VERBOSE(VB_RECORD, LOC + QString("New bitrate %1/%2 kbps VBR") |
1669 |
.arg(bitrate).arg(maxbitrate)); |
1670 |
} |
1671 |
|
1672 |
vector<struct v4l2_ext_control> ext_ctrls; |
1673 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, |
1674 |
(maxbitrate == bitrate) ? |
1675 |
V4L2_MPEG_VIDEO_BITRATE_MODE_CBR : |
1676 |
V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); |
1677 |
|
1678 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE, |
1679 |
bitrate * 1000); |
1680 |
|
1681 |
add_ext_ctrl(ext_ctrls, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, |
1682 |
maxbitrate * 1000); |
1683 |
|
1684 |
set_ctrls(readfd, ext_ctrls); |
1685 |
} |
1686 |
|
1687 |
// Restart streaming. Shouldn't be needed? seems to be with current driver. |
1688 |
QMutexLocker locker(&start_stop_encoding_lock); |
1689 |
StopEncoding(readfd); |
1690 |
StartEncoding(readfd); |
1691 |
|
1692 |
needs_resolution = false; |
1693 |
} |