The frame width, height, scan type and apect ratio of a recording are now stored in the 'info' file under the 'F' tag

This commit is contained in:
Klaus Schmidinger 2023-12-28 21:23:19 +01:00
parent 1770a18598
commit 6458f8b581
8 changed files with 160 additions and 25 deletions

View File

@ -2468,6 +2468,8 @@ Christoph Haubrich <christoph1.haubrich@arcor.de>
for fixing handling zero bytes in cH264Parser
for implementing parsing frame rate and image size for MPEG2, H.264 and H.265
for reporting a bug in generating the index file in the cutter
for adding the frame width, height, scan type and aspect ratio of a recording to the 'F'
tag of the 'info' file
Pekka Mauno <pekka.mauno@iki.fi>
for fixing cSchedule::GetFollowingEvent() in case there is currently no present

View File

@ -9852,7 +9852,9 @@ Video Disk Recorder Revision History
mode receiver device (thanks to Markus Ehrnsperger).
- Revised support for kernel based LIRC driver (thanks to Marko Mäkelä).
2023-02-21:
2023-12-28:
- Fixed broken video data streams on systems without output device when switching live
channel to a different transponder while recording (reported by Markus Ehrnsperger).
- The frame width, height, scan type and apect ratio of a recording are now stored in
the 'info' file under the 'F' tag (thanks to Christoph Haubrich).

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recorder.c 5.4 2021/06/19 14:21:16 kls Exp $
* $Id: recorder.c 5.5 2023/12/28 21:22:29 kls Exp $
*/
#include "recorder.h"
@ -326,8 +326,12 @@ void cRecorder::Action(void)
break;
if (frameDetector->Synced()) {
if (!InfoWritten) {
if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(recordingInfo->FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(recordingInfo->FramesPerSecond(), frameDetector->FramesPerSecond())) {
if ((frameDetector->FramesPerSecond() > 0 && DoubleEqual(recordingInfo->FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(recordingInfo->FramesPerSecond(), frameDetector->FramesPerSecond())) ||
frameDetector->FrameWidth() != recordingInfo->FrameWidth() ||
frameDetector->FrameHeight() != recordingInfo->FrameHeight() ||
frameDetector->AspectRatio() != recordingInfo->AspectRatio()) {
recordingInfo->SetFramesPerSecond(frameDetector->FramesPerSecond());
recordingInfo->SetFrameParams(frameDetector->FrameWidth(), frameDetector->FrameHeight(), frameDetector->ScanType(), frameDetector->AspectRatio());
recordingInfo->Write();
LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(recordingName);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.c 5.22 2023/02/15 14:59:25 kls Exp $
* $Id: recording.c 5.23 2023/12/28 21:22:42 kls Exp $
*/
#include "recording.h"
@ -24,7 +24,6 @@
#include "i18n.h"
#include "interface.h"
#include "menu.h"
#include "remux.h"
#include "ringbuffer.h"
#include "skins.h"
#include "svdrp.h"
@ -363,6 +362,10 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
event = ownEvent ? ownEvent : Event;
aux = NULL;
framesPerSecond = DEFAULTFRAMESPERSECOND;
frameWidth = 0;
frameHeight = 0;
scanType = stUnknown;
aspectRatio = arUnknown;
priority = MAXPRIORITY;
lifetime = MAXLIFETIME;
fileName = NULL;
@ -424,6 +427,10 @@ cRecordingInfo::cRecordingInfo(const char *FileName)
aux = NULL;
errors = -1;
framesPerSecond = DEFAULTFRAMESPERSECOND;
frameWidth = 0;
frameHeight = 0;
scanType = stUnknown;
aspectRatio = arUnknown;
priority = MAXPRIORITY;
lifetime = MAXLIFETIME;
fileName = strdup(cString::sprintf("%s%s", FileName, INFOFILESUFFIX));
@ -458,6 +465,14 @@ void cRecordingInfo::SetFramesPerSecond(double FramesPerSecond)
framesPerSecond = FramesPerSecond;
}
void cRecordingInfo::SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio)
{
frameWidth = FrameWidth;
frameHeight = FrameHeight;
scanType = ScanType;
aspectRatio = AspectRatio;
}
void cRecordingInfo::SetFileName(const char *FileName)
{
bool IsPesRecording = fileName && endswith(fileName, ".vdr");
@ -507,7 +522,35 @@ bool cRecordingInfo::Read(FILE *f)
}
}
break;
case 'F': framesPerSecond = atod(t);
case 'F': {
char *fpsBuf = NULL;
char scanTypeCode;
char *arBuf = NULL;
int n = sscanf(t, "%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &frameWidth, &frameHeight, &scanTypeCode, &arBuf);
if (n >= 1) {
framesPerSecond = atod(fpsBuf);
if (n >= 4) {
scanType = stUnknown;
for (int st = stUnknown + 1; st < stMax; st++) {
if (ScanTypeChars[st] == scanTypeCode) {
scanType = eScanType(st);
break;
}
}
aspectRatio = arUnknown;
if (n == 5) {
for (int ar = arUnknown + 1; ar < arMax; ar++) {
if (strcmp(arBuf, AspectRatioTexts[ar]) == 0) {
aspectRatio = eAspectRatio(ar);
break;
}
}
}
}
}
free(fpsBuf);
free(arBuf);
}
break;
case 'L': lifetime = atoi(t);
break;
@ -536,6 +579,9 @@ bool cRecordingInfo::Write(FILE *f, const char *Prefix) const
if (channelID.Valid())
fprintf(f, "%sC %s%s%s\n", Prefix, *channelID.ToString(), channelName ? " " : "", channelName ? channelName : "");
event->Dump(f, Prefix, true);
if (frameWidth > 0 && frameHeight > 0)
fprintf(f, "%sF %s %s %s %c %s\n", Prefix, *dtoa(framesPerSecond, "%.10g"), *itoa(frameWidth), *itoa(frameHeight), ScanTypeChars[scanType], AspectRatioTexts[aspectRatio]);
else
fprintf(f, "%sF %s\n", Prefix, *dtoa(framesPerSecond, "%.10g"));
fprintf(f, "%sP %d\n", Prefix, priority);
fprintf(f, "%sL %d\n", Prefix, lifetime);
@ -2520,8 +2566,12 @@ void cIndexFileGenerator::Action(void)
if (IndexFileWritten) {
cRecordingInfo RecordingInfo(recordingName);
if (RecordingInfo.Read()) {
if (FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) {
if ((FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) ||
FrameDetector.FrameWidth() != RecordingInfo.FrameWidth() ||
FrameDetector.FrameHeight() != RecordingInfo.FrameHeight() ||
FrameDetector.AspectRatio() != RecordingInfo.AspectRatio()) {
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
RecordingInfo.SetFrameParams(FrameDetector.FrameWidth(), FrameDetector.FrameHeight(), FrameDetector.ScanType(), FrameDetector.AspectRatio());
RecordingInfo.Write();
LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(recordingName);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.h 5.5 2021/05/23 15:03:17 kls Exp $
* $Id: recording.h 5.6 2023/12/27 09:21:29 kls Exp $
*/
#ifndef __RECORDING_H
@ -17,6 +17,7 @@
#include "thread.h"
#include "timers.h"
#include "tools.h"
#include "remux.h"
#define FOLDERDELIMCHAR '~'
@ -69,6 +70,10 @@ private:
cEvent *ownEvent;
char *aux;
double framesPerSecond;
uint16_t frameWidth;
uint16_t frameHeight;
eScanType scanType;
eAspectRatio aspectRatio;
int priority;
int lifetime;
char *fileName;
@ -87,7 +92,14 @@ public:
const cComponents *Components(void) const { return event->Components(); }
const char *Aux(void) const { return aux; }
double FramesPerSecond(void) const { return framesPerSecond; }
uint16_t FrameWidth(void) const { return frameWidth; }
uint16_t FrameHeight(void) const { return frameHeight; }
eScanType ScanType(void) const { return scanType; }
char ScanTypeChar(void) const { return ScanTypeChars[scanType]; }
eAspectRatio AspectRatio(void) const { return aspectRatio; }
const char *AspectRatioText(void) const { return AspectRatioTexts[aspectRatio]; }
void SetFramesPerSecond(double FramesPerSecond);
void SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio);
void SetFileName(const char *FileName);
int Errors(void) const { return errors; } // returns -1 if undefined
void SetErrors(int Errors);

60
remux.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: remux.c 5.5 2022/11/30 14:38:46 kls Exp $
* $Id: remux.c 5.6 2023/12/28 21:22:47 kls Exp $
*/
#include "remux.h"
@ -1177,7 +1177,8 @@ protected:
uint16_t frameWidth;
uint16_t frameHeight;
double framesPerSecond;
bool progressive;
eScanType scanType;
eAspectRatio aspectRatio;
public:
cFrameParser(void);
virtual ~cFrameParser() {};
@ -1195,7 +1196,8 @@ public:
uint16_t FrameWidth(void) { return frameWidth; }
uint16_t FrameHeight(void) { return frameHeight; }
double FramesPerSecond(void) { return framesPerSecond; }
bool Progressive(void) { return progressive; }
eScanType ScanType(void) { return scanType; }
eAspectRatio AspectRatio(void) { return aspectRatio; }
};
cFrameParser::cFrameParser(void)
@ -1207,7 +1209,8 @@ cFrameParser::cFrameParser(void)
frameWidth = 0;
frameHeight = 0;
framesPerSecond = 0.0;
progressive = false;
scanType = stUnknown;
aspectRatio = arUnknown;
}
// --- cAudioParser ----------------------------------------------------------
@ -1322,17 +1325,24 @@ int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
uchar b = tsPayload.GetByte(); // ignoring two MSB of width and height in sequence extension
frameWidth |= b >> 4; // as 12 Bit = max 4095 should be sufficient for all available MPEG2 streams
frameHeight = (b & 0x0F) << 8 | tsPayload.GetByte();
b = tsPayload.GetByte();
b = tsPayload.GetByte(); // hi: aspect ratio info, lo: frame rate code
switch (b >> 4) {
case 1: aspectRatio = ar_1_1; break;
case 2: aspectRatio = ar_4_3; break;
case 3: aspectRatio = ar_16_9; break;
case 4: aspectRatio = ar_2_21_1; break;
default: aspectRatio = arUnknown;
}
uchar frame_rate_value = b & 0x0F;
if (frame_rate_value > 0 && frame_rate_value <= 8)
framesPerSecond = frame_rate_table[frame_rate_value];
}
else if (!seenScanType && scanner == 0x000001B5) { // Extension start code
if ((tsPayload.GetByte() & 0xF0) == 0x10) { // Sequence Extension
progressive = (tsPayload.GetByte() & 0x40) != 0;
scanType = (tsPayload.GetByte() & 0x40) ? stProgressive : stInterlaced;
seenScanType = true;
if (debug) {
cString s = cString::sprintf("MPEG2: %d x %d%c %.2f fps", frameWidth, frameHeight, progressive ? 'p' : 'i', framesPerSecond);
cString s = cString::sprintf("MPEG2: %d x %d%c %.2f fps %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, AspectRatioTexts[aspectRatio]);
dsyslog("%s", *s);
dbgframes("\n%s", *s);
}
@ -1553,7 +1563,7 @@ void cH264Parser::ParseSequenceParameterSet(void)
uint16_t frame_Height = 16 * (1 + GetGolombUe()); // pic_height_in_map_units_minus1
frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
if (frameWidth == 0) {
progressive = frame_mbs_only_flag;
scanType = frame_mbs_only_flag ? stProgressive : stInterlaced;
if (!frame_mbs_only_flag) {
GetBit(); // mb_adaptive_frame_field_flag
frame_Height *= 2;
@ -1584,8 +1594,11 @@ void cH264Parser::ParseSequenceParameterSet(void)
if (GetBit()) { // vui_parameters_present_flag
if (GetBit()) { // aspect_ratio_info_present
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
if (aspect_ratio_idc == 255)
GetBits(32);
if (aspect_ratio_idc == 255) // EXTENDED_SAR
GetBits(32); // sar_width, sar_height
else if (frameHeight >= 720 && (aspect_ratio_idc == 1 || aspect_ratio_idc == 14 || aspect_ratio_idc == 15 || aspect_ratio_idc == 16))
aspectRatio = ar_16_9;
// implement decoding of other aspect_ratio_idc values when they are required
}
if (GetBit()) // overscan_info_present_flag
GetBit(); // overscan_approriate_flag
@ -1606,7 +1619,7 @@ void cH264Parser::ParseSequenceParameterSet(void)
}
}
if (debug) {
cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
dsyslog("%s", *s);
dbgframes("\n%s", *s);
}
@ -1737,7 +1750,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
GetByte();
GetByte();
bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
progressive = general_progressive_source_flag;
scanType = general_progressive_source_flag ? stProgressive : stInterlaced;
GetBit(); // general_interlaced_source_flag
GetBits(6);
GetByte();
@ -1880,6 +1893,9 @@ void cH265Parser::ParseSequenceParameterSet(void)
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
if (aspect_ratio_idc == 255) // EXTENDED_SAR
GetBits(32); // sar_width, sar_height
else if (aspect_ratio_idc == 1 || aspect_ratio_idc == 14)
aspectRatio = ar_16_9;
// implement decoding of other aspect_ratio_idc values when they are required
}
if (GetBit()) // overscan_info_present_flag
GetBit(); // overscan_appropriate_flag
@ -1907,7 +1923,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
}
}
if (debug) {
cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
dsyslog("%s", *s);
dbgframes("\n%s", *s);
}
@ -1915,6 +1931,16 @@ void cH265Parser::ParseSequenceParameterSet(void)
// --- cFrameDetector --------------------------------------------------------
const char *ScanTypeChars = "-pi"; // index is eScanType
const char *AspectRatioTexts[] = { // index is eAspectRatio
"-",
"1:1",
"4:3",
"16:9",
"2.21:1",
NULL
};
cFrameDetector::cFrameDetector(int Pid, int Type)
{
parser = NULL;
@ -1924,6 +1950,10 @@ cFrameDetector::cFrameDetector(int Pid, int Type)
numPtsValues = 0;
numIFrames = 0;
framesPerSecond = 0;
frameWidth = 0;
frameHeight = 0;
scanType = stUnknown;
aspectRatio = arUnknown;
framesInPayloadUnit = framesPerPayloadUnit = 0;
scanning = false;
}
@ -1991,6 +2021,10 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
else {
if (parser->FramesPerSecond() > 0.0) {
framesPerSecond = parser->FramesPerSecond();
frameWidth = parser->FrameWidth();
frameHeight = parser->FrameHeight();
scanType = parser->ScanType();
aspectRatio = parser->AspectRatio();
synced = true;
parser->SetDebug(false);
}

33
remux.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: remux.h 5.2 2021/12/25 14:11:39 kls Exp $
* $Id: remux.h 5.3 2023/12/27 09:30:42 kls Exp $
*/
#ifndef __REMUX_H
@ -504,6 +504,25 @@ void PesDump(const char *Name, const u_char *Data, int Length);
class cFrameParser;
enum eScanType {
stUnknown = 0,
stProgressive = 1,
stInterlaced = 2,
stMax
};
enum eAspectRatio {
arUnknown = 0,
ar_1_1 = 1,
ar_4_3 = 2,
ar_16_9 = 3,
ar_2_21_1 = 4,
arMax
};
extern const char *ScanTypeChars;
extern const char *AspectRatioTexts[];
class cFrameDetector {
private:
enum { MaxPtsValues = 150 };
@ -517,6 +536,10 @@ private:
int numIFrames;
bool isVideo;
double framesPerSecond;
uint16_t frameWidth;
uint16_t frameHeight;
eScanType scanType;
eAspectRatio aspectRatio;
int framesInPayloadUnit;
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
// while others put an entire GOP into one payload unit (> 1).
@ -547,6 +570,14 @@ public:
double FramesPerSecond(void) { return framesPerSecond; }
///< Returns the number of frames per second, or 0 if this information is not
///< available.
uint16_t FrameWidth(void) { return frameWidth; }
///< Returns the frame width, or 0 if this information is not available.
uint16_t FrameHeight(void) { return frameHeight; }
///< Returns the frame height, or 0 if this information is not available.
eScanType ScanType(void) { return scanType; }
///< Returns the scan type, or stUnknown if this information is not available.
eAspectRatio AspectRatio(void) { return aspectRatio; }
///< Returns the aspect ratio, or arUnknown if this information is not available.
};
#endif // __REMUX_H

4
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 5.8 2022/12/26 13:24:09 kls Exp $
.\" $Id: vdr.5 5.9 2023/12/25 20:53:04 kls Exp $
.\"
.TH vdr 5 "27 Dec 2021" "2.6" "Video Disk Recorder Files"
.SH NAME
@ -814,7 +814,7 @@ characters are defined:
.TS
tab (|);
l l.
\fBF\fR|<frame rate>
\fBF\fR|<frame rate> <frame width> <frame height> <scan type> <aspect ratio>
\fBL\fR|<lifetime>
\fBP\fR|<priority>
\fBO\fR|<errors>