From patchwork Thu Jan 18 18:39:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 7331 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.78.2 with SMTP id r2csp7244220jaa; Thu, 18 Jan 2018 10:39:08 -0800 (PST) X-Google-Smtp-Source: ACJfBosHVFFpX6gnPWcmndENqDW1WLgxr3tDnopHECFs+WM0PKbfLo17Ht7hX5jtu4PwEYfSnBRC X-Received: by 10.223.162.193 with SMTP id t1mr6770352wra.87.1516300747992; Thu, 18 Jan 2018 10:39:07 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516300747; cv=none; d=google.com; s=arc-20160816; b=m0UOy01biT3tzr1pSEeundSDxr9SBLteLhd38oQdcsFJgw6GuQqBavlwEUTqmPX0Cg u28JaRr6OV9FUqVZ2CFQsBGLOr7thehr0jR6wWfFO+oP/fJ5CGIJ8fEKWeVXS2QTxv3Y Idkwe68KhjWWLBX9tA8IfdB9NnBGG20vwZt3cQMAtVzxp4rr0nWDxVrAP0zPdEBS44pQ 4nIU5K2Ewr8JSyePzd08zavcCo9Paphxl2iUyLAlevnMJLtI798TnSJmlRjo0OSc2FkF e2OiaHQp1PrjEje9CjkYKq0ImLTDYiGj9fmAxuikbVouwvwiLFXh9fSenkJZAj+TKzFN spyA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :content-language:in-reply-to:mime-version:date:message-id :references:to:from:dkim-signature:delivered-to :arc-authentication-results; bh=Yk1BIxNtlsU3DmNnUkWRCxzsf9TkRF4tXHfDoXgk5no=; b=wFsUD80iTXAuhYgwZhQ24dEncT8nbauxeTIe1xdyn47gZI29CBvuj4VHqactZgeqK+ YfabnTaSdab+TVxW0Ro1ZBqHShh+LKryG/Ly2DAjBZ/9PniUlN1Eog0fxmCc0LsDgVss X8MDkpK6mZYZPC+BYTcmX/aEdN80FuLJEwC0eOgzkZGcXTM8mqiR+vFwruAqfcPhDLWo rvwkWxn/57x6dCvau13Vh0oZfjNNOPGmHO6fNV5KzQFqSjxz0XkkqjrPQIKvERxI1MaP +PtnOs2BGPSsVobWM8NimsKyRMPyc1jaTRHJ+INkG8lWsB2zN0U0F6mAe7xWPTVXTMeN BANA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=S/PBGEw6; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a67si6026589wmi.79.2018.01.18.10.39.07; Thu, 18 Jan 2018 10:39:07 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=S/PBGEw6; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 58D9B68A2A7; Thu, 18 Jan 2018 20:39:06 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout01.mail.de (shout01.mail.de [213.128.151.216]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 22441688398 for ; Thu, 18 Jan 2018 20:39:00 +0200 (EET) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout01.mail.de (Postfix) with ESMTP id 39E4140F6E for ; Thu, 18 Jan 2018 19:39:00 +0100 (CET) Received: from smtp03.mail.de (smtp03.bt.mail.de [10.0.121.213]) by postfix03.mail.de (Postfix) with ESMTP id 224AA4009B for ; Thu, 18 Jan 2018 19:39:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde201610; t=1516300740; bh=qrMx+pi3ojqZH56BSrL8DWjDjaq/D2nJYWPCIMj80+s=; h=Subject:From:To:Reply-To:References:Date:In-Reply-To:From; b=S/PBGEw6yNbdWAUh6ujhKY06REwuhzS+bewVIlYLJg1wGxHoU7lNoHftZ+87M8/Wu QwZ3jF2ekDUZxH6eCuDfdLlUupqYcyI9OTyvurUlHUtzixRKHubhzho8xdWc6tQGRB tNMt3a4wPzPOoI/yBbxIEDSy4IHSoNhrixwtxGHk= Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp03.mail.de (Postfix) with ESMTPSA id C8D6E80174 for ; Thu, 18 Jan 2018 19:38:59 +0100 (CET) From: Thilo Borgmann To: FFmpeg development discussions and patches References: Message-ID: <2fee733d-30f0-f90c-694e-ae5e2f2bd8f9@mail.de> Date: Thu, 18 Jan 2018 19:39:07 +0100 MIME-Version: 1.0 In-Reply-To: Content-Language: en-US Subject: Re: [FFmpeg-devel] [PATCH 1/3] Add muxing/demuxing of RMHD X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This time including the patch... From aaeaf7721ba5e9afd79ee13dbb9cc7c3226027b3 Mon Sep 17 00:00:00 2001 From: Thilo Borgmann Date: Wed, 17 Jan 2018 23:09:03 +0100 Subject: [PATCH 1/3] Add muxing/demuxing of RMHD --- fftools/ffmpeg.c | 28 +++- fftools/ffmpeg_opt.c | 2 +- libavformat/Makefile | 2 + libavformat/allformats.c | 1 + libavformat/rm.c | 1 + libavformat/rm.h | 7 + libavformat/rmdec.c | 96 +++++++++--- libavformat/rmenc.c | 395 ++++++++++++++++++++++++++++++++++++++--------- libavformat/utils.c | 5 +- 9 files changed, 438 insertions(+), 99 deletions(-) diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 528849a..9bd5724 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -1296,7 +1296,18 @@ static void do_video_out(OutputFile *of, if (pkt.pts == AV_NOPTS_VALUE && !(enc->codec->capabilities & AV_CODEC_CAP_DELAY)) pkt.pts = ost->sync_opts; - av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase); + if(enc->codec->id == AV_CODEC_ID_RV60) { + AVRational rm_time_base = {1, 1000}; + if (pkt.pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->mux_timebase); + if (pkt.dts != AV_NOPTS_VALUE && av_cmp_q(rm_time_base, ost->mux_timebase)) { + pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->mux_timebase); + pkt.dts = av_rescale_q(pkt.dts, rm_time_base, enc->time_base); + } + pkt.pts = pkt.dts; + } else { + av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase); + } if (debug_ts) { av_log(NULL, AV_LOG_INFO, "encoder -> type:video " @@ -1940,7 +1951,20 @@ static void flush_encoders(void) av_packet_unref(&pkt); continue; } - av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase); + + if(enc->codec->id == AV_CODEC_ID_RV60) { + AVRational rm_time_base = {1, 1000}; + if (pkt.pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(pkt.pts, enc->time_base, ost->mux_timebase); + if (pkt.dts != AV_NOPTS_VALUE && av_cmp_q(rm_time_base, ost->mux_timebase)) { + pkt.dts = av_rescale_q(pkt.dts, enc->time_base, ost->mux_timebase); + pkt.dts = av_rescale_q(pkt.dts, rm_time_base, enc->time_base); + } + pkt.pts = pkt.dts; + } else { + av_packet_rescale_ts(&pkt, enc->time_base, ost->mux_timebase); + } + pkt_size = pkt.size; output_packet(of, &pkt, ost, 0); if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO && vstats_filename) { diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 92199b3..0036468 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1747,7 +1747,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in ost->logfile_prefix ? ost->logfile_prefix : DEFAULT_PASS_LOGFILENAME_PREFIX, i); - if (!strcmp(ost->enc->name, "libx264")) { + if (!strcmp(ost->enc->name, "libx264") || !strcmp(ost->enc->name, "librv11enc")) { av_dict_set(&ost->encoder_opts, "stats", logfilename, AV_DICT_DONT_OVERWRITE); } else { if (video_enc->flags & AV_CODEC_FLAG_PASS2) { diff --git a/libavformat/Makefile b/libavformat/Makefile index de0de92..3a8d0a2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -414,6 +414,8 @@ OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o OBJS-$(CONFIG_RL2_DEMUXER) += rl2.o OBJS-$(CONFIG_RM_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_RM_MUXER) += rmenc.o rm.o +OBJS-$(CONFIG_RMHD_DEMUXER) += rmdec.o rm.o rmsipr.o +OBJS-$(CONFIG_RMHD_MUXER) += rmenc.o rm.o OBJS-$(CONFIG_ROQ_DEMUXER) += idroqdec.o OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o OBJS-$(CONFIG_RSD_DEMUXER) += rsd.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index ec84096..141ccd2 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -266,6 +266,7 @@ static void register_all(void) REGISTER_DEMUXER (REDSPARK, redspark); REGISTER_DEMUXER (RL2, rl2); REGISTER_MUXDEMUX(RM, rm); + REGISTER_MUXDEMUX(RMHD, rmhd); REGISTER_MUXDEMUX(ROQ, roq); REGISTER_DEMUXER (RPL, rpl); REGISTER_DEMUXER (RSD, rsd); diff --git a/libavformat/rm.c b/libavformat/rm.c index 52c7ccc..a66c9b6 100644 --- a/libavformat/rm.c +++ b/libavformat/rm.c @@ -34,6 +34,7 @@ const AVCodecTag ff_rm_codec_tags[] = { { AV_CODEC_ID_RV20, MKTAG('R','V','T','R') }, { AV_CODEC_ID_RV30, MKTAG('R','V','3','0') }, { AV_CODEC_ID_RV40, MKTAG('R','V','4','0') }, + { AV_CODEC_ID_RV60, MKTAG('R','V','6','0') }, { AV_CODEC_ID_AC3, MKTAG('d','n','e','t') }, { AV_CODEC_ID_RA_144, MKTAG('l','p','c','J') }, { AV_CODEC_ID_RA_288, MKTAG('2','8','_','8') }, diff --git a/libavformat/rm.h b/libavformat/rm.h index 7b080e2..eeb9862 100644 --- a/libavformat/rm.h +++ b/libavformat/rm.h @@ -25,6 +25,13 @@ #include "avformat.h" #include "internal.h" +#define DEINT_ID_GENR MKTAG('g', 'e', 'n', 'r') ///< interleaving for Cooker/ATRAC +#define DEINT_ID_INT0 MKTAG('I', 'n', 't', '0') ///< no interleaving needed +#define DEINT_ID_INT4 MKTAG('I', 'n', 't', '4') ///< interleaving for 28.8 +#define DEINT_ID_SIPR MKTAG('s', 'i', 'p', 'r') ///< interleaving for Sipro +#define DEINT_ID_VBRF MKTAG('v', 'b', 'r', 'f') ///< VBR case for AAC +#define DEINT_ID_VBRS MKTAG('v', 'b', 'r', 's') ///< VBR case for AAC + extern const char * const ff_rm_metadata[4]; extern const AVCodecTag ff_rm_codec_tags[]; diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c index fea71a2..1709cd2 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -33,13 +33,6 @@ #include "rmsipr.h" #include "rm.h" -#define DEINT_ID_GENR MKTAG('g', 'e', 'n', 'r') ///< interleaving for Cooker/ATRAC -#define DEINT_ID_INT0 MKTAG('I', 'n', 't', '0') ///< no interleaving needed -#define DEINT_ID_INT4 MKTAG('I', 'n', 't', '4') ///< interleaving for 28.8 -#define DEINT_ID_SIPR MKTAG('s', 'i', 'p', 'r') ///< interleaving for Sipro -#define DEINT_ID_VBRF MKTAG('v', 'b', 'r', 'f') ///< VBR case for AAC -#define DEINT_ID_VBRS MKTAG('v', 'b', 'r', 's') ///< VBR case for AAC - struct RMStream { AVPacket pkt; ///< place to store merged video frame / reordered audio data int videobufsize; ///< current assembled frame size @@ -330,7 +323,19 @@ int ff_rm_read_mdpr_codecdata(AVFormatContext *s, AVIOContext *pb, codec_pos = avio_tell(pb); v = avio_rb32(pb); - if (v == MKTAG(0xfd, 'a', 'r', '.')) { + if (v == MKBETAG('M', 'L', 'T', 'I')) { + int number_of_streams = avio_rb16(pb); + int number_of_mdpr; + int i; + for (i = 0; ipb; - unsigned int size, n_pkts, str_id, next_off, n, pos, pts; + unsigned int size, n_pkts, str_id, n, pts, version; + uint64_t next_off, pos; AVStream *st; do { @@ -433,10 +439,10 @@ static int rm_read_index(AVFormatContext *s) size = avio_rb32(pb); if (size < 20) return -1; - avio_skip(pb, 2); + version = avio_rb16(pb); n_pkts = avio_rb32(pb); str_id = avio_rb16(pb); - next_off = avio_rb32(pb); + next_off = (version == 2) ? avio_rb64(pb) : avio_rb32(pb); for (n = 0; n < s->nb_streams; n++) if (s->streams[n]->id == str_id) { st = s->streams[n]; @@ -459,7 +465,7 @@ static int rm_read_index(AVFormatContext *s) for (n = 0; n < n_pkts; n++) { avio_skip(pb, 2); pts = avio_rb32(pb); - pos = avio_rb32(pb); + pos = (version == 2) ? avio_rb64(pb) : avio_rb32(pb); avio_skip(pb, 4); /* packet no. */ av_add_index_entry(st, pos, pts, 0, 0, AVINDEX_KEYFRAME); @@ -540,6 +546,7 @@ static int rm_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; unsigned int tag; int tag_size; + unsigned int version; unsigned int start_time, duration; unsigned int data_off = 0, indx_off = 0; char buf[128], mime[128]; @@ -552,7 +559,8 @@ static int rm_read_header(AVFormatContext *s) if (tag == MKTAG('.', 'r', 'a', 0xfd)) { /* very old .ra format */ return rm_read_header_old(s); - } else if (tag != MKTAG('.', 'R', 'M', 'F')) { + } else if (tag != MKTAG('.', 'R', 'M', 'F') && + (tag != MKTAG('.', 'R', 'M', 'P'))) { return AVERROR(EIO); } @@ -564,7 +572,7 @@ static int rm_read_header(AVFormatContext *s) goto fail; tag = avio_rl32(pb); tag_size = avio_rb32(pb); - avio_rb16(pb); + version = avio_rb16(pb); av_log(s, AV_LOG_TRACE, "tag=%s size=%d\n", av_fourcc2str(tag), tag_size); if (tag_size < 10 && tag != MKTAG('D', 'A', 'T', 'A')) @@ -580,7 +588,7 @@ static int rm_read_header(AVFormatContext *s) duration = avio_rb32(pb); /* duration */ s->duration = av_rescale(duration, AV_TIME_BASE, 1000); avio_rb32(pb); /* preroll */ - indx_off = avio_rb32(pb); /* index offset */ + indx_off = (version == 2) ? avio_rb64(pb) : avio_rb32(pb); /* index offset */ data_off = avio_rb32(pb); /* data offset */ avio_rb16(pb); /* nb streams */ flags = avio_rb16(pb); /* flags */ @@ -643,7 +651,13 @@ static int rm_read_header(AVFormatContext *s) rm->nb_packets = avio_rb32(pb); /* number of packets */ if (!rm->nb_packets && (flags & 4)) rm->nb_packets = 3600 * 25; - avio_rb32(pb); /* next data header */ + + if (version == 2) { + avio_rb64(pb); + avio_rb64(pb); /* updated size */ + } else { + avio_rb32(pb); /* next data header */ + } if (!data_off) data_off = avio_tell(pb) - 18; @@ -651,7 +665,11 @@ static int rm_read_header(AVFormatContext *s) !(s->flags & AVFMT_FLAG_IGNIDX) && avio_seek(pb, indx_off, SEEK_SET) >= 0) { rm_read_index(s); - avio_seek(pb, data_off + 18, SEEK_SET); + if (version == 2) { + avio_seek(pb, data_off + 30, SEEK_SET); + } else { + avio_seek(pb, data_off + 18, SEEK_SET); + } } return 0; @@ -1084,12 +1102,25 @@ static int rm_probe(AVProbeData *p) return 0; } +static int rmhd_probe(AVProbeData *p) +{ + /* check file header */ + if ((p->buf[0] == '.' && p->buf[1] == 'R' && + p->buf[2] == 'M' && p->buf[3] == 'P')) { + return AVPROBE_SCORE_MAX; + } else { + return 0; + } +} + static int64_t rm_read_dts(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { RMDemuxContext *rm = s->priv_data; int64_t pos, dts; + int64_t pos_audio, dts_audio; int stream_index2, flags, len, h; + int audio_override = 0; pos = *ppos; @@ -1114,20 +1145,32 @@ static int64_t rm_read_dts(AVFormatContext *s, int stream_index, if(!(h & 0x40)){ seq = avio_r8(s->pb); len--; } + if (!(flags & 2) || + (seq & 0x7F) != 1 || + stream_index2 != stream_index) { + audio_override = 0; + } } if((flags&2) && (seq&0x7F) == 1){ av_log(s, AV_LOG_TRACE, "%d %d-%d %"PRId64" %d\n", flags, stream_index2, stream_index, dts, seq); av_add_index_entry(st, pos, dts, 0, 0, AVINDEX_KEYFRAME); - if(stream_index2 == stream_index) + if(stream_index2 == stream_index) { + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + dts_audio = dts; + pos_audio = pos; + audio_override = 1; + } + break; + } } avio_skip(s->pb, len); } - *ppos = pos; - return dts; + *ppos = (audio_override ? pos_audio : pos); + return (audio_override ? dts_audio : dts); } static int rm_read_seek(AVFormatContext *s, int stream_index, @@ -1154,6 +1197,19 @@ AVInputFormat ff_rm_demuxer = { .read_seek = rm_read_seek, }; +AVInputFormat ff_rmhd_demuxer = { + .name = "rmhd", + .long_name = NULL_IF_CONFIG_SMALL("RealMedia HD"), + .priv_data_size = sizeof(RMDemuxContext), + .read_probe = rmhd_probe, + .read_header = rm_read_header, + .read_packet = rm_read_packet, + .read_close = rm_read_close, + .read_timestamp = rm_read_dts, + .read_seek = rm_read_seek, + .extensions = "rmhd", +}; + AVInputFormat ff_rdt_demuxer = { .name = "rdt", .long_name = NULL_IF_CONFIG_SMALL("RDT demuxer"), diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c index 3bff4da..6170bb2 100644 --- a/libavformat/rmenc.c +++ b/libavformat/rmenc.c @@ -22,6 +22,7 @@ #include "avio_internal.h" #include "rm.h" #include "libavutil/dict.h" +#include "libavutil/intreadwrite.h" typedef struct StreamInfo { int nb_packets; @@ -33,13 +34,19 @@ typedef struct StreamInfo { int nb_frames; /* current frame number */ int total_frames; /* total number of frames */ int num; + int32_t deint_id; AVCodecParameters *par; } StreamInfo; +#define MODE_RMVB 0x01 +#define MODE_RMHD 0x02 + typedef struct RMMuxContext { StreamInfo streams[2]; StreamInfo *audio_stream, *video_stream; int data_pos; /* position of the data after the header */ + int reset_timestamps; + int mode; } RMMuxContext; /* in ms */ @@ -65,9 +72,8 @@ static void put_str8(AVIOContext *s, const char *tag) avio_w8(s, *tag++); } } - static int rv10_write_header(AVFormatContext *ctx, - int data_size, int index_pos) + int64_t data_size, int64_t index_pos) { RMMuxContext *rm = ctx->priv_data; AVIOContext *s = ctx->pb; @@ -78,15 +84,27 @@ static int rv10_write_header(AVFormatContext *ctx, int data_offset; AVDictionaryEntry *tag; - ffio_wfourcc(s, ".RMF"); - avio_wb32(s,18); /* header size */ - avio_wb16(s,0); - avio_wb32(s,0); - avio_wb32(s,4 + ctx->nb_streams); /* num headers */ + // RMF represent rmvb, RMP represent rmhd + if(rm->mode & MODE_RMVB) { + ffio_wfourcc(s,".RMF"); + avio_wb32(s,18); /* header size */ + avio_wb16(s,0); /* object version */ + avio_wb32(s,0); /* file version */ + avio_wb32(s,4 + ctx->nb_streams); /* num headers */ + ffio_wfourcc(s,"PROP"); + avio_wb32(s, 50); + avio_wb16(s, 0); + } else { + ffio_wfourcc(s, ".RMP"); + avio_wb32(s,18); /* header size */ + avio_wb16(s,1); /* object version */ + avio_wb32(s,0); /* file version */ + avio_wb32(s,5 + ctx->nb_streams); /* num headers */ + ffio_wfourcc(s,"PROP"); + avio_wb32(s, 54); + avio_wb16(s, 2); + } - ffio_wfourcc(s,"PROP"); - avio_wb32(s, 50); - avio_wb16(s, 0); packet_max_size = 0; packet_total_size = 0; nb_packets = 0; @@ -115,7 +133,11 @@ static int rv10_write_header(AVFormatContext *ctx, avio_wb32(s, nb_packets); /* num packets */ avio_wb32(s, duration); /* duration */ avio_wb32(s, BUFFER_DURATION); /* preroll */ - avio_wb32(s, index_pos); /* index offset */ + if(rm->mode & MODE_RMVB) { + avio_wb32(s, index_pos); /* index offset */ + } else { + avio_wb64(s, index_pos); /* index offset */ + } /* computation of data the data offset */ data_offset = avio_tell(s); avio_wb32(s, 0); /* data offset : will be patched after */ @@ -148,11 +170,27 @@ static int rv10_write_header(AVFormatContext *ctx, if (stream->par->codec_type == AVMEDIA_TYPE_AUDIO) { desc = "The Audio Stream"; mimetype = "audio/x-pn-realaudio"; - codec_data_size = 73; + if (stream->par->codec_id == AV_CODEC_ID_AAC) { + codec_data_size = 78; + if(stream->par->extradata_size > 0){ + codec_data_size += stream->par->extradata_size + 1; + } + } else { + codec_data_size = 73; + } } else { desc = "The Video Stream"; mimetype = "video/x-pn-realvideo"; - codec_data_size = 34; + if(stream->par->codec_id == AV_CODEC_ID_RV10 || + stream->par->codec_id == AV_CODEC_ID_RV20 || + stream->par->codec_id == AV_CODEC_ID_RV40) { + codec_data_size = 34; + } else if(!stream->par->extradata_size && + stream->par->codec_id == AV_CODEC_ID_RV60) { + codec_data_size = 40; + } else { + codec_data_size = 26 + stream->par->extradata_size; + } } ffio_wfourcc(s,"MDPR"); @@ -184,16 +222,25 @@ static int rv10_write_header(AVFormatContext *ctx, if (stream->par->codec_type == AVMEDIA_TYPE_AUDIO) { int coded_frame_size, fscode, sample_rate; int frame_size = av_get_audio_frame_duration2(stream->par, 0); + int version = stream->par->codec_id == AV_CODEC_ID_AAC ? 5 : 4; sample_rate = stream->par->sample_rate; coded_frame_size = (stream->par->bit_rate * frame_size) / (8 * sample_rate); /* audio codec info */ avio_write(s, ".ra", 3); avio_w8(s, 0xfd); - avio_wb32(s, 0x00040000); /* version */ + if (version == 5) { + avio_wb32(s, 0x00050000); /* version */ + } else { + avio_wb32(s, 0x00040000); /* version */ + } ffio_wfourcc(s, ".ra4"); avio_wb32(s, 0x01b53530); /* stream length */ - avio_wb16(s, 4); /* unknown */ + if (version == 5) { /* write version */ + avio_wb16(s, 5); + } else { + avio_wb16(s, 4); + } avio_wb32(s, 0x39); /* header size */ switch(sample_rate) { @@ -225,30 +272,72 @@ static int rv10_write_header(AVFormatContext *ctx, avio_wb16(s, 0x01); /* frame length : seems to be very important */ avio_wb16(s, coded_frame_size); - avio_wb32(s, 0); /* unknown */ - avio_wb16(s, stream->par->sample_rate); /* sample rate */ - avio_wb32(s, 0x10); /* unknown */ - avio_wb16(s, stream->par->channels); - put_str8(s, "Int0"); /* codec name */ - if (stream->par->codec_tag) { - avio_w8(s, 4); /* tag length */ - avio_wl32(s, stream->par->codec_tag); + if (version == 5) { + avio_wb16(s, coded_frame_size); + avio_wb32(s, 0); + avio_wb32(s, stream->par->sample_rate<<16); /* sample rate */ + avio_wb32(s, stream->par->sample_rate<<16); /* sample rate */ + avio_wb32(s, 0x10); + avio_wb16(s, stream->par->channels); + avio_wb32(s, 0x76627266); /*frbv unknown */ + stream->deint_id = DEINT_ID_VBRF; + if (stream->par->codec_tag) { + avio_wl32(s, stream->par->codec_tag); + } else { + av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n"); + return AVERROR(EINVAL); + } + avio_wb16(s, 0x0100); /* title length set interleaved flag to 1*/ + avio_wb16(s, 0); /* author length */ + if (stream->par->extradata_size > 0) { + avio_wb32(s, stream->par->extradata_size + 1); /* tag length */ + avio_w8(s, 2); /* tag length */ + for(i = 0; i< stream->par->extradata_size; i++) { + avio_w8(s, stream->par->extradata[i]); /* end of header */ + } + } else { + avio_wb32(s, 0); + } } else { - av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n"); - return -1; + avio_wb32(s, 0); + avio_wb16(s, stream->par->sample_rate); /* sample rate */ + avio_wb32(s, 0x10); + avio_wb16(s, stream->par->channels); + put_str8(s, "Int0"); /* codec name */ + if (stream->par->codec_tag) { + avio_w8(s, 4); /* tag length */ + avio_wl32(s, stream->par->codec_tag); + } else { + av_log(ctx, AV_LOG_ERROR, "Invalid codec tag\n"); + return -1; + } + avio_wb16(s, 0); /* title length */ + avio_wb16(s, 0); /* author length */ + avio_wb16(s, 0); /* copyright length */ + avio_w8(s, 0); /* end of header */ } - avio_wb16(s, 0); /* title length */ - avio_wb16(s, 0); /* author length */ - avio_wb16(s, 0); /* copyright length */ - avio_w8(s, 0); /* end of header */ } else { /* video codec info */ avio_wb32(s,34); /* size */ ffio_wfourcc(s, "VIDO"); - if(stream->par->codec_id == AV_CODEC_ID_RV10) + switch (stream->par->codec_id) { + case AV_CODEC_ID_RV10: ffio_wfourcc(s,"RV10"); - else + break; + case AV_CODEC_ID_RV20: ffio_wfourcc(s,"RV20"); + break; + case AV_CODEC_ID_RV30: + ffio_wfourcc(s,"RV30"); + break; + case AV_CODEC_ID_RV40: + ffio_wfourcc(s,"RV40"); + break; + case AV_CODEC_ID_RV60: + ffio_wfourcc(s,"RV60"); + break; + } + avio_wb16(s, stream->par->width); avio_wb16(s, stream->par->height); @@ -259,17 +348,45 @@ static int rv10_write_header(AVFormatContext *ctx, avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* frames per seconds ? */ avio_wb32(s,0); /* unknown meaning */ - avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* unknown meaning */ - avio_wb32(s,0); /* unknown meaning */ - avio_wb16(s, 8); /* unknown meaning */ - /* Seems to be the codec version: only use basic H.263. The next - versions seems to add a differential DC coding as in - MPEG... nothing new under the sun. */ - if(stream->par->codec_id == AV_CODEC_ID_RV10) - avio_wb32(s,0x10000000); - else - avio_wb32(s,0x20103001); - //avio_wb32(s,0x10003000); + + if(rm->mode & MODE_RMVB) { + avio_wb16(s, stream->frame_rate.num / stream->frame_rate.den); /* unknown meaning */ + avio_wb32(s,0); /* unknown meaning */ + avio_wb16(s, 8); /* unknown meaning */ + /* Seems to be the codec version: only use basic H.263. The next + versions seems to add a differential DC coding as in + MPEG... nothing new under the sun. */ + if(stream->par->codec_id == AV_CODEC_ID_RV10) + avio_wb32(s,0x10000000); + else + avio_wb32(s,0x20103001); + //avio_wb32(s,0x10003000); + } else { + avio_wb32(s, ((int)av_q2d(stream->frame_rate)*(1<<16))); /* unknown meaning */ + if (stream->par->codec_id == AV_CODEC_ID_RV10 || + stream->par->codec_id == AV_CODEC_ID_RV20 || + stream->par->codec_id == AV_CODEC_ID_RV40) { + avio_wb32(s, 8); /* unknown meaning */ + if (stream->par->codec_id == AV_CODEC_ID_RV10) { + avio_wb32(s,0x10000000); + } else if (stream->par->codec_id == AV_CODEC_ID_RV20) { + avio_wb32(s,0x20103001); + } else { // AV_CODEC_ID_RV40 + avio_wb32(s,0x40008000); + } + } else if (stream->par->extradata_size >0) { + for (i = 0; i< stream->par->extradata_size; i++) { + avio_w8(s, stream->par->extradata[i]); /* end of header */ + } + } else if (!stream->par->extradata_size && + stream->par->codec_id == AV_CODEC_ID_RV60) { + avio_wb32(s, 0x01081000); + avio_wb32(s, 0x40008000); + avio_wb16(s, 0x0); + avio_wb16(s, stream->par->width); + avio_wb16(s, stream->par->height); + } + } } } @@ -282,11 +399,20 @@ static int rv10_write_header(AVFormatContext *ctx, /* data stream */ ffio_wfourcc(s, "DATA"); - avio_wb32(s,data_size + 10 + 8); - avio_wb16(s,0); - avio_wb32(s, nb_packets); /* number of packets */ - avio_wb32(s,0); /* next data header */ + if (rm->mode & MODE_RMVB) { + avio_wb32(s, data_size + 10 + 8); + avio_wb16(s, 0); + avio_wb32(s, nb_packets); /* number of packets */ + avio_wb32(s, 0); /* next data header */ + } else { + avio_wb32(s, 0); + avio_wb16(s, 2); + avio_wb32(s, nb_packets); /* number of packets */ + avio_wb64(s, 0); /* next data header */ + avio_wb64(s, data_size + 30); /* updated_size */ + } + return 0; } @@ -317,6 +443,12 @@ static int rm_write_header(AVFormatContext *s) int n; AVCodecParameters *par; + rm->mode = MODE_RMVB; + + if (s->oformat && !strcmp("rmhd", s->oformat->name)) { + rm->mode = MODE_RMHD; + } + if (s->nb_streams > 2) { av_log(s, AV_LOG_ERROR, "At most 2 streams are currently supported for muxing in RM\n"); return AVERROR_PATCHWELCOME; @@ -371,15 +503,21 @@ static int rm_write_audio(AVFormatContext *s, const uint8_t *buf, int size, int StreamInfo *stream = rm->audio_stream; int i; - write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY)); - if (stream->par->codec_id == AV_CODEC_ID_AC3) { /* for AC-3, the words seem to be reversed */ + write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY)); for (i = 0; i < size; i += 2) { avio_w8(pb, buf[i + 1]); avio_w8(pb, buf[i]); } + } else if (stream->deint_id == DEINT_ID_VBRF && + stream->par->codec_id == AV_CODEC_ID_AAC) { + write_packet_header(s, stream, size + 4, !!(flags & AV_PKT_FLAG_KEY)); + avio_wb16(pb, 0x10); // total frame size + avio_wb16(pb, size & 0xFFFF); // total frame size + avio_write(pb, buf, size); } else { + write_packet_header(s, stream, size, !!(flags & AV_PKT_FLAG_KEY)); avio_write(pb, buf, size); } stream->nb_frames++; @@ -395,32 +533,78 @@ static int rm_write_video(AVFormatContext *s, const uint8_t *buf, int size, int /* XXX: this is incorrect: should be a parameter */ - /* Well, I spent some time finding the meaning of these bits. I am - not sure I understood everything, but it works !! */ - if (size > MAX_PACKET_SIZE) { - av_log(s, AV_LOG_ERROR, "Muxing packets larger than 64 kB (%d) is not supported\n", size); - return AVERROR_PATCHWELCOME; - } - write_packet_header(s, stream, size + 7 + (size >= 0x4000)*4, key_frame); - /* bit 7: '1' if final packet of a frame converted in several packets */ - avio_w8(pb, 0x81); - /* bit 7: '1' if I-frame. bits 6..0 : sequence number in current - frame starting from 1 */ - if (key_frame) { - avio_w8(pb, 0x81); - } else { - avio_w8(pb, 0x01); - } - if(size >= 0x4000){ - avio_wb32(pb, size); /* total frame size */ - avio_wb32(pb, size); /* offset from the start or the end */ + if (stream->par->codec_id == AV_CODEC_ID_RV30 || + stream->par->codec_id == AV_CODEC_ID_RV40 || + stream->par->codec_id == AV_CODEC_ID_RV60) { + const uint8_t* seg_buf = NULL; + int seg_offset = 0; + int segments = buf[0] + 1; + int total_size = size - (1 + segments * 8); + int seg_size; + uint8_t byte1; + uint8_t byte2; + int part_val; + int i; + + for (i = 0; i < segments; i++) { + seg_buf = buf + 1 + segments * 8 + AV_RL32(buf + 5 + i * 8); + if (segments - i > 1) { + seg_size = AV_RL32(buf + 13 + 8 * i) - AV_RL32(buf + 5 + 8 * i); + } else { + seg_size = buf + size - seg_buf; + } + + if ((i + 1) == segments) { + byte1 = 0x80 | (0x3F & (segments >> 1)); + part_val = seg_size; + } else { + byte1 = 0x3F & (segments >> 1); + part_val = seg_offset; + } + byte2 = (0x80 & (segments << 7)) | (0x7f & (i + 1)); + write_packet_header(s, stream, seg_size + 7 + (total_size >= 0x4000) * 2 + (part_val >= 0x4000) * 2, key_frame); + avio_w8(pb, byte1); + avio_w8(pb, byte2); + if (total_size >= 0x4000) { + avio_wb32(pb, total_size); // total frame size + } else { + avio_wb16(pb, 0x4000 | total_size); // total frame size + } + if (part_val >= 0x4000) { + avio_wb32(pb, part_val); // offset from the start or the end + } else { + avio_wb16(pb, 0x4000 | part_val); // offset from the start or the end + } + seg_offset += seg_size; + avio_w8(pb, stream->nb_frames & 0xff); + avio_write(pb, seg_buf, seg_size); + } }else{ - avio_wb16(pb, 0x4000 | size); /* total frame size */ - avio_wb16(pb, 0x4000 | size); /* offset from the start or the end */ - } - avio_w8(pb, stream->nb_frames & 0xff); + if (size > MAX_PACKET_SIZE) { + av_log(s, AV_LOG_ERROR, "Muxing packets larger than 64 kB (%d) is not supported\n", size); + return AVERROR_PATCHWELCOME; + } + write_packet_header(s, stream, size + 7 + (size >= 0x4000)*4, key_frame); + // bit 7: '1' if final packet of a frame converted in several packets + avio_w8(pb, 0x81); + // bit 7: '1' if I frame. bits 6..0 : sequence number in current + // frame starting from 1 + if (key_frame) { + avio_w8(pb, 0x81); + } else { + avio_w8(pb, 0x01); + } + if (size >= 0x4000) { + avio_wb32(pb, size); /* total frame size */ + avio_wb32(pb, size); /* offset from the start or the end */ + } else { + avio_wb16(pb, 0x4000 | size); /* total frame size */ + avio_wb16(pb, 0x4000 | size); /* offset from the start or the end */ + } + avio_w8(pb, stream->nb_frames & 0xff); - avio_write(pb, buf, size); + avio_write(pb, buf, size); + } stream->nb_frames++; return 0; @@ -438,15 +622,64 @@ static int rm_write_packet(AVFormatContext *s, AVPacket *pkt) static int rm_write_trailer(AVFormatContext *s) { RMMuxContext *rm = s->priv_data; - int data_size, index_pos, i; + int data_size, index_pos, i, j; AVIOContext *pb = s->pb; + AVIndexEntry *entries = NULL; + unsigned char *data_offset_ptr = NULL; + unsigned int index_data_pos = 0; + unsigned int timestamp, n_pkts; + int64_t offset; if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { /* end of file: finish to write header */ index_pos = avio_tell(pb); data_size = index_pos - rm->data_pos; - /* FIXME: write index */ + /* write index */ + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + entries = st->index_entries; + n_pkts = st->nb_index_entries; + if (n_pkts < 1) { + break; + } + if (data_offset_ptr) { + /* patch data offset field */ + index_data_pos = avio_tell(pb); + data_offset_ptr[0] = index_data_pos >> 24; + data_offset_ptr[1] = index_data_pos >> 16; + data_offset_ptr[2] = index_data_pos >> 8; + data_offset_ptr[3] = index_data_pos; + } + ffio_wfourcc(pb,"INDX"); + if (rm->mode & MODE_RMVB) { + avio_wb32(pb, n_pkts * 14 + 20); + } else { + avio_wb32(pb, n_pkts * 18 + 24); + } + avio_wb16(pb, 2); + avio_wb32(pb, n_pkts); + avio_wb16(pb, st->id); + data_offset_ptr = pb->buf_ptr; + if (rm->mode & MODE_RMVB) { + avio_wb32(pb,0); + } else { + avio_wb64(pb, 0); /* data offset gets written later */ + } + + for (j = 0; j < n_pkts; j++) { + timestamp = entries[j].timestamp; + offset = entries[j].pos; + avio_wb16(pb, 2); + avio_wb32(pb, timestamp); + if (rm->mode & MODE_RMVB) { + avio_wb32(pb, (int32_t)offset); + } else { + avio_wb64(pb, offset); + } + avio_wb32(pb, 0); + } + } /* undocumented end header */ avio_wb32(pb, 0); @@ -479,3 +712,17 @@ AVOutputFormat ff_rm_muxer = { .write_trailer = rm_write_trailer, .codec_tag = (const AVCodecTag* const []){ ff_rm_codec_tags, 0 }, }; + +AVOutputFormat ff_rmhd_muxer = { + .name = "rmhd", + .long_name = NULL_IF_CONFIG_SMALL("RealMedia HD"), + .mime_type = "application/vnd.rn-realmedia", + .extensions = "rmhd", + .priv_data_size = sizeof(RMMuxContext), + .audio_codec = AV_CODEC_ID_AAC, + .video_codec = AV_CODEC_ID_RV60, + .write_header = rm_write_header, + .write_packet = rm_write_packet, + .write_trailer = rm_write_trailer, + .codec_tag = (const AVCodecTag* const []){ ff_rm_codec_tags, 0 }, +}; diff --git a/libavformat/utils.c b/libavformat/utils.c index 3d73341..3d18f6b 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -2942,9 +2942,10 @@ static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) FAIL("unspecified size"); if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE) FAIL("unspecified pixel format"); - if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40) + if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40 || + st->codecpar->codec_id == AV_CODEC_ID_RV60) if (!st->sample_aspect_ratio.num && !st->codecpar->sample_aspect_ratio.num && !st->codec_info_nb_frames) - FAIL("no frame in rv30/40 and no sar"); + FAIL("no frame in rv30/40/60 and no sar"); break; case AVMEDIA_TYPE_SUBTITLE: if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)