Support for AV1 MP4 input and output

This commit is contained in:
Thulinma 2023-06-11 16:26:27 +02:00
parent 8b8a28c4ec
commit a9abfa531e
12 changed files with 154 additions and 22 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -35,6 +35,7 @@ mistplayers.html5 = {
shortmime = shortmime.join("/");
//works for mp4 but not for webm
function translateCodec(track) {
if (track.codecstring){return track.codecstring;}
function bin2hex(index) {
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
}

View file

@ -22,6 +22,7 @@ mistplayers.mews = {
//check (and save) codec compatibility
function translateCodec(track) {
if (track.codecstring){return track.codecstring;}
function bin2hex(index) {
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
}

View file

@ -5,8 +5,9 @@
#include "defines.h"
#include "dtsc.h"
#include "encode.h"
#include "lib/shared_memory.h"
#include "lib/util.h"
#include "shared_memory.h"
#include "util.h"
#include "stream.h"
#include <arpa/inet.h> //for htonl/ntohl
#include <cstdlib>
#include <cstring>
@ -2710,6 +2711,7 @@ namespace DTSC{
std::string type = getType(*it);
trackJSON["codec"] = getCodec(*it);
trackJSON["codecstring"] = Util::codecString(getCodec(*it), getInit(*it));
trackJSON["type"] = type;
trackJSON["idx"] = (uint64_t)*it;
trackJSON["trackid"] = (uint64_t)getID(*it);

View file

@ -270,6 +270,8 @@ namespace MP4{
case 0x61616320: return ((AAC *)this)->toPrettyString(indent); break;
case 0x68766331:
case 0x68657631: return ((HEV1 *)this)->toPrettyString(indent); break;
case 0x61763031: return ((AV01 *)this)->toPrettyString(indent); break;
case 0x61763143: return ((AV1C *)this)->toPrettyString(indent); break;
case 0x61766331:
case 0x656E6376: // encv
return ((AVC1 *)this)->toPrettyString(indent);

View file

@ -799,6 +799,70 @@ namespace MP4{
return r.str();
}
AV1C::AV1C(){
memcpy(data + 4, "av1C", 4);
setInt8(0b10000001, 0); // Marker 1, version 1: 0b10000001
}
std::string AV1C::toPrettyString(uint32_t indent){
std::stringstream r;
r << std::string(indent, ' ') << "[av1C] AV1 Init Data (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Marker: " << (int)((getInt8(0) & 0b10000000) >> 7) << std::endl;
r << std::string(indent + 1, ' ') << "Version: " << (int)(getInt8(0) & 0b01111111) << std::endl;
r << std::string(indent + 1, ' ') << "Profile: " << (int)((getInt8(1) & 0b11100000) >> 5) << std::endl;
r << std::string(indent + 1, ' ') << "Level: " << (int)(getInt8(1) & 0b00011111) << std::endl;
r << std::string(indent + 1, ' ') << "Tier: " << (int)((getInt8(2) & 0b10000000) >> 7) << std::endl;
r << std::string(indent + 1, ' ') << "Bit depth: ";
switch ((getInt8(2) & 0b01100000)){
case 0b00000000: r << "8"; break;
case 0b01000000: r << "10"; break;
case 0b01100000: r << "12"; break;
case 0b00100000: r << "Unknown"; break;
}
r << std::endl;
r << std::string(indent + 1, ' ') << "Subsampling format: ";
switch ((getInt8(2) & 0b00011100)){
case 0b00000000: r << "YUV 4:4:4"; break;
case 0b00001000: r << "YUV 4:2:2"; break;
case 0b00001100: r << "YUV 4:2:0"; break;
case 0b00011100: r << "Monochrome 4:0:0"; break;
default: r << "Unknown";
}
r << std::endl;
r << std::string(indent + 1, ' ') << "Subsampling position: ";
switch ((getInt8(2) & 0b00000011)){
case 0: r << "Unknown"; break;
case 1: r << "Vertical"; break;
case 2: r << "Co-located"; break;
case 3: r << "Reserved"; break;
}
r << std::endl;
if (getInt8(3) & 0b00010000){
r << std::string(indent + 1, ' ') << "Initial presentation delay: " << (int)(getInt8(3) & 0b00001111) + 1 << std::endl;
}else{
r << std::string(indent + 1, ' ') << "Initial presentation delay: 0" << std::endl;
}
r << std::string(indent + 1, ' ') << (payloadSize() - 4) << "b of OBU initialization data" << std::endl;
return r.str();
}
void AV1C::setPayload(std::string newPayload){
if (!reserve(0, payloadSize(), newPayload.size())){
ERROR_MSG("Cannot allocate enough memory for payload");
return;
}
memcpy((char *)payload(), (char *)newPayload.c_str(), newPayload.size());
}
Descriptor::Descriptor(){
data = (char *)malloc(2);
data[0] = 0;
@ -2692,22 +2756,26 @@ namespace MP4{
avccBox.setPayload(M.getInit(idx));
setCLAP(avccBox);
}
/*LTS-START*/
if (tCodec == "HEVC"){
setCodec("hev1");
MP4::HVCC hvccBox;
hvccBox.setPayload(M.getInit(idx));
setCLAP(hvccBox);
}
/*LTS-END*/
if (tCodec == "AV1"){
setCodec("av01");
MP4::AV1C av1cBox;
av1cBox.setPayload(M.getInit(idx));
setCLAP(av1cBox);
}
MP4::PASP paspBox;
setPASP(paspBox);
}
void VisualSampleEntry::initialize(){
memcpy(data + 4, "erro", 4);
setHorizResolution(0x00480000);
setVertResolution(0x00480000);
setHorizResolution(72);
setVertResolution(72);
setFrameCount(1);
setCompressorName("");
setDepth(0x0018);
@ -2726,17 +2794,17 @@ namespace MP4{
uint16_t VisualSampleEntry::getHeight(){return getInt16(26);}
void VisualSampleEntry::setHorizResolution(uint32_t newHorizResolution){
setInt32(newHorizResolution, 28);
void VisualSampleEntry::setHorizResolution(double newHorizResolution){
setInt32(newHorizResolution * 65536.0, 28);
}
uint32_t VisualSampleEntry::getHorizResolution(){return getInt32(28);}
double VisualSampleEntry::getHorizResolution(){return getInt32(28) / 65536.0;}
void VisualSampleEntry::setVertResolution(uint32_t newVertResolution){
setInt32(newVertResolution, 32);
void VisualSampleEntry::setVertResolution(double newVertResolution){
setInt32(newVertResolution * 65536.0, 32);
}
uint32_t VisualSampleEntry::getVertResolution(){return getInt32(32);}
double VisualSampleEntry::getVertResolution(){return getInt32(32) / 65536.0;}
void VisualSampleEntry::setFrameCount(uint16_t newFrameCount){setInt16(newFrameCount, 40);}
@ -2839,8 +2907,8 @@ namespace MP4{
r << toPrettySampleString(indent);
r << std::string(indent + 1, ' ') << "Width: " << getWidth() << std::endl;
r << std::string(indent + 1, ' ') << "Height: " << getHeight() << std::endl;
r << std::string(indent + 1, ' ') << "HorizResolution: " << getHorizResolution() << std::endl;
r << std::string(indent + 1, ' ') << "VertResolution: " << getVertResolution() << std::endl;
r << std::string(indent + 1, ' ') << "HorizResolution: " << getHorizResolution() << " DPI" << std::endl;
r << std::string(indent + 1, ' ') << "VertResolution: " << getVertResolution() << " DPI" << std::endl;
r << std::string(indent + 1, ' ') << "FrameCount: " << getFrameCount() << std::endl;
r << std::string(indent + 1, ' ') << "CompressorName: " << getCompressorName() << std::endl;
r << std::string(indent + 1, ' ') << "Depth: " << getDepth() << std::endl;
@ -3249,6 +3317,12 @@ namespace MP4{
return toPrettyVisualString(indent, "[h264] H.264/MPEG-4 AVC");
}
AV01::AV01(){memcpy(data + 4, "av01", 4);}
std::string AV01::toPrettyString(uint32_t indent){
return toPrettyVisualString(indent, "[av01] AV1 Video");
}
FIEL::FIEL(){memcpy(data + 4, "fiel", 4);}
void FIEL::setTotal(char newTotal){setInt8(newTotal, 0);}

View file

@ -200,6 +200,13 @@ namespace MP4{
h265::metaInfo getMetaInfo();
};
class AV1C : public Box{
public:
AV1C();
void setPayload(std::string newPayload);
std::string toPrettyString(uint32_t indent = 0);
};
class Descriptor{
public:
Descriptor();
@ -694,10 +701,10 @@ namespace MP4{
uint16_t getWidth();
void setHeight(uint16_t newHeight);
uint16_t getHeight();
void setHorizResolution(uint32_t newHorizResolution);
uint32_t getHorizResolution();
void setVertResolution(uint32_t newVertResolution);
uint32_t getVertResolution();
void setHorizResolution(double newHorizResolution);
double getHorizResolution();
void setVertResolution(double newVertResolution);
double getVertResolution();
void setFrameCount(uint16_t newFrameCount);
uint16_t getFrameCount();
void setCompressorName(std::string newCompressorName);
@ -851,6 +858,12 @@ namespace MP4{
std::string toPrettyString(uint32_t indent = 0);
};
class AV01 : public VisualSampleEntry{
public:
AV01();
std::string toPrettyString(uint32_t indent = 0);
};
class FIEL : public Box{
public:
FIEL();

View file

@ -105,6 +105,26 @@ std::string Util::codecString(const std::string &codec, const std::string &initD
if (codec == "AAC"){return "mp4a.40.2";}
if (codec == "MP3"){return "mp4a.40.34";}
if (codec == "AC3"){return "mp4a.a5";}
if (codec == "AV1"){
if (initData.size() < 4){return "av01";}// Can't determine properties. :-(
std::stringstream r;
r << "av01.";
r << (int)((initData[1] & 0b11100000) >> 5); //profile
r << ".";
r << std::setw(2) << std::setfill('0') << (int)(initData[1] & 0b00011111); //level
r << ((initData[2] & 0b10000000)?"H":"M"); //tier
r << ".";
switch (initData[2] & 0b01100000){
case 0b00000000: r << "08"; break;
case 0b01000000: r << "10"; break;
case 0b01100000: r << "12"; break;
case 0b00100000: r << "??"; break;
}
/// \TODO Implement the full descriptor as in https://aomediacodec.github.io/av1-isobmff/#codecsparam
/// Init data follows this format:
/// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax
return r.str();
}
return "";
}

View file

@ -118,6 +118,7 @@ namespace Mist{
capa["codecs"]["video"].append("H264");
capa["codecs"]["video"].append("H263");
capa["codecs"]["video"].append("VP6");
capa["codecs"]["video"].append("AV1");
capa["codecs"]["audio"].append("AAC");
capa["codecs"]["audio"].append("AC3");
capa["codecs"]["audio"].append("MP3");
@ -257,7 +258,7 @@ namespace Mist{
MP4::Box sEntryBox = stsdBox.getEntry(0);
std::string sType = sEntryBox.getType();
if (!(sType == "avc1" || sType == "h264" || sType == "mp4v" || sType == "hev1" || sType == "hvc1" || sType == "mp4a" || sType == "aac " || sType == "ac-3" || sType == "tx3g")){
if (!(sType == "avc1" || sType == "h264" || sType == "mp4v" || sType == "hev1" || sType == "hvc1" || sType == "mp4a" || sType == "aac " || sType == "ac-3" || sType == "tx3g" || sType == "av01")){
INFO_MSG("Unsupported track type: %s", sType.c_str());
continue;
}
@ -320,6 +321,23 @@ namespace Mist{
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
}
if (sType == "av01"){
MP4::VisualSampleEntry &vEntryBox = (MP4::VisualSampleEntry &)sEntryBox;
meta.setType(tNumber, "video");
meta.setCodec(tNumber, "AV1");
if (!meta.getWidth(tNumber)){
meta.setWidth(tNumber, vEntryBox.getWidth());
meta.setHeight(tNumber, vEntryBox.getHeight());
}
MP4::Box initBox = vEntryBox.getCLAP();
if (initBox.isType("av1C")){
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
initBox = vEntryBox.getPASP();
if (initBox.isType("av1C")){
meta.setInit(tNumber, initBox.payload(), initBox.payloadSize());
}
}
if (sType == "mp4a" || sType == "aac " || sType == "ac-3"){
MP4::AudioSampleEntry &aEntryBox = (MP4::AudioSampleEntry &)sEntryBox;
meta.setType(tNumber, "audio");

View file

@ -127,6 +127,7 @@ namespace Mist{
capa["url_match"][2u] = "/$.fmp4";
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][0u].append("HEVC");
capa["codecs"][0u][0u].append("AV1");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
capa["codecs"][0u][1u].append("AC3");