diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c index a3301f454f6e3..5e606778c3587 100644 --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -678,7 +678,6 @@ static int set_context_with_sequence(AVCodecContext *avctx, if (ret < 0) return ret; } - avctx->sample_aspect_ratio = (AVRational) { 1, 1 }; if (seq->timing_info.num_units_in_display_tick && seq->timing_info.time_scale) { diff --git a/libavformat/Makefile b/libavformat/Makefile index ed1ba0848def2..1a9e37916650c 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -214,7 +214,7 @@ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o -OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o +OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o hevc.o av1.o vpcc.o OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o OBJS-$(CONFIG_FRAMEHASH_MUXER) += hashenc.o framehash.o diff --git a/libavformat/av1.c b/libavformat/av1.c index 5512c4e0f7748..589d8f345c7b3 100644 --- a/libavformat/av1.c +++ b/libavformat/av1.c @@ -361,7 +361,8 @@ int ff_av1_parse_seq_header(AV1SequenceParameters *seq, const uint8_t *buf, int return AVERROR_INVALIDDATA; } -int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size) +int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, + int write_seq_header) { AVIOContext *meta_pb; AV1SequenceParameters seq_params; @@ -451,7 +452,9 @@ int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size) flush_put_bits(&pbc); avio_write(pb, header, sizeof(header)); - avio_write(pb, seq, seq_size); + if (write_seq_header) { + avio_write(pb, seq, seq_size); + } meta_size = avio_get_dyn_buf(meta_pb, &meta); if (meta_size) diff --git a/libavformat/av1.h b/libavformat/av1.h index dd5b47dc252b8..c78ab248147fd 100644 --- a/libavformat/av1.h +++ b/libavformat/av1.h @@ -94,9 +94,11 @@ int ff_av1_parse_seq_header(AV1SequenceParameters *seq, const uint8_t *buf, int * @param pb pointer to the AVIOContext where the av1C box shall be written * @param buf input data buffer * @param size size in bytes of the input data buffer + * @param write_seq_header If 1, Sequence Header OBU will be written inside the + * av1C box. Otherwise, Sequence Header OBU will be omitted. * * @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); +int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, int write_seq_header); #endif /* AVFORMAT_AV1_H */ diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 81a5c2b452645..a9be08b3fd790 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -346,7 +346,7 @@ static int check_file_extension(const char *filename, const char *extension) { static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, AVRational *frame_rate, char *str, int size) { VPCC vpcc; - int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc); + int ret = ff_isom_get_vpcc_features(s, par, NULL, 0, frame_rate, &vpcc); if (ret == 0) { av_strlcatf(str, size, "vp09.%02d.%02d.%02d", vpcc.profile, vpcc.level, vpcc.bitdepth); diff --git a/libavformat/flv.h b/libavformat/flv.h index 91f006520ca23..62297ed54df42 100644 --- a/libavformat/flv.h +++ b/libavformat/flv.h @@ -35,6 +35,12 @@ #define FLV_VIDEO_FRAMETYPE_OFFSET 4 +/* Extended VideoTagHeader + * defined in reference link: + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ +#define FLV_IS_EX_HEADER 0x80 + /* bitmasks to isolate specific values */ #define FLV_AUDIO_CHANNEL_MASK 0x01 #define FLV_AUDIO_SAMPLESIZE_MASK 0x02 @@ -42,7 +48,7 @@ #define FLV_AUDIO_CODECID_MASK 0xf0 #define FLV_VIDEO_CODECID_MASK 0x0f -#define FLV_VIDEO_FRAMETYPE_MASK 0xf0 +#define FLV_VIDEO_FRAMETYPE_MASK 0x70 #define AMF_END_OF_OBJECT 0x09 @@ -113,6 +119,15 @@ enum { FLV_CODECID_HEVC = 12, }; +enum { + PacketTypeSequenceStart = 0, + PacketTypeCodedFrames = 1, + PacketTypeSequenceEnd = 2, + PacketTypeCodedFramesX = 3, + PacketTypeMetadata = 4, + PacketTypeMPEG2TSSequenceStart = 5, +}; + enum { FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index bd0b6067df4da..def54e2112292 100644 --- a/libavformat/flvdec.c +++ b/libavformat/flvdec.c @@ -77,6 +77,8 @@ typedef struct FLVContext { int64_t last_ts; int64_t time_offset; int64_t time_pos; + + uint8_t exheader; } FLVContext; /* AMF date type */ @@ -300,14 +302,18 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, } } -static int flv_same_video_codec(AVCodecParameters *vpar, int flags) +static int flv_same_video_codec(AVCodecParameters *vpar, uint32_t flv_codecid) { - int flv_codecid = flags & FLV_VIDEO_CODECID_MASK; - if (!vpar->codec_id && !vpar->codec_tag) return 1; switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + return vpar->codec_id == AV_CODEC_ID_HEVC; + case MKBETAG('a', 'v', '0', '1'): + return vpar->codec_id == AV_CODEC_ID_AV1; + case MKBETAG('v', 'p', '0', '9'): + return vpar->codec_id == AV_CODEC_ID_VP9; case FLV_CODECID_H263: return vpar->codec_id == AV_CODEC_ID_FLV1; case FLV_CODECID_SCREEN: @@ -328,12 +334,26 @@ static int flv_same_video_codec(AVCodecParameters *vpar, int flags) } static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, - int flv_codecid, int read) + uint32_t flv_codecid, int read) { + AVStream *const vstreami = vstream; int ret = 0; AVCodecParameters *par = vstream->codecpar; enum AVCodecID old_codec_id = vstream->codecpar->codec_id; + switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + par->codec_id = AV_CODEC_ID_HEVC; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('a', 'v', '0', '1'): + par->codec_id = AV_CODEC_ID_AV1; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('v', 'p', '0', '9'): + par->codec_id = AV_CODEC_ID_VP9; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; case FLV_CODECID_H263: par->codec_id = AV_CODEC_ID_FLV1; break; @@ -365,16 +385,13 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, case FLV_CODECID_H264: par->codec_id = AV_CODEC_ID_H264; vstream->need_parsing = AVSTREAM_PARSE_HEADERS; - ret = 3; // not 4, reading packet type will consume one byte break; case FLV_CODECID_HEVC: par->codec_id = AV_CODEC_ID_HEVC; vstream->need_parsing = AVSTREAM_PARSE_NONE; - ret = 3; // not 4, reading packet type will consume one byte break; case FLV_CODECID_MPEG4: par->codec_id = AV_CODEC_ID_MPEG4; - ret = 3; break; default: avpriv_request_sample(s, "Video codec (%x)", flv_codecid); @@ -803,6 +820,7 @@ static int flv_read_header(AVFormatContext *s) s->start_time = 0; flv->sum_flv_tag_size = 0; flv->last_keyframe_stream_index = -1; + flv->exheader = 0; return 0; } @@ -1033,6 +1051,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = NULL; int last = -1; int orig_size; + uint32_t video_codec_id = 0; retry: /* pkt size is repeated at end. skip it */ @@ -1079,7 +1098,17 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) } else if (type == FLV_TAG_TYPE_VIDEO) { stream_type = FLV_STREAM_TYPE_VIDEO; flags = avio_r8(s->pb); + video_codec_id = flags & FLV_VIDEO_CODECID_MASK; + /* + * Reference Enhancing FLV 2023-03-v1.0.0-B.8 + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ + flv->exheader = (flags >> 7) & 1; size--; + if (flv->exheader) { + video_codec_id = avio_rb32(s->pb); + size -= 4; + } if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) goto skip; } else if (type == FLV_TAG_TYPE_META) { @@ -1137,7 +1166,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) break; } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - (s->video_codec_id || flv_same_video_codec(st->codecpar, flags))) + (s->video_codec_id || flv_same_video_codec(st->codecpar, video_codec_id))) break; } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) { if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) @@ -1240,7 +1269,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) avcodec_parameters_free(&par); } } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { - int ret = flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1); + int ret = flv_set_video_codec(s, st, video_codec_id, 1); if (ret < 0) return ret; size -= ret; @@ -1253,16 +1282,24 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_AAC || st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || - st->codecpar->codec_id == AV_CODEC_ID_HEVC) { - int type = avio_r8(s->pb); - size--; + st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || + st->codecpar->codec_id == AV_CODEC_ID_VP9) { + int type = 0; + if (flv->exheader && stream_type == FLV_STREAM_TYPE_VIDEO) { + type = flags & 0x0F; + } else { + type = avio_r8(s->pb); + size--; + } if (size < 0) { ret = AVERROR_INVALIDDATA; goto leave; } - if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || st->codecpar->codec_id == AV_CODEC_ID_HEVC) { + if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || + (st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) { // sign extension int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000; pts = av_sat_add64(dts, cts); @@ -1276,9 +1313,11 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts); dts = pts = AV_NOPTS_VALUE; } + size -= 3; } if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC || - st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC)) { + st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || st->codecpar->codec_id == AV_CODEC_ID_VP9)) { AVDictionaryEntry *t; if (st->codecpar->extradata) { if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0) diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index 35bf7ace5e5dc..7aae8eb29d82a 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -27,6 +27,9 @@ #include "avio_internal.h" #include "avio.h" #include "avc.h" +#include "av1.h" +#include "vpcc.h" +#include "hevc.h" #include "avformat.h" #include "flv.h" #include "internal.h" @@ -46,6 +49,9 @@ static const AVCodecTag flv_video_codec_ids[] = { { AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, { AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, { AV_CODEC_ID_H264, FLV_CODECID_H264 }, + { AV_CODEC_ID_HEVC, MKBETAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_AV1, MKBETAG('a', 'v', '0', '1') }, + { AV_CODEC_ID_VP9, MKBETAG('v', 'p', '0', '9') }, { AV_CODEC_ID_NONE, 0 } }; @@ -491,7 +497,8 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i FLVContext *flv = s->priv_data; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { int64_t pos; avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ? @@ -534,10 +541,26 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i } avio_write(pb, par->extradata, par->extradata_size); } else { - avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags - avio_w8(pb, 0); // AVC sequence header - avio_wb24(pb, 0); // composition time - ff_isom_write_avcc(pb, par->extradata, par->extradata_size); + if (par->codec_id == AV_CODEC_ID_HEVC) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); // ExVideoTagHeader mode with PacketTypeSequenceStart + avio_write(pb, "hvc1", 4); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags + avio_w8(pb, 0); // AVC sequence header + avio_wb24(pb, 0); // composition time + } + + if (par->codec_id == AV_CODEC_ID_HEVC) + ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0); + else if (par->codec_id == AV_CODEC_ID_AV1) + ff_isom_write_av1c(pb, par->extradata, par->extradata_size, 1); + else if (par->codec_id == AV_CODEC_ID_VP9) + ff_isom_write_vpcc(s, pb, par->extradata, par->extradata_size, par); + else + ff_isom_write_avcc(pb, par->extradata, par->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); @@ -884,6 +907,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) unsigned ts; int size = pkt->size; uint8_t *data = NULL; + uint8_t frametype = pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; int flags = -1, flags_size, ret = 0; int64_t cur_offset = avio_tell(pb); @@ -895,14 +919,17 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) flags_size = 2; - else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) + else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) flags_size = 5; else flags_size = 1; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { - buffer_size_t side_size; + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + size_t side_size; uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { ret = ff_alloc_extradata(par, side_size); @@ -921,7 +948,9 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } - if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { + if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) { if (pkt->pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Packet is missing PTS\n"); return AVERROR(EINVAL); @@ -944,7 +973,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) flags = ff_codec_get_tag(flv_video_codec_ids, par->codec_id); - flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; + flags |= frametype; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, par); @@ -966,6 +995,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) return ret; + } else if (par->codec_id == AV_CODEC_ID_HEVC) { + if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) + if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL)) < 0) + return ret; } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { if (!s->streams[pkt->stream_index]->nb_frames) { @@ -1027,7 +1060,15 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) avio_wb32(pb, data_size + 11); } else { av_assert1(flags>=0); - avio_w8(pb,flags); + if (par->codec_id == AV_CODEC_ID_HEVC) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeCodedFramesX | frametype); // ExVideoTagHeader mode with PacketTypeCodedFramesX + avio_write(pb, "hvc1", 4); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeCodedFrames | frametype); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, flags); + } if (par->codec_id == AV_CODEC_ID_VP6) avio_w8(pb,0); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) { diff --git a/libavformat/http.c b/libavformat/http.c index 3055bcf01ad26..fb2d9306bd2f7 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -48,10 +48,6 @@ * path names). */ #define BUFFER_SIZE (MAX_URL_SIZE + HTTP_HEADERS_SIZE) #define MAX_REDIRECTS 8 -#include "libavutil/time.h" -#define MAX_PARA_SIZE 256 -#define MAX_REDIRECT_BUFFER 2048 -#define MAX_X_KS_HEADER_COUNT 10 #define HTTP_SINGLE 1 #define HTTP_MUTLI 2 #define MAX_EXPIRY 19 @@ -319,7 +315,6 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) off = s->off; location_changed = http_open_cnx_internal(h, options); - if (location_changed < 0) { if (!http_should_reconnect(s, location_changed) || reconnect_delay > s->reconnect_delay_max) @@ -342,7 +337,6 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) if (s->http_code == 401) { if ((cur_auth_type == HTTP_AUTH_NONE || s->auth_state.stale) && s->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_closep(&s->hd); goto redo; } else @@ -351,7 +345,6 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) if (s->http_code == 407) { if ((cur_proxy_auth_type == HTTP_AUTH_NONE || s->proxy_auth_state.stale) && s->proxy_auth_state.auth_type != HTTP_AUTH_NONE && attempts < 4) { - ffurl_closep(&s->hd); goto redo; } else @@ -361,7 +354,6 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) s->http_code == 303 || s->http_code == 307 || s->http_code == 308) && location_changed == 1) { /* url moved, get next */ - ffurl_closep(&s->hd); if (redirects++ >= MAX_REDIRECTS) return AVERROR(EIO); @@ -372,7 +364,6 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) location_changed = 0; goto redo; } - return 0; fail: @@ -659,7 +650,6 @@ static int http_open(URLContext *h, const char *uri, int flags, bail_out: if (ret < 0) av_dict_free(&s->chained_options); - return ret; } @@ -1060,7 +1050,6 @@ static int process_line(URLContext *h, char *line, int line_count, return ret; } } else { - while (*p != '\0' && *p != ':') p++; if (*p != ':') @@ -1117,7 +1106,7 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp(tag, "Content-Encoding")) { if ((ret = parse_content_encoding(h, p)) < 0) return ret; - } + } } return 1; } @@ -1701,7 +1690,6 @@ static int http_read(URLContext *h, uint8_t *buf, int size) size = http_read_stream(h, buf, size); if (size > 0) s->icy_data_read += size; - return size; } @@ -1778,7 +1766,6 @@ static int http_close(URLContext *h) if (s->hd) ffurl_closep(&s->hd); av_dict_free(&s->chained_options); - return ret; } @@ -1851,12 +1838,6 @@ static int http_get_file_handle(URLContext *h) return ffurl_get_file_handle(s->hd); } -URLContext* qyhttp_get_tcpstream(URLContext* httpCtx) -{ - HTTPContext *http = (HTTPContext*) httpCtx->priv_data; - return http->hd; -} - static int http_get_short_seek(URLContext *h) { HTTPContext *s = h->priv_data; diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index b4284a87785d8..383b9322ab019 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -729,7 +729,7 @@ static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb, case AV_CODEC_ID_AV1: if (par->extradata_size) return ff_isom_write_av1c(dyn_cp, par->extradata, - par->extradata_size); + par->extradata_size,1); else put_ebml_void(pb, 4 + 3); break; @@ -2267,7 +2267,7 @@ static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt) ret = avio_open_dyn_buf(&dyn_cp); if (ret < 0) return ret; - ff_isom_write_av1c(dyn_cp, side_data, side_data_size); + ff_isom_write_av1c(dyn_cp, side_data, side_data_size, 1); codecpriv_size = avio_get_dyn_buf(dyn_cp, &codecpriv); if ((ret = dyn_cp->error) < 0 || !codecpriv_size && (ret = AVERROR_INVALIDDATA)) { diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 8a06de2fd2944..30d6a9057bb17 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1298,7 +1298,7 @@ static int mov_write_av1c_tag(AVIOContext *pb, MOVTrack *track) avio_wb32(pb, 0); ffio_wfourcc(pb, "av1C"); - ff_isom_write_av1c(pb, track->vos_data, track->vos_len); + ff_isom_write_av1c(pb, track->vos_data, track->vos_len, 1); return update_size(pb, pos); } @@ -1318,9 +1318,7 @@ static int mov_write_vpcc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra avio_wb32(pb, 0); ffio_wfourcc(pb, "vpcC"); - avio_w8(pb, 1); /* version */ - avio_wb24(pb, 0); /* flags */ - ff_isom_write_vpcc(s, pb, track->par); + ff_isom_write_vpcc(s, pb, track->vos_data, track->vos_len, track->par); return update_size(pb, pos); } @@ -5650,7 +5648,9 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_TRUEHD || - par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len && + par->codec_id == AV_CODEC_ID_AC3 || + par->codec_id == AV_CODEC_ID_VP9 || + par->codec_id == AV_CODEC_ID_TRUEHD) && !trk->vos_len && !TAG_IS_AVCI(trk->tag)) { /* copy frame to create needed atoms */ trk->vos_len = size; diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c index 00eb0873b24b7..ef0d0556e76dd 100644 --- a/libavformat/rtmppkt.c +++ b/libavformat/rtmppkt.c @@ -40,6 +40,12 @@ void ff_amf_write_number(uint8_t **dst, double val) bytestream_put_be64(dst, av_double2int(val)); } +void ff_amf_write_array_start(uint8_t **dst, uint32_t length) +{ + bytestream_put_byte(dst, AMF_DATA_TYPE_ARRAY); + bytestream_put_be32(dst, length); +} + void ff_amf_write_string(uint8_t **dst, const char *str) { bytestream_put_byte(dst, AMF_DATA_TYPE_STRING); diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h index a15d2a57732bc..7c580f2224dd9 100644 --- a/libavformat/rtmppkt.h +++ b/libavformat/rtmppkt.h @@ -244,6 +244,14 @@ void ff_amf_write_null(uint8_t **dst); */ void ff_amf_write_object_start(uint8_t **dst); +/** + * Write marker and length for AMF array to buffer. + * + * @param dst pointer to the input buffer (will be modified) + * @param length value to write + */ +void ff_amf_write_array_start(uint8_t **dst, uint32_t length); + /** * Write string used as field name in AMF object to buffer. * diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index a7667580b327a..5a540e32404e2 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -128,7 +128,6 @@ typedef struct RTMPContext { char auth_params[500]; int do_reconnect; int auth_tried; - char* hostname; } RTMPContext; #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing @@ -2142,12 +2141,14 @@ static int handle_invoke_status(URLContext *s, RTMPPacket *pkt) av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr); return -1; } + t = ff_amf_get_field_value(ptr, data_end, "code", tmpstr, sizeof(tmpstr)); if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING; if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED; if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED; if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING; if (!t && !strcmp(tmpstr, "NetStream.Seek.Notify")) rt->state = STATE_PLAYING; + return 0; } @@ -2492,6 +2493,7 @@ static int rtmp_close(URLContext *h) { RTMPContext *rt = h->priv_data; int ret = 0, i, j; + if (!rt->is_input) { rt->flv_data = NULL; if (rt->out_pkt.size) @@ -2509,7 +2511,6 @@ static int rtmp_close(URLContext *h) free_tracked_methods(rt); av_freep(&rt->flv_data); - av_freep(&rt->hostname); ffurl_closep(&rt->stream); return ret; } @@ -2595,6 +2596,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o uint8_t buf[2048]; int port; int ret; + if (rt->listen_timeout > 0) rt->listen = 1; @@ -2779,16 +2781,6 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o port, "/%s", rt->app); } - if (!rt->hostname) { - rt->hostname = av_malloc(TCURL_MAX_LENGTH); - memset(rt->hostname, 0, TCURL_MAX_LENGTH); - if (!rt->hostname) { - ret = AVERROR(ENOMEM); - goto fail; - } - memcpy(rt->hostname, hostname, strlen(hostname)); - } - if (!rt->flashver) { rt->flashver = av_malloc(FLASHVER_MAX_LENGTH); if (!rt->flashver) { @@ -2814,8 +2806,8 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o rt->max_sent_unacked = 2500000; rt->duration = 0; - av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s, hostname = %s", - proto, path, rt->app, rt->playpath, rt->hostname); + av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n", + proto, path, rt->app, rt->playpath); if (!rt->listen) { if ((ret = gen_connect(s, rt)) < 0) goto fail; @@ -2967,6 +2959,7 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size) const uint8_t *buf_temp = buf; uint8_t c; int ret; + do { if (rt->skip_bytes) { int skip = FFMIN(rt->skip_bytes, size_temp); @@ -3098,21 +3091,6 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size) return size; } -URLContext* qyrtmp_get_tcpstream(URLContext* rtmpCtx ) { - RTMPContext * s = (RTMPContext*)rtmpCtx->priv_data; - return s->stream; -} - -char* qyrtmp_get_domain(URLContext* rtmpCtx ) { - RTMPContext * s = (RTMPContext*)rtmpCtx->priv_data; - return s->hostname; -} - -char* qyrtmp_get_stream_id(URLContext* rtmpCtx ) { - RTMPContext * s = (RTMPContext*)rtmpCtx->priv_data; - return s->playpath; -} - #define OFFSET(x) offsetof(RTMPContext, x) #define DEC AV_OPT_FLAG_DECODING_PARAM #define ENC AV_OPT_FLAG_ENCODING_PARAM diff --git a/libavformat/tcp.c b/libavformat/tcp.c index a20a0b77fd429..2198e0f00e28f 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -41,9 +41,6 @@ typedef struct TCPContext { int listen_timeout; int recv_buffer_size; int send_buffer_size; - long long read_bytes; - long long write_bytes; - char cdn_ip[INET6_ADDRSTRLEN]; int tcp_nodelay; #if !HAVE_WINSOCK2_H int tcp_mss; @@ -56,8 +53,7 @@ typedef struct TCPContext { static const AVOption options[] = { { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .flags = D|E }, { "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, - { "listen_timeout", "Listen awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, - { "open_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX, .flags = D|E }, + { "listen_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "tcp_nodelay", "Use TCP_NODELAY to disable nagle's algorithm", OFFSET(tcp_nodelay), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E }, @@ -104,7 +100,7 @@ static void customize_fd(void *ctx, int fd) } /* return non zero if error */ -static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **options) +static int tcp_open(URLContext *h, const char *uri, int flags) { struct addrinfo hints = { 0 }, *ai, *cur_ai; int port, fd = -1; @@ -114,12 +110,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **op int ret; char hostname[1024],proto[1024],path[1024]; char portstr[10]; - s->write_bytes = 0; - s->read_bytes = 0; - - int64_t dns_start_time; - int64_t connect_start_time = av_gettime(); - h->connect_time = -1; + s->open_timeout = 5000000; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); @@ -146,6 +137,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **op } } if (s->rw_timeout >= 0) { + s->open_timeout = h->rw_timeout = s->rw_timeout; } hints.ai_family = AF_UNSPEC; @@ -153,10 +145,6 @@ static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **op snprintf(portstr, sizeof(portstr), "%d", port); if (s->listen) hints.ai_flags |= AI_PASSIVE; - - dns_start_time = av_gettime(); - h->analyze_dns_time = -1; - if (!hostname[0]) ret = getaddrinfo(NULL, portstr, &hints, &ai); else @@ -168,30 +156,8 @@ static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **op return AVERROR(EIO); } - h->analyze_dns_time = (av_gettime() - dns_start_time) / 1000; - cur_ai = ai; - connect_start_time = av_gettime(); - - // Update server_ip on each restart - if(cur_ai != NULL) - { - char abuf[INET6_ADDRSTRLEN]; - const char* addrQy = NULL; - if (cur_ai->ai_family == AF_INET6) { - struct sockaddr_in6 *sinp = (struct sockaddr_in6 *)cur_ai->ai_addr; - addrQy = inet_ntop(AF_INET6, &sinp->sin6_addr, abuf, INET6_ADDRSTRLEN); - } else if (cur_ai->ai_family == AF_INET) { - struct sockaddr_in *sinp = (struct sockaddr_in *)cur_ai->ai_addr; - addrQy = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET6_ADDRSTRLEN); - } - if(addrQy != NULL) { - strcpy(s->cdn_ip, addrQy); - av_dict_set(options, "server_ip", addrQy, 0); - } - } - #if HAVE_STRUCT_SOCKADDR_IN6 // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number. if (cur_ai->ai_family == AF_INET6){ @@ -234,7 +200,6 @@ static int tcp_open(URLContext *h, const char *uri, int flags, AVDictionary **op goto fail1; } - h->connect_time = (av_gettime() - connect_start_time) / 1000; h->is_streamed = 1; s->fd = fd; @@ -277,7 +242,6 @@ static int tcp_read(URLContext *h, uint8_t *buf, int size) return ret; } ret = recv(s->fd, buf, size, 0); - s->read_bytes += (ret < 0) ? 0 : ret; if (ret == 0) return AVERROR_EOF; return ret < 0 ? ff_neterrno() : ret; @@ -294,7 +258,6 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) return ret; } ret = send(s->fd, buf, size, MSG_NOSIGNAL); - s->write_bytes += (ret < 0) ? 0 : ret; return ret < 0 ? ff_neterrno() : ret; } @@ -327,28 +290,6 @@ static int tcp_get_file_handle(URLContext *h) return s->fd; } -long long ff_qytcp_get_read_bytes( URLContext *h) { - TCPContext *s = h->priv_data; - if ( s ) { - return s->read_bytes; - } - return 0; -} -long long ff_qytcp_get_write_bytes( URLContext *h) { - TCPContext *s = h->priv_data; - if ( s ) { - return s->write_bytes; - } - return 0; -} -char* ff_qytcp_get_ip(URLContext *h) { - TCPContext *s = h->priv_data; - if(s) - return s->cdn_ip; - - return NULL; -} - static int tcp_get_window_size(URLContext *h) { TCPContext *s = h->priv_data; @@ -371,7 +312,7 @@ static int tcp_get_window_size(URLContext *h) const URLProtocol ff_tcp_protocol = { .name = "tcp", - .url_open2 = tcp_open, + .url_open = tcp_open, .url_accept = tcp_accept, .url_read = tcp_read, .url_write = tcp_write, diff --git a/libavformat/url.h b/libavformat/url.h index a8d22159de35f..3bb1cf89f7689 100644 --- a/libavformat/url.h +++ b/libavformat/url.h @@ -49,8 +49,6 @@ typedef struct URLContext { const char *protocol_whitelist; const char *protocol_blacklist; int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */ - int64_t connect_time; - int64_t analyze_dns_time; } URLContext; typedef struct URLProtocol { @@ -394,14 +392,4 @@ typedef struct URLComponents { */ int ff_url_decompose(URLComponents *uc, const char *url, const char *end); - -URLContext* qyrtmp_get_tcpstream(URLContext* rtmpCtx ); -URLContext* qyhttp_get_tcpstream(URLContext* httpCtx ); - -long long ff_qytcp_get_read_bytes( URLContext *h) ; -long long ff_qytcp_get_write_bytes( URLContext *h); -char* ff_qytcp_get_ip( URLContext *h ); -char* qyrtmp_get_domain( URLContext *h ); -char* qyrtmp_get_stream_id( URLContext *h ); - #endif /* AVFORMAT_URL_H */ diff --git a/libavformat/vpcc.c b/libavformat/vpcc.c index e0b7f288a6555..256407dd6ddb1 100644 --- a/libavformat/vpcc.c +++ b/libavformat/vpcc.c @@ -21,8 +21,12 @@ #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/get_bits.h" #include "vpcc.h" +#define VP9_SYNCCODE 0x498342 + enum VPX_CHROMA_SUBSAMPLING { VPX_SUBSAMPLING_420_VERTICAL = 0, @@ -113,7 +117,41 @@ static int get_vp9_level(AVCodecParameters *par, AVRational *frame_rate) { } } +static void parse_bitstream(GetBitContext *gb, int *profile, int *bit_depth) { + int keyframe, invisible; + + if (get_bits(gb, 2) != 0x2) // frame marker + return; + *profile = get_bits1(gb); + *profile |= get_bits1(gb) << 1; + if (*profile == 3) + *profile += get_bits1(gb); + + if (get_bits1(gb)) + return; + + keyframe = !get_bits1(gb); + invisible = !get_bits1(gb); + get_bits1(gb); + + if (keyframe) { + if (get_bits(gb, 24) != VP9_SYNCCODE) + return; + } else { + int intraonly = invisible ? get_bits1(gb) : 0; + if (!intraonly || get_bits(gb, 24) != VP9_SYNCCODE) + return; + if (*profile < 1) { + *bit_depth = 8; + return; + } + } + + *bit_depth = *profile <= 1 ? 8 : 10 + get_bits1(gb) * 2; +} + int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, + const uint8_t *data, int len, AVRational *frame_rate, VPCC *vpcc) { int profile = par->profile; @@ -128,7 +166,17 @@ int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, if (bit_depth < 0 || vpx_chroma_subsampling < 0) return AVERROR_INVALIDDATA; - if (profile == FF_PROFILE_UNKNOWN) { + if (len && (profile == FF_PROFILE_UNKNOWN || !bit_depth)) { + GetBitContext gb; + + int ret = init_get_bits8(&gb, data, len); + if (ret < 0) + return ret; + + parse_bitstream(&gb, &profile, &bit_depth); + } + + if (profile == FF_PROFILE_UNKNOWN && bit_depth) { if (vpx_chroma_subsampling == VPX_SUBSAMPLING_420_VERTICAL || vpx_chroma_subsampling == VPX_SUBSAMPLING_420_COLLOCATED_WITH_LUMA) { profile = (bit_depth == 8) ? FF_PROFILE_VP9_0 : FF_PROFILE_VP9_2; @@ -137,6 +185,9 @@ int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, } } + if (profile == FF_PROFILE_UNKNOWN || !bit_depth) + av_log(s, AV_LOG_WARNING, "VP9 profile and/or bit depth not set or could not be derived\n"); + vpcc->profile = profile; vpcc->level = level; vpcc->bitdepth = bit_depth; @@ -147,15 +198,18 @@ int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, } int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, + const uint8_t *data, int len, AVCodecParameters *par) { VPCC vpcc; int ret; - ret = ff_isom_get_vpcc_features(s, par, NULL, &vpcc); + ret = ff_isom_get_vpcc_features(s, par, data, len, NULL, &vpcc); if (ret < 0) return ret; + avio_w8(pb, 1); /* version */ + avio_wb24(pb, 0); /* flags */ avio_w8(pb, vpcc.profile); avio_w8(pb, vpcc.level); avio_w8(pb, (vpcc.bitdepth << 4) | (vpcc.chroma_subsampling << 1) | vpcc.full_range_flag); diff --git a/libavformat/vpcc.h b/libavformat/vpcc.h index e87bec55c26b5..e570888df4b04 100644 --- a/libavformat/vpcc.h +++ b/libavformat/vpcc.h @@ -45,14 +45,19 @@ typedef struct VPCC { * * @param s address of the AVFormatContext for the logging context. * @param pb address of the AVIOContext where the vpcC shall be written. + * @param data address of a data array which contains coded bitstream data from + * which codec information can be extracted. May be NULL. + * @param len length of the data array. * @param par address of the AVCodecParameters which contains codec information. * @return >=0 in case of success, a negative value corresponding to an AVERROR * code in case of failure */ int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, + const uint8_t *data, int len, AVCodecParameters *par); int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, + const uint8_t *data, int len, AVRational *frame_rate, VPCC *vpcc); #endif /* AVFORMAT_VPCC_H */