452 lines
14 KiB
C++
452 lines
14 KiB
C++
#include "vc1Parser.h"
|
|
|
|
#include <fs/systemlog.h>
|
|
#include <memory.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include "nalUnits.h"
|
|
#include "vodCoreException.h"
|
|
#include "vod_common.h"
|
|
// ----------------------------------------------------------------------
|
|
|
|
using namespace std;
|
|
|
|
const char* pict_type_str[4] = {"I_TYPE", "P_TYPE", "B_TYPE", "BI_TYPE"};
|
|
|
|
static inline int get_unary(BitStreamReader& bitReader, int stop, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < len && bitReader.getBit() != stop; i++)
|
|
;
|
|
return i;
|
|
}
|
|
|
|
static inline int decode012(BitStreamReader& bitReader)
|
|
{
|
|
int n = bitReader.getBit();
|
|
if (n == 0)
|
|
return 0;
|
|
else
|
|
return bitReader.getBit() + 1;
|
|
}
|
|
|
|
// ---------------------------- VC1Unit ------------------------------------------
|
|
|
|
void VC1Unit::updateBits(int bitOffset, int bitLen, int value)
|
|
{
|
|
uint8_t* ptr = (uint8_t*)bitReader.getBuffer() + bitOffset / 8;
|
|
BitStreamWriter bitWriter{};
|
|
int byteOffset = bitOffset % 8;
|
|
bitWriter.setBuffer(ptr, ptr + (bitLen / 8 + 5));
|
|
|
|
uint8_t* ptr_end = (uint8_t*)bitReader.getBuffer() + (bitOffset + bitLen) / 8;
|
|
int endBitsPostfix = 8 - ((bitOffset + bitLen) % 8);
|
|
|
|
if (byteOffset > 0)
|
|
{
|
|
int prefix = *ptr >> (8 - byteOffset);
|
|
bitWriter.putBits(byteOffset, prefix);
|
|
}
|
|
bitWriter.putBits(bitLen, value);
|
|
|
|
if (endBitsPostfix < 8)
|
|
{
|
|
int postfix = *ptr_end & (1 << endBitsPostfix) - 1;
|
|
bitWriter.putBits(endBitsPostfix, postfix);
|
|
}
|
|
bitWriter.flushBits();
|
|
}
|
|
|
|
// ---------------------------- VC1SequenceHeader ------------------------------------------
|
|
|
|
string VC1SequenceHeader::getStreamDescr()
|
|
{
|
|
std::ostringstream rez;
|
|
rez << "Profile: ";
|
|
switch (profile)
|
|
{
|
|
case Profile::SIMPLE:
|
|
rez << "Simple";
|
|
break;
|
|
case Profile::MAIN:
|
|
rez << "Main";
|
|
break;
|
|
case Profile::COMPLEX:
|
|
rez << "Complex";
|
|
break;
|
|
case Profile::ADVANCED:
|
|
rez << "Advanced@" << level;
|
|
break;
|
|
default:
|
|
rez << "Unknown";
|
|
break;
|
|
};
|
|
rez << " Resolution: " << coded_width << ':' << coded_height;
|
|
rez << (interlace ? 'i' : 'p') << " ";
|
|
rez << "Frame rate: ";
|
|
double fps = getFPS();
|
|
if (fps != 0)
|
|
rez << fps;
|
|
else
|
|
rez << "not found";
|
|
return rez.str();
|
|
}
|
|
|
|
double VC1SequenceHeader::getFPS()
|
|
{
|
|
if (time_base_num == 0 || time_base_den == 0)
|
|
return 0;
|
|
double fps = time_base_den / (double)time_base_num;
|
|
// if (fps > 25.0 && pulldown)
|
|
// fps /= 1.25;
|
|
return fps;
|
|
}
|
|
|
|
void VC1SequenceHeader::setFPS(double value)
|
|
{
|
|
// if (value < 25.0 && pulldown)
|
|
// value *= 1.25;
|
|
|
|
if (m_fpsFieldBitVal > 0)
|
|
{
|
|
int nr, dr;
|
|
int time_scale = (int)(value + 0.5) * 1000;
|
|
int num_units_in_tick = (int)(time_scale / value + 0.5);
|
|
if ((time_scale == 24000 || time_scale == 25000 || time_scale == 30000 || time_scale == 50000 ||
|
|
time_scale == 60000) &&
|
|
(num_units_in_tick == 1000 || num_units_in_tick == 1001))
|
|
{
|
|
time_base_den = time_scale;
|
|
time_base_num = num_units_in_tick;
|
|
}
|
|
else
|
|
THROW(ERR_VC1_ERR_FPS,
|
|
"Can't overwrite stream fps. Non standard fps values not supported for VC-1 streams");
|
|
|
|
switch (time_scale)
|
|
{
|
|
case 24000:
|
|
nr = 1;
|
|
break;
|
|
case 25000:
|
|
nr = 2;
|
|
break;
|
|
case 30000:
|
|
nr = 3;
|
|
break;
|
|
case 50000:
|
|
nr = 4;
|
|
break;
|
|
case 60000:
|
|
nr = 5;
|
|
break;
|
|
default:
|
|
THROW(ERR_VC1_ERR_FPS,
|
|
"Can't overwrite stream fps. Non standard fps values not supported for VC-1 streams");
|
|
}
|
|
dr = (num_units_in_tick == 1000) ? 1 : 2;
|
|
|
|
updateBits(m_fpsFieldBitVal, 8, nr);
|
|
updateBits(m_fpsFieldBitVal + 8, 4, dr);
|
|
}
|
|
}
|
|
|
|
int VC1SequenceHeader::decode_sequence_header()
|
|
{
|
|
try
|
|
{
|
|
bitReader.setBuffer(m_nalBuffer, m_nalBuffer + m_nalBufferLen); // skip 00 00 01 xx marker
|
|
profile = (Profile)bitReader.getBits(2);
|
|
if (profile == Profile::COMPLEX)
|
|
LTRACE(LT_WARN, 0, "WMV3 Complex Profile is not fully supported");
|
|
|
|
else if (profile == Profile::ADVANCED)
|
|
return decode_sequence_header_adv();
|
|
else
|
|
{
|
|
int res_sm = bitReader.getBits(2); // reserved
|
|
if (res_sm)
|
|
{
|
|
LTRACE(LT_ERROR, 0, "Reserved RES_SM=" << res_sm << " is forbidden");
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
}
|
|
|
|
bitReader.skipBits(8); // frmrtq_postproc, bitrtq_postproc
|
|
if (bitReader.getBit() && profile == Profile::SIMPLE) // loop_filter
|
|
LTRACE(LT_WARN, 0, "LOOPFILTER shell not be enabled in simple profile");
|
|
if (bitReader.getBit()) // reserved res_x8
|
|
LTRACE(LT_WARN, 0, "1 for reserved RES_X8 is forbidden");
|
|
bitReader.skipBit(); // multires
|
|
int res_fasttx = bitReader.getBit(); // reserved
|
|
if (!res_fasttx)
|
|
LTRACE(LT_WARN, 0, "0 for reserved RES_FASTTX is forbidden");
|
|
if (profile == Profile::SIMPLE && !bitReader.getBit()) // fastuvmc
|
|
{
|
|
LTRACE(LT_ERROR, 0, "FASTUVMC unavailable in Simple Profile");
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
if (profile == Profile::SIMPLE && bitReader.getBit()) // extended_mv
|
|
{
|
|
LTRACE(LT_ERROR, 0, "Extended MVs unavailable in Simple Profile");
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
bitReader.skipBits(3); // dquant, vstransform
|
|
|
|
if (bitReader.getBit()) // res_transtab
|
|
{
|
|
LTRACE(LT_ERROR, 0, "1 for reserved RES_TRANSTAB is forbidden\n");
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
bitReader.skipBits(2); // overlap, resync_marker
|
|
|
|
rangered = bitReader.getBit();
|
|
if (rangered && profile == Profile::SIMPLE)
|
|
LTRACE(LT_WARN, 0, "RANGERED should be set to 0 in simple profile");
|
|
max_b_frames = bitReader.getBits(3);
|
|
bitReader.skipBits(2); // quantizer_mode
|
|
finterpflag = bitReader.getBit();
|
|
|
|
if (!bitReader.getBit()) // res_rtm_flag
|
|
LTRACE(LT_WARN, 0, "Old WMV3 version detected.");
|
|
// TODO: figure out what they mean (always 0x402F)
|
|
if (!res_fasttx)
|
|
bitReader.skipBits(16);
|
|
return 0;
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
int VC1SequenceHeader::decode_sequence_header_adv()
|
|
{
|
|
level = bitReader.getBits(3);
|
|
if (level >= 5)
|
|
LTRACE(LT_WARN, 0, "Reserved LEVEL " << level);
|
|
bitReader.skipBits(11); // chromaformat, frmrtq_postproc, bitrtq_postproc, postprocflag
|
|
coded_width = (bitReader.getBits(12) + 1) << 1;
|
|
coded_height = (bitReader.getBits(12) + 1) << 1;
|
|
pulldown = bitReader.getBit();
|
|
interlace = bitReader.getBit();
|
|
tfcntrflag = bitReader.getBit();
|
|
finterpflag = bitReader.getBit();
|
|
bitReader.skipBit();
|
|
psf = bitReader.getBit();
|
|
/*
|
|
if(psf) { //PsF, 6.1.13
|
|
LTRACE(LT_ERROR, 0, "Progressive Segmented Frame mode: not supported (yet)");
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
*/
|
|
max_b_frames = 7;
|
|
if (bitReader.getBit())
|
|
{ // Display Info - decoding is not affected by it
|
|
int w, h, ar = 0;
|
|
display_width = w = bitReader.getBits(14) + 1;
|
|
display_height = h = bitReader.getBits(14) + 1;
|
|
if (bitReader.getBit())
|
|
ar = bitReader.getBits(4);
|
|
if (ar && ar < 14)
|
|
{
|
|
sample_aspect_ratio = ff_vc1_pixel_aspect[ar];
|
|
}
|
|
else if (ar == 15)
|
|
{
|
|
w = bitReader.getBits(8);
|
|
h = bitReader.getBits(8);
|
|
sample_aspect_ratio = AVRational(w, h);
|
|
}
|
|
|
|
if (bitReader.getBit())
|
|
{ // framerate stuff
|
|
if (bitReader.getBit())
|
|
{
|
|
time_base_num = 32;
|
|
time_base_den = bitReader.getBits(16) + 1;
|
|
}
|
|
else
|
|
{
|
|
int nr, dr;
|
|
m_fpsFieldBitVal = bitReader.getBitsCount();
|
|
nr = bitReader.getBits(8);
|
|
dr = bitReader.getBits(4);
|
|
if (nr > 0 && nr < 8 && dr > 0 && dr < 3)
|
|
{
|
|
time_base_num = ff_vc1_fps_dr[dr - 1];
|
|
time_base_den = ff_vc1_fps_nr[nr - 1] * 1000;
|
|
}
|
|
else
|
|
LTRACE(LT_WARN, 0, "Invalid fps value");
|
|
}
|
|
}
|
|
|
|
if (bitReader.getBit())
|
|
bitReader.skipBits(24); // color_prim, transfer_char, matrix_coef
|
|
}
|
|
|
|
hrd_param_flag = bitReader.getBit();
|
|
if (hrd_param_flag)
|
|
{
|
|
hrd_num_leaky_buckets = bitReader.getBits(5);
|
|
bitReader.skipBits(8); // bitrate exponent, buffer size exponent
|
|
for (int i = 0; i < hrd_num_leaky_buckets; i++) bitReader.skipBits(32); // hrd_rate[n], hrd_buffer[n]
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int VC1SequenceHeader::decode_entry_point()
|
|
{
|
|
try
|
|
{
|
|
bitReader.setBuffer(m_nalBuffer, m_nalBuffer + m_nalBufferLen); // skip 00 00 01 xx marker
|
|
bitReader.skipBit(); // blink = broken link
|
|
bitReader.skipBit(); // clentry = closed entry
|
|
bitReader.skipBit(); // panscanflag
|
|
bitReader.skipBit(); // refdist flag
|
|
bitReader.skipBit(); // loop_filter
|
|
bitReader.skipBit(); // fastuvmc
|
|
int extended_mv = bitReader.getBit();
|
|
bitReader.skipBits(6); // dquant, vstransform, overlap, quantizer_mode
|
|
|
|
if (hrd_param_flag)
|
|
{
|
|
for (int i = 0; i < hrd_num_leaky_buckets; i++) bitReader.skipBits(8); // hrd_full[n]
|
|
}
|
|
|
|
if (bitReader.getBit())
|
|
{
|
|
coded_width = (bitReader.getBits(12) + 1) << 1;
|
|
coded_height = (bitReader.getBits(12) + 1) << 1;
|
|
}
|
|
if (extended_mv)
|
|
bitReader.skipBit(); // extended_dmv
|
|
if (bitReader.getBit())
|
|
{
|
|
// av_log(avctx, AV_LOG_ERROR, "Luma scaling is not supported, expect wrong picture\n");
|
|
bitReader.skipBits(3); // Y range, ignored for now
|
|
}
|
|
if (bitReader.getBit())
|
|
{
|
|
// av_log(avctx, AV_LOG_ERROR, "Chroma scaling is not supported, expect wrong picture\n");
|
|
bitReader.skipBits(3); // UV range, ignored for now
|
|
}
|
|
return 0;
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
// -------------------------- VC1Frame ---------------------------
|
|
|
|
int VC1Frame::decode_frame_direct(const VC1SequenceHeader& sequenceHdr, uint8_t* buffer, uint8_t* end)
|
|
{
|
|
try
|
|
{
|
|
bitReader.setBuffer(buffer, end); // skip 00 00 01 xx marker
|
|
if (sequenceHdr.profile < Profile::ADVANCED)
|
|
return vc1_parse_frame_header(sequenceHdr);
|
|
else
|
|
return vc1_parse_frame_header_adv(sequenceHdr);
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
int VC1Frame::vc1_parse_frame_header(const VC1SequenceHeader& sequenceHdr)
|
|
{
|
|
if (sequenceHdr.finterpflag)
|
|
bitReader.skipBit(); // interpfrm
|
|
bitReader.skipBits(2); // framecnt
|
|
if (sequenceHdr.rangered)
|
|
bitReader.skipBit(); // rangeredfrm
|
|
pict_type = (VC1PictType)bitReader.getBit();
|
|
if (sequenceHdr.max_b_frames > 0)
|
|
{
|
|
if (pict_type == VC1PictType::I_TYPE)
|
|
{
|
|
if (bitReader.getBit())
|
|
pict_type = VC1PictType::I_TYPE;
|
|
else
|
|
pict_type = VC1PictType::B_TYPE;
|
|
}
|
|
else
|
|
pict_type = VC1PictType::P_TYPE;
|
|
}
|
|
else
|
|
pict_type = (pict_type != VC1PictType::I_TYPE) ? VC1PictType::P_TYPE : VC1PictType::I_TYPE;
|
|
return 0;
|
|
}
|
|
|
|
int VC1Frame::vc1_parse_frame_header_adv(const VC1SequenceHeader& sequenceHdr)
|
|
{
|
|
fcm = sequenceHdr.interlace ? decode012(bitReader) : 0;
|
|
|
|
if (fcm == 2) // is coded field
|
|
{
|
|
switch (bitReader.getBits(3))
|
|
{
|
|
case 0:
|
|
case 1:
|
|
pict_type = VC1PictType::I_TYPE;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
pict_type = VC1PictType::P_TYPE;
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
pict_type = VC1PictType::B_TYPE;
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
pict_type = VC1PictType::BI_TYPE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (get_unary(bitReader, 0, 4))
|
|
{
|
|
case 0:
|
|
pict_type = VC1PictType::P_TYPE;
|
|
break;
|
|
case 1:
|
|
pict_type = VC1PictType::B_TYPE;
|
|
break;
|
|
case 2:
|
|
pict_type = VC1PictType::I_TYPE;
|
|
break;
|
|
case 3:
|
|
pict_type = VC1PictType::BI_TYPE;
|
|
break;
|
|
case 4:
|
|
pict_type = VC1PictType::P_TYPE; // skipped pic
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sequenceHdr.tfcntrflag)
|
|
bitReader.skipBits(8); // TFCNTR
|
|
if (sequenceHdr.pulldown)
|
|
{
|
|
rptfrmBitPos = bitReader.getBitsCount();
|
|
if (!sequenceHdr.interlace || sequenceHdr.psf)
|
|
{
|
|
rptfrm = bitReader.getBits(2); // Repeat Frame Count (0 .. 3)
|
|
}
|
|
else
|
|
{
|
|
tff = bitReader.getBit(); // TFF (top field first)
|
|
rff = bitReader.getBit(); // RFF (repeat first field)
|
|
}
|
|
}
|
|
return 0;
|
|
}
|