From patchwork Fri Feb 2 02:43:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 7478 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.156.27 with SMTP id q27csp318489jak; Thu, 1 Feb 2018 18:43:09 -0800 (PST) X-Google-Smtp-Source: AH8x225nLM6SK7XkHU+hpBNZZ0x5qxWnueC7PanuwARJdtq9KTPXmy57b3u7Dt0xh6pWoyxQtnHG X-Received: by 10.223.129.67 with SMTP id 61mr31051459wrm.271.1517539389708; Thu, 01 Feb 2018 18:43:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517539389; cv=none; d=google.com; s=arc-20160816; b=z6N3WnqQLd13NALGKTKOp/SLvkuxP4rR9qEv2WrpmoB+sjpunAQHB0GKKVyAP/22bK P76A4+5IlFBPpp9EZyQbBr6w0gTOdK+hWvllL2+5KhQJ6fJq71l7lAC9Mo8zrf0/ikRc 2btOsGAse2bwYAzHNH7DG4oFaeoK7s2mhb6Ejg7MQe7Ki5w1YUW/d8Xi0u5dqjOdnaLt AfkFNIYMjnav7VqGo6NFWBoEyilcK5WhmsKxHxehH5deHR0SZpdZpbQ7BVv+JGjjNeWy AiZ8g1n+cGqYWwg4oTn7zwE0gt7XKdYWhtrHnsP2OgrmIGXqgWUdXWuo/tENycq3DxXn n4GQ== 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:from :references:to:dkim-signature:delivered-to :arc-authentication-results; bh=a1B9DyTTTX78sQBaGMLVB+xr4tp6nO5KgxxxlJpLZuE=; b=MDNP185WaGC7muQ+KOsG4W+2SqQnVylCmIhruNqQuUQBNozSYwBaMzNx9RtINrqIvV vMpqot+sa0arHNqpgkigmTNMsPpYgcsNBZPoLAmAMKnTQ0nkU1uzuVihgA8/5kHE4Gny rw09q+9BVDYTjnG5ENPUelGtrsJqDFHlTLTIRRvNzxy/mzm4HUktEtnN33kUgi5TCPJf ZkqCimHdnQgsCHCTQqBabCGgEn5qAo1cIsyUYmiBLBRLWBBf2XaZdCpaVJMOpOGyUFCk SDcEY7RnU6LXJYSHdE4IubV4CupCferRgZ4Xe69775XBqCkwkFjlNkgWMNrcUaEtJYJn RMQA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=QO753eDk; 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 g85si437276wmc.109.2018.02.01.18.43.09; Thu, 01 Feb 2018 18:43:09 -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=QO753eDk; 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 9F0926891D7; Fri, 2 Feb 2018 04:42:58 +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 F3CBD68833C for ; Fri, 2 Feb 2018 04:42:51 +0200 (EET) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout01.mail.de (Postfix) with ESMTP id F03F14FA83 for ; Fri, 2 Feb 2018 03:42:57 +0100 (CET) Received: from smtp03.mail.de (smtp03.bt.mail.de [10.0.121.213]) by postfix03.mail.de (Postfix) with ESMTP id DA63E40180 for ; Fri, 2 Feb 2018 03:42:57 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde201610; t=1517539377; bh=CvjMUrErDcFjgqSwuMLjJH7YTrZCZaFlbrfNK2ErPa4=; h=Subject:To:References:From:Date:In-Reply-To:From; b=QO753eDkLOk0TJRXLdNCOaUHVe87kvVXP85SjznmEwkJA7/zpX/29OQibChBQ0I3W F8KxZGlz8q6l62S//90ks6vpexVzjxTdUekkEPCs0Dax0DIztv8lxvyin7WO5W59FI obSonNJr68S5wCzFGOlojLNwfIupZnCC0YbMRypo= 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 8CC0C8049B for ; Fri, 2 Feb 2018 03:42:57 +0100 (CET) To: ffmpeg-devel@ffmpeg.org References: <7225d2d3-f8a6-f89b-826f-6d4b9a0c49c0@mail.de> From: Thilo Borgmann Message-ID: Date: Fri, 2 Feb 2018 03:43:56 +0100 MIME-Version: 1.0 In-Reply-To: <7225d2d3-f8a6-f89b-826f-6d4b9a0c49c0@mail.de> Content-Language: en-US Subject: [FFmpeg-devel] [V2 PATCH 2/4] 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" Does not add a new demuxer anymore. For the muxer I'm not sure it should be done - would require more logic to check for valid codec ids for both cases I think (like for AAC audio). -Thilo From 1e4331416f6d3fb93215530759fe99745c4d91de Mon Sep 17 00:00:00 2001 From: Thilo Borgmann Date: Fri, 2 Feb 2018 02:49:22 +0100 Subject: [PATCH 2/4] Add muxing/demuxing of RMHD --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/rm.c | 1 + libavformat/rm.h | 7 + libavformat/rmdec.c | 50 ++++-- libavformat/rmenc.c | 397 +++++++++++++++++++++++++++++++++++++---------- libavformat/utils.c | 5 +- 7 files changed, 366 insertions(+), 96 deletions(-) diff --git a/libavformat/Makefile b/libavformat/Makefile index de0de92..1bbbe50 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -414,6 +414,7 @@ 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_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..589e756 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_MUXER (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 cf3d545..7004e64 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 @@ -437,7 +430,8 @@ skip: static int rm_read_index(AVFormatContext *s) { AVIOContext *pb = s->pb; - 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 { @@ -446,10 +440,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]; @@ -472,7 +466,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); @@ -553,6 +547,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]; @@ -565,7 +560,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); } @@ -577,7 +573,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')) @@ -593,7 +589,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 */ @@ -656,7 +652,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; @@ -664,7 +666,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; @@ -1093,10 +1099,22 @@ static int rm_probe(AVProbeData *p) (p->buf[0] == '.' && p->buf[1] == 'r' && p->buf[2] == 'a' && p->buf[3] == 0xfd)) return AVPROBE_SCORE_MAX; + else if (AV_RL32(&p->buf[0]) == MKTAG('.', 'R', 'M', 'P')) + return AVPROBE_SCORE_MAX; else return 0; } +static int rmhd_probe(AVProbeData *p) +{ + /* check file header */ + if (AV_RL32(&p->buf[0]) == MKTAG('.', 'R', 'M', '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) { diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c index 83b0f3c..8fb8980 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,18 @@ 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 mode; } RMMuxContext; /* in ms */ @@ -67,26 +73,38 @@ static void put_str8(AVIOContext *s, const char *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; StreamInfo *stream; const char *desc, *mimetype; - int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i; + int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i, j; int bit_rate, v, duration, flags; 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,7 +170,14 @@ 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"; @@ -156,6 +185,9 @@ static int rv10_write_header(AVFormatContext *ctx, 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; } @@ -190,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) { @@ -231,22 +272,50 @@ 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_wb16(s, 0x10); + avio_wb16(s, stream->par->channels); + avio_wb32(s, 0x76627266); + 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(j = 0; j< stream->par->extradata_size; j++) { + avio_w8(s, stream->par->extradata[j]); /* 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 */ @@ -264,6 +333,9 @@ static int rv10_write_header(AVFormatContext *ctx, 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); @@ -276,17 +348,44 @@ 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); + } 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 (j = 0; j< stream->par->extradata_size; j++) { + avio_w8(s, stream->par->extradata[j]); /* 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); + } + } } } @@ -299,19 +398,29 @@ 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; } static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream, - int length, int key_frame) + int length, int key_frame, AVPacket *pkt) { int timestamp; AVIOContext *s = ctx->pb; + RMMuxContext *rm = ctx->priv_data; stream->nb_packets++; stream->packet_total_size += length; @@ -321,7 +430,11 @@ static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream, avio_wb16(s,0); /* version */ avio_wb16(s,length + 12); avio_wb16(s, stream->num); /* stream number */ - timestamp = av_rescale_q_rnd(stream->nb_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO); + if(pkt && rm->mode & MODE_RMHD) { + timestamp = pkt->pts; + } else { + timestamp = av_rescale_q_rnd(stream->nb_frames, (AVRational){1000, 1}, stream->frame_rate, AV_ROUND_ZERO); + } avio_wb32(s, timestamp); /* timestamp */ avio_w8(s, 0); /* reserved */ avio_w8(s, key_frame ? 2 : 0); /* flags */ @@ -334,6 +447,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; @@ -351,6 +470,10 @@ static int rm_write_header(AVFormatContext *s) stream->bit_rate = par->bit_rate; stream->par = par; + if (rm->mode & MODE_RMHD) { + avpriv_set_pts_info(st, 32, 1, 1000); + } + switch (par->codec_type) { case AVMEDIA_TYPE_AUDIO: rm->audio_stream = stream; @@ -363,7 +486,8 @@ static int rm_write_header(AVFormatContext *s) break; case AVMEDIA_TYPE_VIDEO: rm->video_stream = stream; - if (st->codecpar->codec_id == AV_CODEC_ID_RV40) { + if (st->codecpar->codec_id == AV_CODEC_ID_RV40 || + st->codecpar->codec_id == AV_CODEC_ID_RV60) { stream->frame_rate = st->avg_frame_rate; } else { stream->frame_rate = av_inv_q(st->time_base); @@ -384,29 +508,37 @@ static int rm_write_header(AVFormatContext *s) return 0; } -static int rm_write_audio(AVFormatContext *s, const uint8_t *buf, int size, int flags) +static int rm_write_audio(AVFormatContext *s, const uint8_t *buf, + int size, int flags, AVPacket *pkt) { RMMuxContext *rm = s->priv_data; AVIOContext *pb = s->pb; 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), pkt); 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), pkt); + 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), pkt); avio_write(pb, buf, size); } stream->nb_frames++; return 0; } -static int rm_write_video(AVFormatContext *s, const uint8_t *buf, int size, int flags) +static int rm_write_video(AVFormatContext *s, const uint8_t *buf, + int size, int flags, AVPacket *pkt) { RMMuxContext *rm = s->priv_data; AVIOContext *pb = s->pb; @@ -415,32 +547,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, pkt); + 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, pkt); + // 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; @@ -450,23 +628,72 @@ static int rm_write_packet(AVFormatContext *s, AVPacket *pkt) { if (s->streams[pkt->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) - return rm_write_audio(s, pkt->data, pkt->size, pkt->flags); + return rm_write_audio(s, pkt->data, pkt->size, pkt->flags, pkt); else - return rm_write_video(s, pkt->data, pkt->size, pkt->flags); + return rm_write_video(s, pkt->data, pkt->size, pkt->flags, 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); @@ -499,3 +726,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 28ea071..f05c666 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -2953,9 +2953,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)