[mythtv] Random blockiness that probably isn't CPU or PCI bus
bandwidth-related
Eric Anderson
rico99 at sbcglobal.net
Tue Jan 18 17:40:55 EST 2005
Kyle -
I have been seeing a very similar problem on my system (2.6-GHz
hyperthreaded P4)
with two HD-3000 cards. And it doesn't happen every time. It actually
seems to happen
more often with the latest CVS (with John Poet's ringbuffer code). With
my own version
of the ringbuffer code substituted, it happens a lot less. I'm not
sure why. With the latest
CVS, it happens maybe 1 out of 5 recordings or so? With my version of
the ringbuffer
code, maybe 1 time out of 20-30 recordings. (Hard to say, haven't seen
it that often
lately.)
[I'll attach my version of the ringbuffer if you want to try it... John
has claimed he wasn't
able to get the performance he needed for recording with 3 cards
without using a highly
tuned version, but I'm not sure if he ever tried my final version of
the ringbuffer. One
difference is that my version uses a single wrap-around buffer and
works out of that
buffer -- in an attempt to reduce the number of extra copies.]
When the bug occurs, data starts being corrupted part way through a
recording. And
it continues to be corrupted pretty much until the end of the
recording. I was wondering
exactly what was broken in the recording...
So yesterday I wrote a small program to scan through the saved
transport stream to try to
figure out *how* the data was corrupted. What I found is sets of
duplicate packets. Thus,
I am starting to think that this is a driver issue.
I've attached a test program (xsum188.c) in case you want to see if
you're running into
the same problem. The program is a quick hack, but it may help reveal
if you have
repeated data in your stream. It computes checksums over the first
50,000 TS packets
that it sees. And then it runs 50K x 50K comparisons on all of the
checksums that it
sees and reports duplicates. (Doesn't handle cases of >2 duplicates.
Probably could
be extended to do that...)
Here is an example of what I see:
% gcc -o xsum188 -O3 xsum188.c
% dd if=bad.nuv bs=188 skip=7416480 count=50000 > bad_short.nuv
% ./xsum188 < bad_short.nuv > bad_short.nuv.cksum
% grep GOP bad_short.nuv.cksum
pkt:00000 [4760313d] 58d0b676 [GOP t=0b2d6780]
pkt:07727 [47603135] ce418a12 [GOP t=0b2da780]
pkt:10694 [4760313f] 889c74e2 ! del= +830[GOP t=0b2dc780]
pkt:11524 [4760313f] 889c74e2 ! del= -830[GOP t=0b2dc780]
pkt:16110 [47603133] 4b545ea0 [GOP t=0b2de780]
pkt:20839 [47603133] 09e2492f [GOP t=0b2e0780]
pkt:25554 [4760313e] ca5db35d [GOP t=0b2e2780]
pkt:30033 [47603134] 846c1d6d [GOP t=0b2e4780]
pkt:33642 [47603138] 2da8073b [GOP t=0b2e6780]
pkt:38316 [47603137] e91870ad [GOP t=0b2e8780]
Notice how I got a duplicate GOP for t=0xb2dc780. And in fact, the
timestamps
went d6, da, dc, dc, de, e0, etc... In other words, we skipped d8, and
doubled
up on dc. This is consistent with d8 being overwritten with da, da
being overwritten
with dc, and then the overruns stopping and going back to normal (ie
dc, de, e0...).
In fact, I had started out by looking only at the GOP timestamps. But
then I
decided to also compare checksums of the TS packets. So if I look at the
data around the duplicate GOP, I see this:
pkt:10682 [47203116] 244d45e9
pkt:10683 [47203117] 75642fb2
pkt:10684 [4720341e] 74b3955f
pkt:10685 [47203117] b33620aa ! del= +830
pkt:10686 [47203118] 1a32c97b ! del= +830
pkt:10687 [47203119] 27d35eb9 ! del= +830
pkt:10688 [4720311a] 3db1ba3e ! del= +830
pkt:10689 [4720311b] a7bd387d ! del= +830
pkt:10690 [4720311c] ab3376b4 ! del= +830
pkt:10691 [4720341f] 1c8df000 ! del= +830
pkt:10692 [4720311d] 83325a60 ! del= +830
pkt:10693 [4720311e] b80ef2d7 ! del= +830
pkt:10694 [4760313f] 889c74e2 ! del= +830[GOP t=0b2dc780]
pkt:10695 [47203110] a72a617b ! del= +830
pkt:10696 [47203111] 203a8d7e ! del= +830
pkt:10697 [47203112] d595a7df ! del= +830
pkt:10698 [47203113] 18091d71 ! del= +830
pkt:10699 [47203114] 5608e305 ! del= +830
pkt:10700 [47203115] bf8acfbe ! del= +830
pkt:10701 [47203116] faadfdf0 ! del= +830
pkt:10702 [47203117] 70c50c73 ! del= +830
pkt:10703 [47203118] 389f6833 ! del= +830
pkt:10704 [47203119] b56a1ee9 ! del= +830
pkt:10705 [4720311e] 88aeb213
pkt:10706 [4720311f] a6da992f
pkt:10707 [47203110] cbe19a29
So this sequence of packets (b33620aa, 1a32c97b, etc...) appear later
on in the
stream. In fact, 830 packets later. Thus there must have been an
overrun of some
kind. But I did *NOT* get a message from the driver in my kernel log
while this
stream was being recorded. I only found out it was corrupted when I
tried to play
it back. (And it's pretty once the dups start happening.) Note that the
amount of
packets between the duplicates varies between 753 and 1148. [Since not
all packets
received are part of the stream being recorded, it is conceivable to
see variation like
this.]
I've only looked at a few places so I can't say for certain I
understand everything
that's going on. But I do know *somewhere* along the line I'm getting
duplicated
runs of packets.
Note that if I run this same program on a "good" stream, I see no
duplicates.
(Other than PAT packets, etc. -- but I've made an attempt to filter
those out using
the program.)
I would be interested to know if others have seen similar problems.
My particulars:
Fedora Core 2 - Kernel rev 2.6.5
HD3000 driver rev 1.4 (Note: I tweaked the buffer size in
cx88-atsc.c to
be 188*1536 instead of
188*512. And I disabled
the audio thread in
cx88-video.c that just prints
messages and doesn't
seem to do anything else. I still need
to try reverting this
to a stock version to see if it helps.)
Latest myth CVS (w/ John Poet's ringbuffer code) -- seems to happen
more often with this
Latest myth CVS (w/ my version of ringbuffer code) -- I've seen this
only once in the last week
Pentium 4 @ 2.6-GHz Hyperthreading enabled
2 x HD3000 cards
1 x Sony gigapocket tuner card (using ivtv) -- not recording when
this bug occurs
Attachments:
xsum188.c -- in case you want to analyze a corrupted stream.
diffs -- My diffs for HD3000 rev 1.4.
The next thing I was going to do was try to add some instrumentation
code to the
HD3000 driver. Because I *think* the card and/or driver are getting
into a bad state
at some point which is causing all of these overruns; and it doesn't
recover for the
duration of the recording.
Note: I don't even lose packet sync. It's just packets clobbering other
packets. (Which
is consistent with data being clobbered in the driver, because the
driver deals with
multiples of 188-bytes internally.)
Suggestions on how to proceed welcome! (other than buying a different
kind of card... =))
-Eric
On Jan 10, 2005, at 7:15 AM, Kyle Rose wrote:
> Daniel Thor Kristjansson <danielk at mrl.nyu.edu> writes:
>
>> This doesn't really jibe with my experience, unless you are running
>> out
>> of CPU, which shouldn't be the case for a P4 3.2Ghz machine.
>
> Well, the problem is evident in the recording itself, so it may be
> that the 1.533 athlon isn't enough. But after the recording starts,
> (which for some reason uses a huge amount of CPU), the thing never
> uses more than 3% of the CPU. As I said, I doubt it's interrupt
> latency, or the driver would report buffer overruns, but something
> somewhere is losing data only when both capture cards are being used
> at once.
>
>> Of course I
>> do compile with extra optimizations, even in debug mode. If it is not
>> the CPU, do the following:
>
>> Make sure there are errors in the stream.
>> Yes - If there are is the signal level too low?
>> Yes - nothing we can do
>> No - there may be a bug in the ringbuffer
>> No - there may be bug in ffmpeg
>
> ffmpeg isn't relevant to recording, so it isn't that. Signal strength
> is always >85%, and shouldn't fluctuate when more than one card is
> being used; nonetheless, I'll check that because there could be some
> kind of wacky interference going on.
>
> Cheers,
> Kyle
-------------- next part --------------
#include <endian.h>
//#include <machine/endian.h>
main()
{
int i,j;
unsigned int s[64];
int xsum;
int pkt=0;
int xsums[50000];
unsigned int data_d[50000];
unsigned int data_e[50000];
unsigned int data_f[50000];
unsigned int pids[50000];
int xsumcnt=0;
int xsumlast=0;
int match;
int delta;
for (i=0;i<64;i++) s[i] = 0;
for (i=0;i<50000;i++) xsums[i] = 0;
while (read(0,s,188)) {
xsum = 0;
for (i=0;i<47;i++) {
xsum += s[i];
}
// Append
xsums[xsumlast] = xsum;
data_d[xsumlast] = ntohl(s[0xd]);
data_e[xsumlast] = ntohl(s[0xe]);
data_f[xsumlast] = ntohl(s[0xf]);
pids[xsumlast] = ntohl(s[0]);
if (++xsumlast == 50000) break;
}
for (i=0;i<xsumlast;i++) {
// Search for match
match = 0;
for (j=0;j<50000;j++) {
if (i==j) continue;
if (xsums[i] == xsums[j]) {
delta=(j-i);
match=1;
}
}
// Ignore PAT duplicates
if ((pids[i] & 0x0f00) == 0) match = 0;
printf("pkt:%05d [%08x] %08x %c ", pkt++, pids[i], xsums[i], match ? '!' : ' ');
if (match) printf("del= %c%d",(delta>=0) ? '+' : ' ', delta);
if ((data_d[i] == 0x1) && ((data_e[i] >> 24) == 0xb8)) {
printf("[GOP t=%08x]", (data_e[i] << 8) | (data_f[i] >> 24));
}
printf("\n");
}
}
-------------- next part --------------
-------------- next part --------------
A non-text attachment was scrubbed...
Name: diffs
Type: application/octet-stream
Size: 1074 bytes
Desc: not available
Url : http://mythtv.org/pipermail/mythtv-dev/attachments/20050118/b5ee5288/diffs.obj
-------------- next part --------------
-------------- next part --------------
/* HDTVRecorder
Version 0.1
July 4th, 2003
GPL License (c)
By Brandon Beattie
Portions of this code taken from mpegrecorder
Modified Oct. 2003 by Doug Larrick
* decodes MPEG-TS locally (does not use libavformat) for flexibility
* output format is MPEG-TS omitting unneeded PIDs, for smaller files
and no audio stutter on stations with >1 audio stream
* Emits proper keyframe data to recordedmarkup db, enabling seeking
Oct. 22, 2003:
* delay until GOP before writing output stream at start and reset
(i.e. channel change)
* Rewrite PIDs after channel change to be the same as before, so
decoder can follow
Oct. 27, 2003 by Jason Hoos:
* if no GOP is detected within the first 30 frames of the stream, then
assume that it's safe to treat a picture start as a GOP.
Oct. 30, 2003 by Jason Hoos:
* added code to rewrite PIDs in the MPEG PAT and PMT tables. fixes
a problem with stations (particularly, WGN in Chicago) that list their
programs in reverse order in the PAT, which was confusing ffmpeg
(ffmpeg was looking for program 5, but only program 2 was left in
the stream, so it choked).
Nov. 3, 2003 by Doug Larrick:
* code to enable selecting a program number for stations with
multiple programs
* always renumber PIDs to match outgoing program number (#1:
0x10 (base), 0x11 (video), 0x14 (audio))
* change expected keyframe distance to 30 frames to see if this
lets people seek past halfway in recordings
Jan 30, 2004 by Daniel Kristjansson
* broke out tspacket to handle TS packets
* added CRC check on PAT & PMT packets
Sept 27, 2004
* added decoding of most ATSC Tables
* added multiple audio support
References / example code:
ATSC standards a.54, a.69 (www.atsc.org)
ts2pes from mpegutils from dvb (www.linuxtv.org)
bbinfo from Brent Beyeler, beyeler at home.com
*/
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <ctime>
#include "videodev_myth.h"
using namespace std;
#include "hdtvrecorder.h"
#include "RingBuffer.h"
#include "mythcontext.h"
#include "programinfo.h"
#include "channel.h"
#include "mpegtables.h"
#include "atscstreamdata.h"
extern "C" {
#include "../libavcodec/avcodec.h"
#include "../libavformat/avformat.h"
#include "../libavformat/mpegts.h"
}
#define REPORT_RING_STATS 1
#define REPORT_TS_STATS 0
#define DEFAULT_SUBCHANNEL 1
#define WHACK_A_BUG_VIDEO 0
#define WHACK_A_BUG_AUDIO 0
#if FAKE_VIDEO
static int fake_video_index = 0;
#define FAKE_VIDEO_NUM 4
static const char* FAKE_VIDEO_FILES[FAKE_VIDEO_NUM] =
{
"/video/abc.ts",
"/video/wb.ts",
"/video/abc2.ts",
"/video/nbc.ts",
};
#endif
HDTVRecorder::HDTVRecorder()
: DTVRecorder(), _atsc_stream_data(0), _resync_count(0)
{
_error = false;
_atsc_stream_data = new ATSCStreamData(DEFAULT_SUBCHANNEL);
ringbuf.size = 16 * 1024 * 1024;
ringbuf.wrapextra = 2048; // ~11 packets
ringbuf.minreadsize = 2048;
ringbuf.maxreadsize = 128 * 1024;
ringbuf.writesize = 2 * 1024 * 1024;
if ((ringbuf.buffer = new unsigned char[ringbuf.size + ringbuf.wrapextra]) == NULL) {
VERBOSE(VB_IMPORTANT, "Failed to allocate HDTVRecorder ring buffer.");
_error = true;
}
else {
// make valgrind happy, initialize buffer memory
memset(ringbuf.buffer, 0xFF, ringbuf.size + ringbuf.wrapextra);
VERBOSE(VB_RECORD, QString("HD ring buffer size %1 KB")
.arg(ringbuf.size/1024));
}
pthread_mutex_init(&ringbuf.mutex, NULL);
pthread_cond_init(&ringbuf.pausewait, NULL);
pthread_cond_init(&ringbuf.writewait, NULL);
pthread_cond_init(&ringbuf.readwait, NULL);
}
HDTVRecorder::~HDTVRecorder()
{
if (_stream_fd >= 0)
close(_stream_fd);
if (_atsc_stream_data)
delete _atsc_stream_data;
pthread_mutex_destroy(&ringbuf.mutex);
pthread_cond_destroy(&ringbuf.pausewait);
pthread_cond_destroy(&ringbuf.writewait);
pthread_cond_destroy(&ringbuf.readwait);
}
void HDTVRecorder::SetOptionsFromProfile(RecordingProfile *profile,
const QString &videodev,
const QString &audiodev,
const QString &vbidev, int ispip)
{
(void)audiodev;
(void)vbidev;
(void)profile;
(void)ispip;
SetOption("videodevice", videodev);
SetOption("tvformat", gContext->GetSetting("TVFormat"));
SetOption("vbiformat", gContext->GetSetting("VbiFormat"));
}
bool HDTVRecorder::Open()
{
if (!_atsc_stream_data || !ringbuf.buffer)
return false;
#if FAKE_VIDEO
_stream_fd = open(FAKE_VIDEO_FILES[fake_video_index], O_RDWR);
VERBOSE(VB_IMPORTANT, QString("Opened fake video source %1").arg(FAKE_VIDEO_FILES[fake_video_index]));
fake_video_index = (fake_video_index+1)%FAKE_VIDEO_NUM;
#else
_stream_fd = open(videodevice.ascii(), O_RDWR);
#endif
if (_stream_fd <= 0)
{
VERBOSE(VB_IMPORTANT, QString("Can't open video device: %1 chanfd = %2")
.arg(videodevice).arg(_stream_fd));
perror("open video:");
}
return (_stream_fd > 0);
}
void HDTVRecorder::Close()
{
if (_stream_fd < 0) return;
int ret = close(_stream_fd);
if (ret < 0) {
perror("close");
}
_stream_fd = -1;
}
void HDTVRecorder::StartRecording(void)
{
if (!Open())
{
_error = true;
return;
}
_error = false;
_request_recording = true;
_request_pause = false;
_recording = true;
_sync_count = 0;
ringbuf.rdidx = 0;
ringbuf.wridx = 0;
ringbuf.eof = false;
ringbuf.max_used = 0;
ringbuf.avg_cnt = 0;
ringbuf.avg_used = 0;
ringbuf.tlast = 0;
ringbuf.request_recording = true;
ringbuf.request_pause = false;
ringbuf.recording = true;
ringbuf.paused = false;
pthread_create(&ringbuf.thread, NULL, StartRingBuffer, reinterpret_cast<void *>(this));
// TRANSFER DATA
while (_recording)
{
int fifo_depth;
int process_len;
int remainder;
// Hand off pause request
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.request_pause = _request_pause;
ringbuf.request_recording = _request_recording;
_recording = ringbuf.recording;
// Wait for response
if (_request_pause) {
_paused = ringbuf.paused;
if (!_paused) {
pthread_cond_wait(&ringbuf.pausewait, &ringbuf.mutex);
if (ringbuf.paused) {
pauseWait.wakeAll();
} else {
VERBOSE(VB_IMPORTANT,
QString("HD ringbuf thread not pausing??"));
}
}
pthread_mutex_unlock(&ringbuf.mutex);
usleep(1000);
continue;
}
_paused = ringbuf.paused;
fifo_depth = ringbuf.wridx - ringbuf.rdidx;
// Bytes to process
process_len = fifo_depth;
// Wrap case
if (process_len < 0) {
// For stats
fifo_depth += ringbuf.size;
// Cap process_len
process_len = ringbuf.size - ringbuf.rdidx;
// The writer always copies ringbuf.wrapextra bytes *beyond* the
// end of the fifo. This gives us a minimum number of contiguous
// bytes for routines that can't deal with the wrap.
process_len += (ringbuf.wridx > ringbuf.wrapextra) ?
ringbuf.wrapextra : ringbuf.wridx;
}
// Don't process more than maxreadsize
if (process_len > ringbuf.maxreadsize) {
process_len = ringbuf.maxreadsize;
}
// Not enough data to work on?
if (fifo_depth < ringbuf.minreadsize) {
// Wait for data
if (_recording) {
pthread_cond_wait(&ringbuf.readwait, &ringbuf.mutex);
}
pthread_mutex_unlock(&ringbuf.mutex);
continue;
}
pthread_mutex_unlock(&ringbuf.mutex);
#if REPORT_RING_STATS
// Update Stats/Watermarks -- print information every minute.
if (fifo_depth > ringbuf.max_used) {
ringbuf.max_used = fifo_depth;
}
ringbuf.avg_used = ((ringbuf.avg_used * ringbuf.avg_cnt) + fifo_depth)
/ ++ringbuf.avg_cnt;
int t = time(NULL);
if (t >= ringbuf.tlast + 60) {
VERBOSE(VB_RECORD, QString("%1 ringbuf avg %2% max %3% cur %4% samples:%5")
.arg(videodevice)
.arg((static_cast<double>(ringbuf.avg_used) / ringbuf.size) * 100.0)
.arg((static_cast<double>(ringbuf.max_used) / ringbuf.size) * 100.0)
.arg((static_cast<double>(fifo_depth) / ringbuf.size) * 100.0)
.arg(ringbuf.avg_cnt));
ringbuf.tlast = t;
ringbuf.avg_cnt = 0;
ringbuf.avg_used = 0;
}
#endif
// Process data
remainder = ProcessData(&ringbuf.buffer[ringbuf.rdidx], process_len);
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.rdidx += (process_len - remainder);
if (ringbuf.rdidx >= ringbuf.size) {
ringbuf.rdidx -= ringbuf.size;
}
pthread_cond_signal(&ringbuf.writewait);
pthread_mutex_unlock(&ringbuf.mutex);
}
if (ringbuf.eof) {
VERBOSE(VB_IMPORTANT, QString("%1 - HD8 end of file found in packet").arg(videodevice));
}
// Clean up
pthread_join(ringbuf.thread, NULL);
FinishRecording();
_recording = false;
}
void *HDTVRecorder::StartRingBuffer(void *arg)
{
HDTVRecorder *hdtvrec = reinterpret_cast<HDTVRecorder *>(arg);
hdtvrec->fill_ringbuffer();
return(NULL);
}
int HDTVRecorder::fill_ringbuffer()
{
int len;
int read_bytes;
int fifo_space;
bool recording = true;
bool paused = false;
while (recording) {
// Update run/pause status
pthread_mutex_lock(&ringbuf.mutex);
paused = ringbuf.request_pause;
recording = ringbuf.request_recording;
// Echo back the pause
if (ringbuf.paused != paused) {
ringbuf.paused = paused;
pthread_cond_signal(&ringbuf.pausewait);
}
// Get fifo depth
fifo_space = ringbuf.rdidx - ringbuf.wridx;
if (fifo_space <= 0) fifo_space += ringbuf.size;
// Enough space? If not, wait.
if (fifo_space < ringbuf.writesize) {
pthread_cond_wait(&ringbuf.writewait, &ringbuf.mutex);
pthread_mutex_unlock(&ringbuf.mutex);
continue;
}
pthread_mutex_unlock(&ringbuf.mutex);
// How much can we safely read?
read_bytes = ringbuf.writesize;
if (read_bytes + ringbuf.wridx > ringbuf.size) {
read_bytes = fifo_space - ringbuf.wridx;
}
len = read(_stream_fd, &ringbuf.buffer[ringbuf.wridx], read_bytes);
if (len < 0) {
VERBOSE(VB_IMPORTANT, QString("%1 - HD: Read from device failed!").arg(videodevice));
perror("read");
_error = 1;
continue;
}
// EOF?
if (len == 0) {
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.eof = true;
pthread_mutex_unlock(&ringbuf.mutex);
break;
}
// If we loaded below ringbuf.wrapextra, refresh end of buffer
if (ringbuf.wridx < ringbuf.wrapextra) {
memmove(ringbuf.buffer + ringbuf.size, // to end
ringbuf.buffer, // from start
ringbuf.wrapextra);
}
// Write pointer increment
if (!paused) {
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.wridx += len;
if (ringbuf.wridx >= ringbuf.size) ringbuf.wridx -= ringbuf.size;
pthread_cond_signal(&ringbuf.readwait);
pthread_mutex_unlock(&ringbuf.mutex);
}
}
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.recording = false;
pthread_cond_signal(&ringbuf.readwait);
pthread_mutex_unlock(&ringbuf.mutex);
Close();
pthread_exit(reinterpret_cast<void *>(0));
return(0);
}
int HDTVRecorder::ResyncStream(unsigned char *buffer, int curr_pos, int len)
{
// Search for repeated syncs
const int syncpackets = 10;
int i;
int pos = curr_pos;
// Search for repeating sync pattern
while (pos + syncpackets * (int)TSPacket::SIZE < len) {
for (i=0;i<syncpackets;i++) {
if (buffer[pos + i * TSPacket::SIZE] != SYNC_BYTE) {
pos++;
break;
}
}
if (i == syncpackets) return(pos);
}
return(-pos); // Negative means try again
}
void HDTVRecorder::WritePAT() {
if (StreamData()->PAT()) {
ProgramAssociationTable* pat = StreamData()->PAT();
int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
pat->tsheader()->SetContinuityCounter(next);
ringBuffer->Write(pat->tsheader()->data(), TSPacket::SIZE);
}
}
#if WHACK_A_BUG_VIDEO
static int WABV_base_pid = 0x100;
#define WABV_WAIT 60
static int WABV_wait_a_while = WABV_WAIT;
bool WABV_started = false;
#endif
#if WHACK_A_BUG_AUDIO
static int WABA_base_pid = 0x200;
#define WABA_WAIT 60
static int WABA_wait_a_while = WABA_WAIT;
bool WABA_started = false;
#endif
void HDTVRecorder::WritePMT() {
if (StreamData()->PMT()) {
ProgramMapTable* pmt = StreamData()->PMT();
int next = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
pmt->tsheader()->SetContinuityCounter(next);
#if WHACK_A_BUG_VIDEO
WABV_wait_a_while--;
if (WABV_wait_a_while<=0) {
WABV_started = true;
WABV_wait_a_while = WABV_WAIT;
WABV_base_pid = (((WABV_base_pid-0x100)+1)%32)+0x100;
VERBOSE(VB_IMPORTANT, QString("Whack a Bug: new video pid %1").arg(WABV_base_pid));
// rewrite video pid
assert(StreamID::MPEG2Video == StreamData()->PMT()->StreamType(0));
const uint old_video_pid=StreamData()->PMT()->StreamPID(0);
StreamData()->PMT()->SetStreamPID(0, WABV_base_pid);
if (StreamData()->PMT()->PCRPID() == old_video_pid)
StreamData()->PMT()->SetPCRPID(WABV_base_pid);
StreamData()->PMT()->SetCRC(StreamData()->PMT()->CalcCRC());
VERBOSE(VB_IMPORTANT, StreamData()->PMT()->toString());
}
#endif
#if WHACK_A_BUG_AUDIO
WABA_wait_a_while--;
if (WABA_wait_a_while<=0) {
WABA_started = true;
WABA_wait_a_while = WABA_WAIT;
WABA_base_pid = (((WABA_base_pid-0x200)+1)%32)+0x200;
VERBOSE(VB_IMPORTANT, QString("Whack a Bug: new audio BASE pid %1").arg(WABA_base_pid));
// rewrite audio pids
for (uint i=0; i<StreamData()->PMT()->StreamCount(); i++) {
if (StreamID::MPEG2Audio == StreamData()->PMT()->StreamType(i) ||
StreamID::MPEG2Audio == StreamData()->PMT()->StreamType(i)) {
const uint old_audio_pid = StreamData()->PMT()->StreamPID(i);
const uint new_audio_pid = WABA_base_pid + old_audio_pid;
StreamData()->PMT()->SetStreamPID(i, new_audio_pid);
if (StreamData()->PMT()->PCRPID() == old_audio_pid)
StreamData()->PMT()->SetPCRPID(new_audio_pid);
StreamData()->PMT()->SetCRC(StreamData()->PMT()->CalcCRC());
VERBOSE(VB_IMPORTANT, StreamData()->PMT()->toString());
}
}
}
#endif
ringBuffer->Write(pmt->tsheader()->data(), TSPacket::SIZE);
}
}
void HDTVRecorder::HandleVideo(const TSPacket* tspacket)
{
FindKeyframes(tspacket);
// decoder needs video, of course (just this PID)
// delay until first GOP to avoid decoder crash on res change
if (_wait_for_keyframe && !_keyframe_seen)
return;
#if WHACK_A_BUG_VIDEO
if (WABV_started)
((TSPacket*)(tspacket))->SetPID(WABV_base_pid);
#endif
ringBuffer->Write(tspacket->data(), TSPacket::SIZE);
}
void HDTVRecorder::HandleAudio(const TSPacket* tspacket)
{
// decoder needs audio, of course (just this PID)
if (_wait_for_keyframe && !_keyframe_seen)
return;
#if WHACK_A_BUG_AUDIO
if (WABA_started)
((TSPacket*)(tspacket))->SetPID(WABA_base_pid+tspacket->PID());
#endif
ringBuffer->Write(tspacket->data(), TSPacket::SIZE);
}
bool HDTVRecorder::ProcessTSPacket(const TSPacket& tspacket)
{
bool ok = !tspacket.TransportError();
if (ok && !tspacket.ScramplingControl())
{
if (tspacket.HasAdaptationField())
StreamData()->HandleAdaptationFieldControl(&tspacket);
if (tspacket.HasPayload())
{
const unsigned int lpid = tspacket.PID();
// Pass or reject frames based on PID, and parse info from them
if (lpid == StreamData()->VideoPID())
HandleVideo(&tspacket);
else if (StreamData()->IsAudioPID(lpid))
HandleAudio(&tspacket);
else if (StreamData()->IsListeningPID(lpid))
StreamData()->HandleTables(&tspacket, this);
else if (StreamData()->IsWritingPID(lpid))
ringBuffer->Write(tspacket.data(), TSPacket::SIZE);
else if (StreamData()->VersionMGT()>=0)
_ts_stats.IncrPIDCount(lpid);
}
}
return ok;
}
int HDTVRecorder::ProcessData(unsigned char *buffer, int len)
{
int pos = 0;
const int unsyncpackets = 5;
while (pos + 187 < len) // while we have a whole packet left
{
// If not synced, try to reacquire.
if (_sync_count == 0) {
pos = ResyncStream(buffer, pos, len);
if (pos < 0) {
// No sync found -- advance pointer.
return len - (-pos);
}
_resync_count++;
VERBOSE(VB_RECORD, QString("%1 - Resync").arg(videodevice));
}
// Too many errors in a row, drop sync.
if (buffer[pos] != SYNC_BYTE) {
if (_sync_count > 0) {
_sync_count--;
if (_sync_count == 0) {
VERBOSE(VB_RECORD, QString("%1 - Lost Sync").arg(videodevice));
}
}
continue;
}
// Synced
_sync_count = unsyncpackets;
const TSPacket *pkt = reinterpret_cast<const TSPacket*>(&buffer[pos]);
if (ProcessTSPacket(*pkt)) {
_ts_stats.IncrTSPacketCount();
#if REPORT_TS_STATS
if (0 == _ts_stats.TSPacketCount()%1000000)
VERBOSE(VB_RECORD, _ts_stats.toString());
#endif
}
pos += TSPacket::SIZE; // Advance to next TS packet
// else // Let it resync in case of dropped bytes
// buffer[pos] = SYNC_BYTE+1;
}
return len - pos;
}
void HDTVRecorder::Reset(void)
{
DTVRecorder::Reset();
write(0,"HDTVRecorder::Reset()\r\n",23);
// Stats
_sync_count = 0;
_resync_count = 0;
_ts_stats.Reset();
if (curRecording && db_lock && db_conn)
{
pthread_mutex_lock(db_lock);
MythContext::KickDatabase(db_conn);
curRecording->ClearPositionMap(MARK_GOP_BYFRAME, db_conn);
pthread_mutex_unlock(db_lock);
}
// Flush read/write pointers
pthread_mutex_lock(&ringbuf.mutex);
ringbuf.rdidx = 0;
ringbuf.wridx = 0;
ringbuf.eof = false;
ringbuf.max_used = 0;
ringbuf.avg_cnt = 0;
ringbuf.avg_used = 0;
ringbuf.tlast = 0;
pthread_cond_signal(&ringbuf.readwait);
pthread_cond_signal(&ringbuf.writewait);
pthread_mutex_unlock(&ringbuf.mutex);
StreamData()->Reset();
}
void HDTVRecorder::ChannelNameChanged(const QString& new_chan)
{
if (!_atsc_stream_data || !ringbuf.buffer)
return;
_wait_for_keyframe = _wait_for_keyframe_option;
#if FAKE_VIDEO
RecorderBase::ChannelNameChanged("51");
#else
DTVRecorder::ChannelNameChanged(new_chan);
#endif
// look up freqid
pthread_mutex_lock(db_lock);
QString thequery = QString("SELECT freqid "
"FROM channel WHERE channum = \"%1\";")
.arg(curChannelName);
QSqlQuery query = db_conn->exec(thequery);
if (!query.isActive())
MythContext::DBError("fetchtuningparamschanid", query);
if (query.numRowsAffected() <= 0)
{
pthread_mutex_unlock(db_lock);
return;
}
query.next();
QString freqid(query.value(0).toString());
VERBOSE(VB_RECORD, QString("Setting frequency for startRecording freqid: %1").arg(freqid));
pthread_mutex_unlock(db_lock);
int desired_subchannel = DEFAULT_SUBCHANNEL;
int pos = freqid.find('-');
if (pos != -1)
desired_subchannel = atoi(freqid.mid(pos+1).ascii());
else
VERBOSE(VB_IMPORTANT,
QString("Error: Desired subchannel not specified in freqid \"%1\", Using %2.").arg(freqid).arg(desired_subchannel));
StreamData()->Reset(desired_subchannel);
}
-------------- next part --------------
-------------- next part --------------
// -*- Mode: c++ -*-
/**
* HDTVRecorder
* Copyright (c) 2003-2004 by Brandon Beattie, Doug Larrick,
* Jason Hoos, and Daniel Thor Kristjansson
* Device ringbuffer added by John Poet
* Distributed as part of MythTV under GPL v2 and later.
*/
#ifndef HDTVRECORDER_H_
#define HDTVRECORDER_H_
#include "dtvrecorder.h"
#include "tsstats.h"
struct AVFormatContext;
struct AVPacket;
class ATSCStreamData;
class HDTVRecorder : public DTVRecorder
{
friend class ATSCStreamData;
friend class TSPacketProcessor;
public:
HDTVRecorder();
~HDTVRecorder();
void SetOptionsFromProfile(RecordingProfile *profile,
const QString &videodev,
const QString &audiodev,
const QString &vbidev, int ispip);
void StartRecording(void);
void Reset(void);
bool Open(void);
void Close(void);
void ChannelNameChanged(const QString& new_freqid);
private:
int ProcessData(unsigned char *buffer, int len);
bool ProcessTSPacket(const TSPacket& tspacket);
void HandleVideo(const TSPacket* tspacket);
void HandleAudio(const TSPacket* tspacket);
int ResyncStream(unsigned char *buffer, int curr_pos, int len);
void WritePAT();
void WritePMT();
ATSCStreamData* StreamData() { return _atsc_stream_data; }
const ATSCStreamData* StreamData() const { return _atsc_stream_data; }
ATSCStreamData* _atsc_stream_data;
// statistics
TSStats _ts_stats;
long long _resync_count;
int _sync_count;
// Data for managing the device ringbuffer
struct {
unsigned char *buffer;
int size;
int wrapextra;
int wridx;
int rdidx;
int minreadsize;
int maxreadsize;
int writesize;
bool eof;
pthread_t thread;
pthread_mutex_t mutex;
pthread_cond_t pausewait;
pthread_cond_t readwait;
pthread_cond_t writewait;
int request_recording;
int request_pause;
int recording;
int paused;
int max_used;
int avg_used;
int avg_cnt;
int tlast;
} ringbuf;
static void *StartRingBuffer(void *arg);
int fill_ringbuffer();
};
#endif
-------------- next part --------------
More information about the mythtv-dev
mailing list