Support for AV1 MP4 input and output
This commit is contained in:
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
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 "";
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue