You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-15 14:13:16 +02:00
avformat/movenc: add support for AV1 streams
Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
@@ -302,7 +302,7 @@ OBJS-$(CONFIG_MM_DEMUXER) += mm.o
|
|||||||
OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o
|
OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o
|
||||||
OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o
|
OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o
|
||||||
OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o replaygain.o
|
OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o replaygain.o
|
||||||
OBJS-$(CONFIG_MOV_MUXER) += movenc.o avc.o hevc.o vpcc.o \
|
OBJS-$(CONFIG_MOV_MUXER) += movenc.o av1.o avc.o hevc.o vpcc.o \
|
||||||
movenchint.o mov_chan.o rtp.o \
|
movenchint.o mov_chan.o rtp.o \
|
||||||
movenccenc.o rawutils.o
|
movenccenc.o rawutils.o
|
||||||
OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
|
OBJS-$(CONFIG_MP2_MUXER) += rawenc.o
|
||||||
|
108
libavformat/av1.c
Normal file
108
libavformat/av1.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* AV1 helper functions for muxers
|
||||||
|
* Copyright (c) 2018 James Almer <jamrial@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libavutil/mem.h"
|
||||||
|
#include "libavcodec/av1.h"
|
||||||
|
#include "libavcodec/av1_parse.h"
|
||||||
|
#include "av1.h"
|
||||||
|
#include "avio.h"
|
||||||
|
|
||||||
|
int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
const uint8_t *end = buf + size;
|
||||||
|
int64_t obu_size;
|
||||||
|
int start_pos, type, temporal_id, spatial_id;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
while (buf < end) {
|
||||||
|
int ret = parse_obu_header(buf, end - buf, &obu_size, &start_pos,
|
||||||
|
&type, &temporal_id, &spatial_id);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
obu_size += start_pos;
|
||||||
|
if (obu_size > INT_MAX)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case AV1_OBU_TEMPORAL_DELIMITER:
|
||||||
|
case AV1_OBU_REDUNDANT_FRAME_HEADER:
|
||||||
|
case AV1_OBU_PADDING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
avio_write(pb, buf, obu_size);
|
||||||
|
size += obu_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf += obu_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ff_av1_filter_obus_buf(const uint8_t *buf, uint8_t **out, int *size)
|
||||||
|
{
|
||||||
|
AVIOContext *pb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = avio_open_dyn_buf(&pb);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ff_av1_filter_obus(pb, buf, *size);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
av_freep(out);
|
||||||
|
*size = avio_close_dyn_buf(pb, out);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
int64_t obu_size;
|
||||||
|
int start_pos, type, temporal_id, spatial_id;
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
int ret = parse_obu_header(buf, size, &obu_size, &start_pos,
|
||||||
|
&type, &temporal_id, &spatial_id);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
obu_size += start_pos;
|
||||||
|
if (obu_size > INT_MAX)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case AV1_OBU_SEQUENCE_HEADER:
|
||||||
|
case AV1_OBU_METADATA:
|
||||||
|
avio_write(pb, buf, obu_size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size -= obu_size;
|
||||||
|
buf += obu_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
70
libavformat/av1.h
Normal file
70
libavformat/av1.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* AV1 helper functions for muxers
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVFORMAT_AV1_H
|
||||||
|
#define AVFORMAT_AV1_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "avio.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and write
|
||||||
|
* the resulting bitstream to the provided AVIOContext.
|
||||||
|
*
|
||||||
|
* @param pb pointer to the AVIOContext where the filtered bitstream shall be
|
||||||
|
* written
|
||||||
|
* @param buf input data buffer
|
||||||
|
* @param size size of the input data buffer
|
||||||
|
*
|
||||||
|
* @return the amount of bytes written in case of success, a negative AVERROR
|
||||||
|
* code in case of failure
|
||||||
|
*/
|
||||||
|
int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and write
|
||||||
|
* the resulting bitstream to a newly allocated data buffer.
|
||||||
|
*
|
||||||
|
* @param pb pointer to the AVIOContext where the filtered bitstream shall be
|
||||||
|
* written
|
||||||
|
* @param buf input data buffer
|
||||||
|
* @param out pointer to pointer that will hold the allocated data buffer
|
||||||
|
* @param size size of the input data buffer. The size of the resulting output
|
||||||
|
data buffer will be written here
|
||||||
|
*
|
||||||
|
* @return the amount of bytes written in case of success, a negative AVERROR
|
||||||
|
* code in case of failure. On failure, out and size are unchanged
|
||||||
|
*/
|
||||||
|
int ff_av1_filter_obus_buf(const uint8_t *buf, uint8_t **out, int *size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes AV1 extradata (Sequence Header and Metadata OBUs) to the provided
|
||||||
|
* AVIOContext.
|
||||||
|
*
|
||||||
|
* @param pb pointer to the AVIOContext where the hvcC shall be written
|
||||||
|
* @param buf input data buffer
|
||||||
|
* @param size size in bytes of the input data buffer
|
||||||
|
*
|
||||||
|
* @return >= 0 in case of success, a negative AVERROR code in case of failure
|
||||||
|
*/
|
||||||
|
int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size);
|
||||||
|
|
||||||
|
#endif /* AVFORMAT_AV1_H */
|
@@ -30,6 +30,7 @@
|
|||||||
#include "riff.h"
|
#include "riff.h"
|
||||||
#include "avio.h"
|
#include "avio.h"
|
||||||
#include "isom.h"
|
#include "isom.h"
|
||||||
|
#include "av1.h"
|
||||||
#include "avc.h"
|
#include "avc.h"
|
||||||
#include "libavcodec/ac3_parser_internal.h"
|
#include "libavcodec/ac3_parser_internal.h"
|
||||||
#include "libavcodec/dnxhddata.h"
|
#include "libavcodec/dnxhddata.h"
|
||||||
@@ -1163,6 +1164,19 @@ static int mov_write_d263_tag(AVIOContext *pb)
|
|||||||
return 0xf;
|
return 0xf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_write_av1c_tag(AVIOContext *pb, MOVTrack *track)
|
||||||
|
{
|
||||||
|
int64_t pos = avio_tell(pb);
|
||||||
|
|
||||||
|
avio_wb32(pb, 0);
|
||||||
|
ffio_wfourcc(pb, "av1C");
|
||||||
|
avio_w8(pb, 0); /* version */
|
||||||
|
avio_wb24(pb, 0); /* flags */
|
||||||
|
avio_w8(pb, 0); /* reserved (3), initial_presentation_delay_present (1), initial_presentation_delay_minus_one/reserved (4) */
|
||||||
|
ff_isom_write_av1c(pb, track->vos_data, track->vos_len);
|
||||||
|
return update_size(pb, pos);
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track)
|
static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track)
|
||||||
{
|
{
|
||||||
int64_t pos = avio_tell(pb);
|
int64_t pos = avio_tell(pb);
|
||||||
@@ -2009,6 +2023,8 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr
|
|||||||
mov_write_uuid_tag_ipod(pb);
|
mov_write_uuid_tag_ipod(pb);
|
||||||
} else if (track->par->codec_id == AV_CODEC_ID_VP9) {
|
} else if (track->par->codec_id == AV_CODEC_ID_VP9) {
|
||||||
mov_write_vpcc_tag(mov->fc, pb, track);
|
mov_write_vpcc_tag(mov->fc, pb, track);
|
||||||
|
} else if (track->par->codec_id == AV_CODEC_ID_AV1) {
|
||||||
|
mov_write_av1c_tag(pb, track);
|
||||||
} else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
|
} else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0)
|
||||||
mov_write_dvc1_tag(pb, track);
|
mov_write_dvc1_tag(pb, track);
|
||||||
else if (track->par->codec_id == AV_CODEC_ID_VP6F ||
|
else if (track->par->codec_id == AV_CODEC_ID_VP6F ||
|
||||||
@@ -5324,6 +5340,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
} else {
|
} else {
|
||||||
size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
|
size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL);
|
||||||
}
|
}
|
||||||
|
} else if (par->codec_id == AV_CODEC_ID_AV1) {
|
||||||
|
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) {
|
||||||
|
ff_av1_filter_obus_buf(pkt->data, &reformatted_data, &size);
|
||||||
|
avio_write(pb, reformatted_data, size);
|
||||||
|
} else {
|
||||||
|
size = ff_av1_filter_obus(pb, pkt->data, pkt->size);
|
||||||
|
}
|
||||||
#if CONFIG_AC3_PARSER
|
#if CONFIG_AC3_PARSER
|
||||||
} else if (par->codec_id == AV_CODEC_ID_EAC3) {
|
} else if (par->codec_id == AV_CODEC_ID_EAC3) {
|
||||||
size = handle_eac3(mov, pkt, trk);
|
size = handle_eac3(mov, pkt, trk);
|
||||||
@@ -5517,6 +5540,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
|
|
||||||
if (trk->par->codec_id == AV_CODEC_ID_MP4ALS ||
|
if (trk->par->codec_id == AV_CODEC_ID_MP4ALS ||
|
||||||
trk->par->codec_id == AV_CODEC_ID_AAC ||
|
trk->par->codec_id == AV_CODEC_ID_AAC ||
|
||||||
|
trk->par->codec_id == AV_CODEC_ID_AV1 ||
|
||||||
trk->par->codec_id == AV_CODEC_ID_FLAC) {
|
trk->par->codec_id == AV_CODEC_ID_FLAC) {
|
||||||
int side_size = 0;
|
int side_size = 0;
|
||||||
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
|
uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
|
||||||
@@ -6214,15 +6238,20 @@ static int mov_init(AVFormatContext *s)
|
|||||||
pix_fmt == AV_PIX_FMT_MONOWHITE ||
|
pix_fmt == AV_PIX_FMT_MONOWHITE ||
|
||||||
pix_fmt == AV_PIX_FMT_MONOBLACK;
|
pix_fmt == AV_PIX_FMT_MONOBLACK;
|
||||||
}
|
}
|
||||||
if (track->par->codec_id == AV_CODEC_ID_VP9) {
|
if (track->par->codec_id == AV_CODEC_ID_VP9 ||
|
||||||
|
track->par->codec_id == AV_CODEC_ID_AV1) {
|
||||||
if (track->mode != MODE_MP4) {
|
if (track->mode != MODE_MP4) {
|
||||||
av_log(s, AV_LOG_ERROR, "VP9 only supported in MP4.\n");
|
av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id));
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
} else if (track->par->codec_id == AV_CODEC_ID_AV1) {
|
if (track->par->codec_id == AV_CODEC_ID_AV1 &&
|
||||||
/* spec is not finished, so forbid for now */
|
s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
|
||||||
av_log(s, AV_LOG_ERROR, "AV1 muxing is currently not supported.\n");
|
av_log(s, AV_LOG_ERROR,
|
||||||
return AVERROR_PATCHWELCOME;
|
"av1 in MP4 support is experimental, add "
|
||||||
|
"'-strict %d' if you want to use it.\n",
|
||||||
|
FF_COMPLIANCE_EXPERIMENTAL);
|
||||||
|
return AVERROR_EXPERIMENTAL;
|
||||||
|
}
|
||||||
} else if (track->par->codec_id == AV_CODEC_ID_VP8) {
|
} else if (track->par->codec_id == AV_CODEC_ID_VP8) {
|
||||||
/* altref frames handling is not defined in the spec as of version v1.0,
|
/* altref frames handling is not defined in the spec as of version v1.0,
|
||||||
* so just forbid muxing VP8 streams altogether until a new version does */
|
* so just forbid muxing VP8 streams altogether until a new version does */
|
||||||
@@ -6737,6 +6766,7 @@ const AVCodecTag codec_mp4_tags[] = {
|
|||||||
{ AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') },
|
{ AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') },
|
||||||
{ AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') },
|
{ AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') },
|
||||||
{ AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') },
|
{ AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') },
|
||||||
|
{ AV_CODEC_ID_AV1 , MKTAG('a', 'v', '0', '1') },
|
||||||
{ AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') },
|
{ AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') },
|
||||||
{ AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') },
|
{ AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') },
|
||||||
{ AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') },
|
{ AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') },
|
||||||
|
Reference in New Issue
Block a user