Skip to content

Commit

Permalink
Add lhvC parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristianFeldmann committed Mar 24, 2024
1 parent f7b50b8 commit 849027e
Show file tree
Hide file tree
Showing 15 changed files with 446 additions and 110 deletions.
10 changes: 10 additions & 0 deletions YUViewLib/src/common/Functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@ std::string toLower(std::string str)
return str;
}

ByteVector readData(std::istream &istream, const size_t nrBytes)
{
ByteVector data;
data.resize(nrBytes);
istream.read(reinterpret_cast<char *>(data.data()), nrBytes);
const auto nrBytesActuallyRead = istream.gcount();
data.resize(nrBytesActuallyRead);
return data;
}

std::optional<unsigned long> toUnsigned(const std::string &text)
{
try
Expand Down
2 changes: 2 additions & 0 deletions YUViewLib/src/common/Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include <common/Typedef.h>

#include <istream>
#include <optional>

namespace functions
Expand Down Expand Up @@ -64,6 +65,7 @@ QString formatDataSize(double size, bool isBits = false);

QStringList toQStringList(const std::vector<std::string> &stringVec);
std::string toLower(std::string str);
ByteVector readData(std::istream &istream, const size_t nrBytes);

inline std::string boolToString(bool b)
{
Expand Down
25 changes: 25 additions & 0 deletions YUViewLib/src/ffmpeg/AVInputFormatWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,31 @@ AVInputFormatWrapper::AVInputFormatWrapper(AVInputFormat *f, LibraryVersion v)
this->update();
}

QString AVInputFormatWrapper::getName() const
{
return this->name;
}

QString AVInputFormatWrapper::getLongName() const
{
return this->long_name;
}

int AVInputFormatWrapper::getFlags() const
{
return this->flags;
}

QString AVInputFormatWrapper::getExtensions() const
{
return this->extensions;
}

QString AVInputFormatWrapper::getMimeType() const
{
return this->mime_type;
}

void AVInputFormatWrapper::update()
{
if (this->fmt == nullptr)
Expand Down
6 changes: 6 additions & 0 deletions YUViewLib/src/ffmpeg/AVInputFormatWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ class AVInputFormatWrapper
AVInputFormatWrapper();
AVInputFormatWrapper(AVInputFormat *f, LibraryVersion v);

QString getName() const;
QString getLongName() const;
int getFlags() const;
QString getExtensions() const;
QString getMimeType() const;

explicit operator bool() const { return fmt != nullptr; };

private:
Expand Down
44 changes: 44 additions & 0 deletions YUViewLib/src/filesource/FileSourceFFmpegFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include <QProgressDialog>
#include <QSettings>
#include <fstream>

#include <common/Formatting.h>
#include <ffmpeg/AVCodecContextWrapper.h>
Expand All @@ -56,8 +57,18 @@ namespace

auto startCode = QByteArrayLiteral("\x00\x00\x01");

uint64_t getBoxSize(ByteVector::const_iterator iterator)
{
uint64_t size = 0;
size += static_cast<uint64_t>(*(iterator++)) << (8 * 3);
size += static_cast<uint64_t>(*(iterator++)) << (8 * 2);
size += static_cast<uint64_t>(*(iterator++)) << (8 * 1);
size += static_cast<uint64_t>(*iterator);
return size;
}

} // namespace

FileSourceFFmpegFile::FileSourceFFmpegFile()
{
connect(&this->fileWatcher,
Expand Down Expand Up @@ -217,6 +228,38 @@ StringPairVec FileSourceFFmpegFile::getMetadata()
return ff.getDictionaryEntries(this->formatCtx.getMetadata(), "", 0);
}

ByteVector FileSourceFFmpegFile::getLhvCData()
{
const auto inputFormat = this->formatCtx.getInputFormat();
const auto isMp4 = inputFormat.getName().contains("mp4");
if (!this->getVideoStreamCodecID().isHEVC() || !isMp4)
return {};

// This is a bit of a hack. The problem is that FFmpeg can currently not extract this for us.
// Maybe this will be added in the future. So the only option we have here is to manually extract
// the lhvC data from the mp4 file.
std::ifstream input(this->fileName.toStdString(), std::ios::binary);
const auto rawFileData = functions::readData(input, 1024);
if (rawFileData.empty())
return {};

const std::string searchString = "lhvC";
const auto lhvcPos =
std::search(rawFileData.begin(), rawFileData.end(), searchString.begin(), searchString.end());
if (lhvcPos == rawFileData.end())
return {};

if (std::distance(rawFileData.begin(), lhvcPos) < 4)
return {};

const auto boxSize = getBoxSize(lhvcPos - 4);
if (boxSize == 0 || boxSize > std::distance(lhvcPos, rawFileData.end()))
return {};

// We just return the payload without the box size or the "lhvC" tag
return ByteVector(lhvcPos + 4, lhvcPos + boxSize - 4);
}

QList<QByteArray> FileSourceFFmpegFile::getParameterSets()
{
if (!this->isFileOpened)
Expand Down Expand Up @@ -484,6 +527,7 @@ void FileSourceFFmpegFile::openFileAndFindVideoStream(QString fileName)
if (!this->ff.openInput(this->formatCtx, fileName))
return;

this->fileName = fileName;
this->formatCtx.getInputFormat();

for (unsigned idx = 0; idx < this->formatCtx.getNbStreams(); idx++)
Expand Down
4 changes: 4 additions & 0 deletions YUViewLib/src/filesource/FileSourceFFmpegFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "FileSource.h"
#include <ffmpeg/AVCodecIDWrapper.h>
#include <ffmpeg/AVCodecParametersWrapper.h>
#include <ffmpeg/AVInputFormatWrapper.h>
#include <ffmpeg/AVPacketWrapper.h>
#include <ffmpeg/FFmpegVersionHandler.h>
#include <video/rgb/videoHandlerRGB.h>
Expand Down Expand Up @@ -85,6 +86,7 @@ class FileSourceFFmpegFile : public QObject

QByteArray getExtradata();
StringPairVec getMetadata();
ByteVector getLhvCData();
// Return a list containing the raw data of all parameter set NAL units
QList<QByteArray> getParameterSets();

Expand Down Expand Up @@ -135,6 +137,8 @@ private slots:
FFmpeg::AVPacketWrapper currentPacket; //< A place for the curren (frame) input buffer
bool endOfFile{false}; //< Are we at the end of file (draining mode)?

QString fileName;

// Seek the stream to the given pts value, flush the decoder and load the first packet so
// that we are ready to start decoding from this pts.
int64_t duration{-1}; //< duration / AV_TIME_BASE is the duration in seconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,57 +30,17 @@
* along with this program. If not, see <http:https://www.gnu.org/licenses/>.
*/

#include "HVCC.h"
#include "DecoderConfigurationHvcC.h"

namespace parser::avformat
{

using namespace reader;

void HVCCNalUnit::parse(unsigned unitID,
SubByteReaderLogging &reader,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel)
{
SubByteReaderLoggingSubLevel subLevel(reader, "nal unit " + std::to_string(unitID));

this->nalUnitLength = reader.readBits("nalUnitLength", 16);

// Get the bytes of the raw nal unit to pass to the "real" hevc parser
auto nalData = reader.readBytes("", nalUnitLength, Options().withLoggingDisabled());

// Let the hevc annexB parser parse this
auto parseResult =
hevcParser->parseAndAddNALUnit(unitID, nalData, {}, {}, reader.getCurrentItemTree());
if (parseResult.success && bitrateModel != nullptr && parseResult.bitrateEntry)
bitrateModel->addBitratePoint(0, *parseResult.bitrateEntry);
}

void HVCCNalArray::parse(unsigned arrayID,
SubByteReaderLogging &reader,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel)
{
SubByteReaderLoggingSubLevel subLevel(reader, "nal unit array " + std::to_string(arrayID));

// The next 3 bytes contain info about the array
this->array_completeness = reader.readFlag("array_completeness");
reader.readFlag("reserved_flag_false", Options().withCheckEqualTo(0, CheckLevel::Warning));
this->nal_unit_type = reader.readBits("nal_unit_type", 6);
this->numNalus = reader.readBits("numNalus", 16);

for (unsigned i = 0; i < numNalus; i++)
{
HVCCNalUnit nal;
nal.parse(i, reader, hevcParser, bitrateModel);
nalList.push_back(nal);
}
}

void HVCC::parse(ByteVector & data,
std::shared_ptr<TreeItem> root,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel)
void DecoderConfigurationHvcC::parse(const ByteVector & data,
std::shared_ptr<TreeItem> root,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel)
{
SubByteReaderLogging reader(data, root, "Extradata (HEVC hvcC format)");
reader.disableEmulationPrevention();
Expand Down Expand Up @@ -123,10 +83,10 @@ void HVCC::parse(ByteVector & data,
// Now parse the contained raw NAL unit arrays
for (unsigned i = 0; i < this->numOfArrays; i++)
{
HVCCNalArray a;
a.parse(i, reader, hevcParser, bitrateModel);
this->naluArrays.push_back(a);
DecoderConfigurationNALArray array;
array.parse(i, reader, hevcParser, bitrateModel);
this->naluArrays.push_back(array);
}
}

} // namespace parser::avformat
} // namespace parser::avformat
Original file line number Diff line number Diff line change
Expand Up @@ -32,50 +32,21 @@

#pragma once

#include <common/Typedef.h>
#include <parser/HEVC/ParserAnnexBHEVC.h>
#include <parser/common/BitratePlotModel.h>
#include <parser/common/SubByteReaderLogging.h>
#include <parser/common/TreeItem.h>

namespace parser::avformat
{
#include "DecoderConfigurationNalArray.h"

class HVCCNalUnit
{
public:
HVCCNalUnit() = default;

void parse(unsigned unitID,
reader::SubByteReaderLogging &reader,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel);

unsigned nalUnitLength{};
};

class HVCCNalArray
namespace parser::avformat
{
public:
HVCCNalArray() = default;

void parse(unsigned arrayID,
reader::SubByteReaderLogging &reader,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel);

bool array_completeness{};
unsigned nal_unit_type{};
unsigned numNalus{};
vector<HVCCNalUnit> nalList;
};

class HVCC
class DecoderConfigurationHvcC
{
public:
HVCC() = default;
DecoderConfigurationHvcC() = default;

void parse(ByteVector & data,
void parse(const ByteVector & data,
std::shared_ptr<TreeItem> root,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel);
Expand All @@ -99,7 +70,7 @@ class HVCC
unsigned lengthSizeMinusOne{};
unsigned numOfArrays{};

vector<HVCCNalArray> naluArrays;
std::vector<DecoderConfigurationNALArray> naluArrays;
};

} // namespace parser::avformat
} // namespace parser::avformat
76 changes: 76 additions & 0 deletions YUViewLib/src/parser/AVFormat/DecoderConfigurationLhvC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* This file is part of YUView - The YUV player with advanced analytics toolset
* <https://github.com/IENT/YUView>
* Copyright (C) 2015 Institut f�r Nachrichtentechnik, RWTH Aachen University, GERMANY
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*
* You must obey the GNU General Public License in all respects for all
* of the code used other than OpenSSL. If you modify file(s) with this
* exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http:https://www.gnu.org/licenses/>.
*/

#include "DecoderConfigurationLhvC.h"

namespace parser::avformat
{

using namespace reader;

void DecoderConfigurationLhvC::parse(const ByteVector & data,
std::shared_ptr<TreeItem> root,
ParserAnnexBHEVC * hevcParser,
BitratePlotModel * bitrateModel)
{
SubByteReaderLogging reader(data, root, "Extradata (HEVC lhvC format)");
reader.disableEmulationPrevention();

// ISO/IEC 14496-15, 9.6.3
this->configurationVersion =
reader.readBits("configurationVersion", 8, Options().withCheckEqualTo(1));
reader.readBits("reserved_4onebits", 4, Options().withCheckEqualTo(15));
this->min_spatial_segmentation_idc = reader.readBits("min_spatial_segmentation_idc", 12);
reader.readBits("reserver_6onebits", 6, Options().withCheckEqualTo(63));
this->parallelismType =
reader.readBits("parallelismType",
2,
Options().withMeaningVector({"mixed-type parallel decoding",
"slice-based parallel decoding",
"tile-based parallel decoding",
"wavefront-based parallel decoding"}));
reader.readBits("reserver_2onebits", 2, Options().withCheckEqualTo(3));
this->numTemporalLayers = reader.readBits("numTemporalLayers", 3);
this->temporalIdNested = reader.readFlag("temporalIdNested");
this->lengthSizeMinusOne = reader.readBits("lengthSizeMinusOne", 2);
this->numOfArrays = reader.readBits("numOfArrays", 8);

// Now parse the contained raw NAL unit arrays
for (unsigned i = 0; i < this->numOfArrays; i++)
{
DecoderConfigurationNALArray array;
array.parse(i, reader, hevcParser, bitrateModel);
this->naluArrays.push_back(array);
}
}

} // namespace parser::avformat
Loading

0 comments on commit 849027e

Please sign in to comment.