From patchwork Mon Sep 19 13:25:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: erkki.seppala.ext@nokia.com X-Patchwork-Id: 643 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp1001599vsd; Mon, 19 Sep 2016 06:29:51 -0700 (PDT) X-Received: by 10.28.164.130 with SMTP id n124mr8792101wme.105.1474291791035; Mon, 19 Sep 2016 06:29:51 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 123si19166661wmt.39.2016.09.19.06.29.49; Mon, 19 Sep 2016 06:29:50 -0700 (PDT) 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=@nokia.onmicrosoft.com; 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 dis=NONE) header.from=nokia.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id BB2E568A110; Mon, 19 Sep 2016 16:26:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from EUR03-VE1-obe.outbound.protection.outlook.com (mail-eopbgr50115.outbound.protection.outlook.com [40.107.5.115]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 60E3168A05F for ; Mon, 19 Sep 2016 16:26:06 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nokia.onmicrosoft.com; s=selector1-nokia-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=EsT1gEsaBDk28esTStU1fobRsf8OzEqpeVi/SibjMps=; b=Q/hucq/o3LMa7xnpOY1zvh+xLEWNEbqu7kn6ElWYwwjx2k12RQ9pfvZC3YqRfC7hdQoMF2U2YFFYzo8n1mWWzr2OZDXYECXYf/jM2CdilLlfwsdSllmmlc9YBg3p+cKpcNb3X9KLWSiap0VBOmajScooV03vizuIua6fiVTgymc= Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=erkki.seppala.ext@nokia.com; Received: from erkkise-laptop.vincit.intranet (131.228.2.24) by AM5PR0701MB2529.eurprd07.prod.outlook.com (10.169.153.149) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.629.8; Mon, 19 Sep 2016 13:26:18 +0000 Received: by erkkise-laptop.vincit.intranet (Postfix, from userid 1000) id 0CDA04F0F6D; Mon, 19 Sep 2016 16:25:49 +0300 (EEST) From: =?UTF-8?q?Erkki=20Sepp=C3=A4l=C3=A4?= To: Date: Mon, 19 Sep 2016 16:25:48 +0300 Message-ID: <1474291548-17810-26-git-send-email-erkki.seppala.ext@nokia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1474291548-17810-1-git-send-email-erkki.seppala.ext@nokia.com> References: <1471943019-14136-1-git-send-email-erkki.seppala.ext@nokia.com> <1474291548-17810-1-git-send-email-erkki.seppala.ext@nokia.com> MIME-Version: 1.0 X-Originating-IP: [131.228.2.24] X-ClientProxiedBy: YQXPR01CA0052.CANPRD01.PROD.OUTLOOK.COM (10.165.103.20) To AM5PR0701MB2529.eurprd07.prod.outlook.com (10.169.153.149) X-MS-Office365-Filtering-Correlation-Id: 5ec010e6-cae7-481b-cdbe-08d3e0908a3d X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2529; 2:hJ7jz+WObDdXr8o6T/1X0aE0wrYmiauqHNpRwDCUQ6njuFmhZIqeEwDQBbS7h4SLBIE1AFb3sI4IQESclVDNhQSGILorK3jXTE+Yj561J2ljqzXMsFpVNVlOaH6RpwTWiQx0NCauL9XgC+3gRr/J5KZqU+piXAGuLKweBpiY04POv8H9bCjVWQtur8YG5wAD; 3:kTfJTOCJpgXeTmshwTonMsFJjlnTakelxtg43DFlmWghY9cBxlk4LSIE2trqTdb6QvCKlxH+QhM3RIoYdCwJbw2wjom2zTksY0p1JKuHTo7CUWGszrRCn+1lfijmQDzf; 25:0OSpQus6sB9pMdubaGOEdJtoi4EtPe2btaDLA53ip0T1JjKdvQ0ME1FZczV41jyXYR3JvIs1K4rCL5aoLGaLcwqP05cTuJgvwKzYr/qvJaE1Jfk98KdcZapIeFviv3Am9LXJO6zFQuLhM7+f67xbP85c/qIlkV0Alsm0RtImskvUkx5Z6Hx64O8L5SaBg0O8mck6Pc9Ao3+1R9RGT1JgvSCc+P1sOmi6NFz/9DlRuRdUyp5EoKXZz2O1105zJkfCt74Mrpo6IOZsJOHZEJPkDr823hSbOoahZpofYVb0St/KnMHtMZAUlGz9hZIZCjLAidpQuqA49SMMHMi7OWe+mi/uKsGi2znusmBOWNK759UAflyWo11Q56N81y7KqR/fgsl/4OZ1BRf33cZrX6ilgwP1LE1Gr31uU0vIJgie0CA= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:AM5PR0701MB2529; X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2529; 31:Cjnfl6HZ4Ya2j0v5kHLX9LA9Se7LFHKl4OrlxgD8rvEIijAi9J5bx3rQ8o6PyoY9vIyRIdyp0Xl9DtytKVq91gKrdaZ/tk6dEdq1r1ZT8O34s/JzJ+dbPQQtGRAzeYKRDon0Y9eCvd5uxlsMUCZ1/K6fKIxA6dZL4AKCofrfReCtoqGxwK6nTpWIy4JdpsnbtCQQcc8YE50F3BlM7TdaKCrxhI4wz7zVa9fCDCRr5RE=; 20:/OzFfO/6C3URBD4DfXujGGIdgGTQR6SEAYiiV43aTWmGu5maacV6iw4At7Iw1DjSeZaydLsUW3CczqcwOdTz//Z1mwD+aPOw6lUTyxCqqX0Rs3e42hvGjwL2ny2QlNU44OseF2KIMGjLzK0eXe3tUCt9Dcj0FW94Qbdq5JwAXSLwmHfDxVwg1nFsVg8pznm1yFRyoT948x6/TG3JWRUK8B9clR5hkBRJnxVVLJAEp3hGYLBDVDhw/Mfen76tckUtUoB7djjepIJEK09hen+M8IXFchTjn4QHrhvpNJIqFI03UzacZEIoej/VAuZgmZrOodM3+dPkQtHRDHGfoT5sU7TrycQ8WxqXCZQDr+25uHtQHw2WqKMBbbprbhQiU57fIyuRSA7bkV3U98aBUvGickNHxF+xPjkIxEfX/A7VsrPm5Lt00tpmGQypSJ1q9unoKICXu9/+nSQy6HlejZGNlj6Gf6/12BtEVTGW9psdLnx5Vb0la/YTTkeopDIlcv4B X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(82608151540597); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040176)(601004)(2401047)(8121501046)(5005006)(3002001)(10201501046)(6055026); SRVR:AM5PR0701MB2529; BCL:0; PCL:0; RULEID:; SRVR:AM5PR0701MB2529; X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2529; 4:1SWyXsrBFIu3pDVNjAwTGWf+lOrs+l2RQ85/PnW1qb+WL/552FV3QcZYMs6xUTuyazTP3HkW2PemyxIwA/+cgmKXG3gERbCpbQLg+WVKo+LbDghIC9c9fzEwA6YxFlEIX8E0bUScA59OXJC/I2v2GFrvJsIrgQuSt/fWfpTfpuAnuN/OnsocQ93SgL6HMOgBZu17B5wM6SH/WwkozO6JH2e9odblqUB+OGUo7eVIYMhgrOKl3wTPZ+XcjP7Bwoh9V+dldy0RtQC/dqCazw7I+lqOtq3tMnb7Pfy7bqcTO0CwYIRRIr/RZE5ya73ZhStnQJlve7fq7Zqeq4gUkE32+0dySuVdAO3CkmLGLZpYZ82HravPv/YtnGr4sAqKVBBzy9N4ozXuIb5nY+8SxMvcU6Xmtoc4i0NmY00DLJGMg/sz2efONPILqsvAQO+wfjgx X-Forefront-PRVS: 0070A8666B X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(7916002)(189002)(199003)(6116002)(7736002)(86362001)(305945005)(5820100001)(2906002)(8676002)(52956003)(81166006)(81156014)(4326007)(3846002)(229853001)(110136003)(50986999)(7846002)(2870700001)(92566002)(107886002)(66066001)(47776003)(97736004)(189998001)(42186005)(450100001)(2351001)(19580395003)(36756003)(106356001)(19580405001)(105586002)(50226002)(68736007)(5660300001)(50466002)(586003)(33646002)(101416001)(15975445007)(15395725005)(76176999)(45336002)(46386002)(2950100001)(122856001)(23676002)(4001430100002)(90966002); DIR:OUT; SFP:1102; SCL:1; SRVR:AM5PR0701MB2529; H:erkkise-laptop.vincit.intranet; FPR:; SPF:None; PTR:InfoNoRecords; MX:1; A:1; LANG:en; Received-SPF: None (protection.outlook.com: nokia.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtBTTVQUjA3MDFNQjI1Mjk7MjM6dmg0N3lYZ0MzanFWSHV0ZE84SGI0SjRU?= =?utf-8?B?dE84enRnWjZYWU55NFU0cktYaVF1SHZFeXZGb0J3Y3NVVktZVnVxZ0dKaE9I?= =?utf-8?B?WGw1bFRHSHZEUzE2dm4zaTBKeHFrQnFFYkt1ZVJ1L1NhVU56cW0va3lyejB2?= =?utf-8?B?cjhDSUxJU2JUUkI4U0FLc2NCNTkzWlplMkRaQlpWSHJLays4RW93N1lnNDMw?= =?utf-8?B?QjlEbTFEeTFCckM0SzNBM2N5YWZDVHA4aVI2SVF1bW1WbkYzU1BVSVozN0Vk?= =?utf-8?B?SWJRY0oyYU8rejM2OEE2WWlqRnlweGtGQU0zd3o1NStYNzZJQUVheGpLazc4?= =?utf-8?B?RlFmeWtlTk0zMUpFc2RYMjYyeWxhL09xdVB1NXBITjlwVHdUODRESDUxWkVj?= =?utf-8?B?L2ZUb2EyUVg5cVBHKzZsenE3VEJ2Q2xDNi9ubVR4cmxzZzJVVldIMGNTMi9Q?= =?utf-8?B?Z1U0eWZsZFZEY1A4blprUHRqdUwvQkExS3NrclYraTFwTVFjSy9DK0N0RHpp?= =?utf-8?B?aUgwaUYyOHpSV1JTQ1JTYTVHdlZTS0ExSiswRU9LSmp6VzhoK3FRUjdkRlU1?= =?utf-8?B?SlRHR2RDa2ZZSUJOMW9xa3Vac1hZZVpGV0ZTazFqamM2U2NjcE94NEdkaDFS?= =?utf-8?B?Y0Z1WFNPMkFCR2kvTTJCYkVtU3FrUlZzRng5Q3B0T2lCV1lpVkt0UnpQWjBI?= =?utf-8?B?TE1RL1I2N2JHcW42V1E3NjA4dmhNZDFsZTZkcDBwd3ZtbTFrQUtkWFhLSzVM?= =?utf-8?B?RnJNQzVBLzV3N2pGQllKYmw0NEgrVHd2V1hvbGZoaFRGRmR5MmNSazZQbGRO?= =?utf-8?B?NE9oUkVLRlhRZG5NMUJrR01hVVdNY1FXL3JIWkhLWnZwR0F6N0MxVlZNV1Fq?= =?utf-8?B?M29lWjNsYWJqbmtYOEloMVFhaHVYSXVSaCs4Z0FBNlR0T015M1lDYVZpbTdZ?= =?utf-8?B?SmxGWVJVOWZGVjIxSWx3OFA2YWNEYUdSbTF5U1A2TEpkdU51aGtpOU0rWkxN?= =?utf-8?B?UDR0c01ZZ251L2VGVklDTk1mSEJZQ3hWdjRldnJsY2VMMDRTNDBHSFprZC9I?= =?utf-8?B?SjdXOUd0OFlSaXcrZm5vMWZBNTNCTW1XUnJ6ZFMrRjE4eG1WYXdVTzVkWWlF?= =?utf-8?B?UjRKZlpMNEVPRStDVTNha0tST2VKOERuTU9PaFVHUGxQOW15dm14NU5xRWw4?= =?utf-8?B?d3lnSTFWaFAyM3FFNld0c2ZpeENNL1FlWnZyZ3o5Nnp4TGw3K244RFVMekxF?= =?utf-8?B?a3hmRGFHWlJKWXZjc1ZYQkliY1U3RzQ1RHo2dFlzQy9iVUI0KzNWTlVDQjFC?= =?utf-8?B?OWtBamRjUkQzTnhLYjJ5Z21qRFcvR3d6cHpvZDBhdnNwM0hLbjFnZENpUzlx?= =?utf-8?B?bzEvaXc4SXRLVTBUNmNxUHdDdkg1Wktua094aW9RVjVuUnk5eXhXL0dMRnZq?= =?utf-8?B?b3Jac1FWcGsxaWZJdkpKcVNTeEJUc3R1MzJ6NVNTODBHQ254cTRIU2k1dEhM?= =?utf-8?B?VUhLeExENDc0Yk5SQkl1MmN1SFBZUTlvditQMFpVVHRDTy8wQ3NDWkZuYmRS?= =?utf-8?B?c0xXU0xRNFpHMldOd1FFU3RBR2pmYU5tSjNWd1BDVjJzRHZ1Z2cvcHZvcUZP?= =?utf-8?B?aXZmUEZSOGZpUTVmSWt1NmxhNk5rWTB0MklKNmlZYjF2K09CWHlPV210ZGhN?= =?utf-8?B?NmJRQWorUCtSWU55d0tMWTJvcUJuTG1sazVuVGlVT25kZlowYzZmMnlyTVpS?= =?utf-8?B?Z0FIdlViYWl4bWhlNHVraXRId2pkV3lxSUNiOWFRYmZmVFdBOG5tVy9mQUZh?= =?utf-8?Q?VevTtB7y3XdIvhV?= X-Microsoft-Exchange-Diagnostics: 1; AM5PR0701MB2529; 6:5MfWO9RKVj2Bsrg7dXamLuCxU8WbVZqKiTrHRsaeIv5MZnM4Slu5SwjCyvUyGytpQk+FdXrC0gZeJLLmeR3BA68lYOj4ZlLwVOlwVfmt8TFaY+daWyhtne3zQ5Lykhs2IQK2sX9YTM0a0TT/YRKlG6zvB+Fb8cSq/E+2lGDCboFsFNukFXsfy7ePwEbOmhn6BJqzCiSi65T2GDKpWd5hS3emYTcygm4XldBKyD+33KPmES8Ip9lbnItNJZlwpOqU9auLUifbtMxuxdXEt5bFw7mUhGFGvTzAHXpN7VdauJ0Dl/5wyXPwuGdYFZ1jrEVphHfSvrZCYB3iH1GR5eAHBg==; 5:RZhHyiZ4YLmBZjiQe1ahVo2xkdT7Q6rQBurcIPqzF2OGMuhd6bFVltpA0DkYZPhUrSN+YQ5eA1/MQtzGOWuziajxYU4yu7hesaMpeClEYboBHqb+CMqEAuqxVlbma7DFi8izHHvNSZkEx8qqCsj32Q==; 24:jzI5idPK1OifGOsFYenvLiWG7CxXG8eKw8zKFpk/AFQe0l7rDsfkbl/XyvsxV7tRDxYuMcLZmFaVixAdTx98alW8Nq4UTYkCUK+dG9IupO8=; 7:zcsgRLcyxCzmcd40Y9TWKXuM6nKFPDVPODvgSjcWFklnkdt0JEVtU2gthq60gzT+NhV19FFtyJ3bl7BaNHAnloLtbUpuFlcAoEWPEZo0pa1wSIxiDAQnzBpUZL4KTpITsB1nILJjHXOMqtsLz79tfHJ37a6tUScAGx5CStxY2P42eNKxUfZ+1t4/IFCfjFymLDK1wKtXxD4wdym21v3wWcewL08g8hZjmGscNX5Ldve4hcbgcANhhFTXc7mMdRSda+F9Q6z34fofaAiHcvF93MzyMj0vej3EGKGpNR0z5JxNWU/eOuUkD8v6x5fwQZA/ SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: nokia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 19 Sep 2016 13:26:18.6058 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM5PR0701MB2529 Subject: [FFmpeg-devel] [PATCH v3 25/25] doc/examples/muxing_with_metadata: example for dealing with timed meta data 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 Cc: =?UTF-8?q?Erkki=20Sepp=C3=A4l=C3=A4?= , OZOPlayer Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Erkki Seppälä Signed-off-by: OZOPlayer --- configure | 2 + doc/examples/Makefile | 2 + doc/examples/muxing_with_metadata.c | 885 ++++++++++++++++++++++++++++++++++++ 3 files changed, 889 insertions(+) create mode 100644 doc/examples/muxing_with_metadata.c diff --git a/configure b/configure index b586353..00726e2 100755 --- a/configure +++ b/configure @@ -1459,6 +1459,7 @@ EXAMPLE_LIST=" filtering_video_example metadata_example muxing_example + muxing_with_metadata_example qsvdec_example remuxing_example resampling_audio_example @@ -3095,6 +3096,7 @@ filtering_audio_example_deps="avfilter avcodec avformat avutil" filtering_video_example_deps="avfilter avcodec avformat avutil" metadata_example_deps="avformat avutil" muxing_example_deps="avcodec avformat avutil swscale" +muxing_with_metadata_example_deps="avcodec avformat avutil swscale" qsvdec_example_deps="avcodec avutil libmfx h264_qsv_decoder vaapi_x11" remuxing_example_deps="avcodec avformat avutil" resampling_audio_example_deps="avutil swresample" diff --git a/doc/examples/Makefile b/doc/examples/Makefile index c10033e..d1f4e1f 100644 --- a/doc/examples/Makefile +++ b/doc/examples/Makefile @@ -22,6 +22,7 @@ EXAMPLES= avio_dir_cmd \ http_multiclient \ metadata \ muxing \ + muxing_with_metadata \ remuxing \ resampling_audio \ scaling_video \ @@ -35,6 +36,7 @@ avcodec: LDLIBS += -lm decoding_encoding: LDLIBS += -lm muxing: LDLIBS += -lm resampling_audio: LDLIBS += -lm +muxing_with_metadata: LDLIBS += -lm .phony: all clean-test clean diff --git a/doc/examples/muxing_with_metadata.c b/doc/examples/muxing_with_metadata.c new file mode 100644 index 0000000..6631141 --- /dev/null +++ b/doc/examples/muxing_with_metadata.c @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2015 Erkki Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file + * libavformat API example. + * + * Output a media file in any supported libavformat format. The default + * codecs are used. + * @example muxing.c + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STREAM_DURATION 10.0 +#define STREAM_FRAME_RATE 25 /* 25 images/s */ +#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */ + +#define SCALE_FLAGS SWS_BICUBIC + +// a wrapper around a single output AVStream +typedef struct OutputStream { + AVStream *st; + AVCodecContext *enc; + + /* pts of the next frame that will be generated */ + int64_t next_pts; + int samples_count; + + AVFrame *frame; + AVFrame *tmp_frame; + + float t, tincr, tincr2; + + struct SwsContext *sws_ctx; + struct SwrContext *swr_ctx; +} OutputStream; + +typedef struct StreamState { + int (*writer)(AVFormatContext *oc, OutputStream *ost); + int *flag; + OutputStream *stream; +} StreamState; + +static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt) +{ + AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base; + + printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n", + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base), + pkt->stream_index); +} + +static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt) +{ + /* rescale output packet timestamp values from codec to stream timebase */ + av_packet_rescale_ts(pkt, *time_base, st->time_base); + pkt->stream_index = st->index; + + /* Write the compressed frame to the media file. */ + log_packet(fmt_ctx, pkt); + return av_interleaved_write_frame(fmt_ctx, pkt); +} + +/* Add an output stream. */ +static void add_stream(OutputStream *ost, AVFormatContext *oc, + AVCodec **codec, + enum AVCodecID codec_id) +{ + AVCodecContext *c; + int i; + + /* find the encoder */ + *codec = avcodec_find_encoder(codec_id); + if (!(*codec)) { + fprintf(stderr, "Could not find encoder for '%s'\n", + avcodec_get_name(codec_id)); + exit(1); + } + + ost->st = avformat_new_stream(oc, NULL); + if (!ost->st) { + fprintf(stderr, "Could not allocate stream\n"); + exit(1); + } + ost->st->id = oc->nb_streams-1; + c = avcodec_alloc_context3(*codec); + if (!c) { + fprintf(stderr, "Could not alloc an encoding context\n"); + exit(1); + } + ost->enc = c; + + switch ((*codec)->type) { + case AVMEDIA_TYPE_AUDIO: + c->sample_fmt = (*codec)->sample_fmts ? + (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; + c->bit_rate = 64000; + c->sample_rate = 44100; + if ((*codec)->supported_samplerates) { + c->sample_rate = (*codec)->supported_samplerates[0]; + for (i = 0; (*codec)->supported_samplerates[i]; i++) { + if ((*codec)->supported_samplerates[i] == 44100) + c->sample_rate = 44100; + } + } + c->channels = av_get_channel_layout_nb_channels(c->channel_layout); + c->channel_layout = AV_CH_LAYOUT_STEREO; + if ((*codec)->channel_layouts) { + c->channel_layout = (*codec)->channel_layouts[0]; + for (i = 0; (*codec)->channel_layouts[i]; i++) { + if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO) + c->channel_layout = AV_CH_LAYOUT_STEREO; + } + } + c->channels = av_get_channel_layout_nb_channels(c->channel_layout); + ost->st->time_base = (AVRational){ 1, c->sample_rate }; + break; + + case AVMEDIA_TYPE_VIDEO: + c->codec_id = codec_id; + + c->bit_rate = 400000; + /* Resolution must be a multiple of two. */ + c->width = 352; + c->height = 288; + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE }; + c->time_base = ost->st->time_base; + + c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = STREAM_PIX_FMT; + if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + c->max_b_frames = 2; + } + if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + c->mb_decision = 2; + } + break; + + case AVMEDIA_TYPE_DATA: + c->codec_id = codec_id; + + c->bit_rate = 10; + /* timebase: This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identical to 1. */ + ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE }; + c->time_base = ost->st->time_base; + break; + + default: + break; + } + + /* Some formats want stream headers to be separate. */ + if (oc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +} + +/**************************************************************/ +/* audio output */ + +static AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt, + uint64_t channel_layout, + int sample_rate, int nb_samples) +{ + AVFrame *frame = av_frame_alloc(); + int ret; + + if (!frame) { + fprintf(stderr, "Error allocating an audio frame\n"); + exit(1); + } + + frame->format = sample_fmt; + frame->channel_layout = channel_layout; + frame->sample_rate = sample_rate; + frame->nb_samples = nb_samples; + + if (nb_samples) { + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + fprintf(stderr, "Error allocating an audio buffer\n"); + exit(1); + } + } + + return frame; +} + +static void open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg) +{ + AVCodecContext *c; + int nb_samples; + int ret; + AVDictionary *opt = NULL; + + c = ost->enc; + + /* open it */ + av_dict_copy(&opt, opt_arg, 0); + ret = avcodec_open2(c, codec, &opt); + av_dict_free(&opt); + if (ret < 0) { + fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret)); + exit(1); + } + + /* init signal generator */ + ost->t = 0; + ost->tincr = 2 * M_PI * 110.0 / c->sample_rate; + /* increment frequency by 110 Hz per second */ + ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; + + if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) + nb_samples = 10000; + else + nb_samples = c->frame_size; + + ost->frame = alloc_audio_frame(c->sample_fmt, c->channel_layout, + c->sample_rate, nb_samples); + ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_S16, c->channel_layout, + c->sample_rate, nb_samples); + + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(ost->st->codecpar, c); + if (ret < 0) { + fprintf(stderr, "Could not copy the stream parameters\n"); + exit(1); + } + + /* create resampler context */ + ost->swr_ctx = swr_alloc(); + if (!ost->swr_ctx) { + fprintf(stderr, "Could not allocate resampler context\n"); + exit(1); + } + + /* set options */ + av_opt_set_int (ost->swr_ctx, "in_channel_count", c->channels, 0); + av_opt_set_int (ost->swr_ctx, "in_sample_rate", c->sample_rate, 0); + av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0); + av_opt_set_int (ost->swr_ctx, "out_channel_count", c->channels, 0); + av_opt_set_int (ost->swr_ctx, "out_sample_rate", c->sample_rate, 0); + av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt", c->sample_fmt, 0); + + /* initialize the resampling context */ + if ((ret = swr_init(ost->swr_ctx)) < 0) { + fprintf(stderr, "Failed to initialize the resampling context\n"); + exit(1); + } +} + +static AVFrame *alloc_meta_frame(int nb_samples) +{ + AVFrame *frame = av_frame_alloc(); + + if (!frame) { + fprintf(stderr, "Error allocating a meta frame\n"); + exit(1); + } + + frame->nb_samples = nb_samples; + + if (nb_samples) { + frame->buf[0] = av_buffer_alloc(nb_samples); + + av_assert0(frame->buf[0]); + av_assert0(frame->buf[0]->data); + + frame->data[0] = frame->buf[0]->data; + } + + return frame; +} + + +static void open_data(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg) +{ + AVCodecContext *c; + int nb_samples = 100; + int ret; + AVDictionary *opt = NULL; + + c = ost->enc; + + /* open it */ + av_dict_copy(&opt, opt_arg, 0); + ret = avcodec_open2(c, codec, &opt); + av_dict_free(&opt); + if (ret < 0) { + fprintf(stderr, "Could not open meta codec: %s\n", av_err2str(ret)); + exit(1); + } + + ost->frame = alloc_meta_frame(nb_samples); + ost->tmp_frame = alloc_meta_frame(nb_samples); + + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(ost->st->codecpar, c); + if (ret < 0) { + fprintf(stderr, "Could not copy the stream parameters\n"); + exit(1); + } +} + + +/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and + * 'nb_channels' channels. */ +static AVFrame *get_audio_frame(OutputStream *ost) +{ + AVFrame *frame = ost->tmp_frame; + int j, i, v; + int16_t *q = (int16_t*)frame->data[0]; + + /* check if we want to generate more frames */ + if (av_compare_ts(ost->next_pts, ost->enc->time_base, + STREAM_DURATION, (AVRational){ 1, 1 }) >= 0) + return NULL; + + for (j = 0; j nb_samples; j++) { + v = (int)(sin(ost->t) * 10000); + for (i = 0; i < ost->enc->channels; i++) + *q++ = v; + ost->t += ost->tincr; + ost->tincr += ost->tincr2; + } + + frame->pts = ost->next_pts; + ost->next_pts += frame->nb_samples; + + return frame; +} + +static AVFrame *get_meta_frame(OutputStream *ost) +{ + AVFrame *frame = ost->tmp_frame; + unsigned char *buffer = (unsigned char*) frame->data[0]; + + static int n = 0; + ++n; + + frame->pts = ost->next_pts; + ost->next_pts += 1; + + snprintf((char*) buffer, 42, "urn:example.com:%d", n); + frame->data[0] = buffer; + frame->nb_samples = strlen((char*) buffer); + frame->pts = ost->next_pts++; + + return frame; +} + +static int write_timed_meta_frame(AVFormatContext *oc, OutputStream *ost) +{ + AVCodecContext *c; + AVPacket pkt = { 0 }; // data and size must be 0; + AVFrame *frame; + int ret; + + av_init_packet(&pkt); + c = ost->enc; + + frame = get_meta_frame(ost); + + ost->samples_count += 1; + + if ((ret = avcodec_send_frame(c, frame))) { + fprintf(stderr, "Error while encoding meta frame: %s\n", + av_err2str(ret)); + exit(1); + } + + while (avcodec_receive_packet(c, &pkt) == 0) { + c->frame_number++; + + ret = write_frame(oc, &c->time_base, ost->st, &pkt); + if (ret < 0) { + fprintf(stderr, "Error while writing meta frame: %s\n", + av_err2str(ret)); + exit(1); + } + av_packet_unref(&pkt); + } + + return 0; +} + + +/* + * encode one audio frame and send it to the muxer + * return 1 when encoding is finished, 0 otherwise + */ +static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) +{ + AVCodecContext *c; + AVPacket pkt = { 0 }; // data and size must be 0; + AVFrame *frame; + int ret; + int dst_nb_samples; + int got_packet = 0; + int eof = 0; + + c = ost->enc; + + frame = get_audio_frame(ost); + + if (frame) { + /* convert samples from native format to destination codec format, using the resampler */ + /* compute destination number of samples */ + dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples, + c->sample_rate, c->sample_rate, AV_ROUND_UP); + av_assert0(dst_nb_samples == frame->nb_samples); + + /* when we pass a frame to the encoder, it may keep a reference to it + * internally; + * make sure we do not overwrite it here + */ + ret = av_frame_make_writable(ost->frame); + if (ret < 0) + exit(1); + + /* convert to destination format */ + ret = swr_convert(ost->swr_ctx, + ost->frame->data, dst_nb_samples, + (const uint8_t **)frame->data, frame->nb_samples); + if (ret < 0) { + fprintf(stderr, "Error while converting\n"); + exit(1); + } + frame = ost->frame; + + frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base); + ost->samples_count += dst_nb_samples; + } + + av_init_packet(&pkt); + ret = avcodec_send_frame(c, frame); + if (ret == AVERROR_EOF) + eof = 1; + else + if (ret < 0) { + fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret)); + exit(1); + } + + while (avcodec_receive_packet(c, &pkt) == 0) { + ret = write_frame(oc, &c->time_base, ost->st, &pkt); + if (ret < 0) { + fprintf(stderr, "Error while writing audio frame: %s\n", + av_err2str(ret)); + exit(1); + } else + got_packet = 1; + av_packet_unref(&pkt); + } + + return ((frame || got_packet) && !eof) ? 0 : 1; +} + +/**************************************************************/ +/* video output */ + +static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) +{ + AVFrame *picture; + int ret; + + picture = av_frame_alloc(); + if (!picture) + return NULL; + + picture->format = pix_fmt; + picture->width = width; + picture->height = height; + + /* allocate the buffers for the frame data */ + ret = av_frame_get_buffer(picture, 32); + if (ret < 0) { + fprintf(stderr, "Could not allocate frame data.\n"); + exit(1); + } + + return picture; +} + +static void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg) +{ + int ret; + AVCodecContext *c = ost->enc; + AVDictionary *opt = NULL; + + av_dict_copy(&opt, opt_arg, 0); + + /* open the codec */ + ret = avcodec_open2(c, codec, &opt); + av_dict_free(&opt); + if (ret < 0) { + fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret)); + exit(1); + } + + /* allocate and init a re-usable frame */ + ost->frame = alloc_picture(c->pix_fmt, c->width, c->height); + if (!ost->frame) { + fprintf(stderr, "Could not allocate video frame\n"); + exit(1); + } + + /* If the output format is not YUV420P, then a temporary YUV420P + * picture is needed too. It is then converted to the required + * output format. */ + ost->tmp_frame = NULL; + if (c->pix_fmt != AV_PIX_FMT_YUV420P) { + ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height); + if (!ost->tmp_frame) { + fprintf(stderr, "Could not allocate temporary picture\n"); + exit(1); + } + } + + /* copy the stream parameters to the muxer */ + ret = avcodec_parameters_from_context(ost->st->codecpar, c); + if (ret < 0) { + fprintf(stderr, "Could not copy the stream parameters\n"); + exit(1); + } +} + +/* Prepare a dummy image. */ +static void fill_yuv_image(AVFrame *pict, int frame_index, + int width, int height) +{ + int x, y, i, ret; + + /* when we pass a frame to the encoder, it may keep a reference to it + * internally; + * make sure we do not overwrite it here + */ + ret = av_frame_make_writable(pict); + if (ret < 0) + exit(1); + + i = frame_index; + + /* Y */ + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; + + /* Cb and Cr */ + for (y = 0; y < height / 2; y++) { + for (x = 0; x < width / 2; x++) { + pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; + pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; + } + } +} + +static AVFrame *get_video_frame(OutputStream *ost) +{ + AVCodecContext *c = ost->enc; + + /* check if we want to generate more frames */ + if (av_compare_ts(ost->next_pts, ost->enc->time_base, + STREAM_DURATION, (AVRational){ 1, 1 }) >= 0) + return NULL; + + if (c->pix_fmt != AV_PIX_FMT_YUV420P) { + /* as we only generate a YUV420P picture, we must convert it + * to the codec pixel format if needed */ + if (!ost->sws_ctx) { + ost->sws_ctx = sws_getContext(c->width, c->height, + AV_PIX_FMT_YUV420P, + c->width, c->height, + c->pix_fmt, + SCALE_FLAGS, NULL, NULL, NULL); + if (!ost->sws_ctx) { + fprintf(stderr, + "Could not initialize the conversion context\n"); + exit(1); + } + } + fill_yuv_image(ost->tmp_frame, ost->next_pts, c->width, c->height); + sws_scale(ost->sws_ctx, + (const uint8_t * const *)ost->tmp_frame->data, ost->tmp_frame->linesize, + 0, c->height, ost->frame->data, ost->frame->linesize); + } else { + fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height); + } + + ost->frame->pts = ost->next_pts++; + + return ost->frame; +} + +/* + * encode one video frame and send it to the muxer + * return 1 when encoding is finished, 0 otherwise + */ +static int write_video_frame(AVFormatContext *oc, OutputStream *ost) +{ + int ret; + AVCodecContext *c; + AVFrame *frame; + int got_packet = 0; + + c = ost->enc; + + frame = get_video_frame(ost); + + if (oc->oformat->flags & AVFMT_RAWPICTURE) { + /* a hack to avoid data copy with some raw video muxers */ + AVPacket pkt; + av_init_packet(&pkt); + + if (!frame) + return 1; + + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index = ost->st->index; + pkt.data = (uint8_t *)frame; + pkt.size = sizeof(AVPicture); + + pkt.pts = pkt.dts = frame->pts; + av_packet_rescale_ts(&pkt, c->time_base, ost->st->time_base); + + ret = av_interleaved_write_frame(oc, &pkt); + } else { + AVPacket pkt = { 0 }; + av_init_packet(&pkt); + + /* encode the image */ + ret = avcodec_send_frame(c, frame); + if (ret < 0) { + fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); + exit(1); + } + + while (avcodec_receive_packet(c, &pkt) == 0) { + ret = write_frame(oc, &c->time_base, ost->st, &pkt); + av_packet_unref(&pkt); + } + } + + if (ret < 0) { + fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret)); + exit(1); + } + + return (frame || got_packet) ? 0 : 1; +} + +static void close_stream(AVFormatContext *oc, OutputStream *ost) +{ + avcodec_close(ost->enc); + av_frame_free(&ost->frame); + av_frame_free(&ost->tmp_frame); + sws_freeContext(ost->sws_ctx); + swr_free(&ost->swr_ctx); +} + +/**************************************************************/ +/* media file output */ + +int main(int argc, char **argv) +{ + OutputStream video_st = { 0 }, audio_st = { 0 }, data_st = { 0 }; + const char *filename; + AVOutputFormat *fmt; + AVFormatContext *oc; + AVCodec *audio_codec, *video_codec, *data_codec; + int ret; + int have_video = 0, have_audio = 0, have_data = 0; + int encode_video = 0, encode_audio = 0, mux_meta = 0; + AVDictionary *opt = NULL; + + StreamState streams[3] = { + { write_video_frame, + &encode_video, + &video_st }, + { write_audio_frame, + &encode_audio, + &audio_st }, + { write_timed_meta_frame, + &mux_meta, + &data_st } + }; + + /* Initialize libavcodec, and register all codecs and formats. */ + av_register_all(); + + if (argc < 2) { + printf("usage: %s output_file\n" + "API example program to output a media file with libavformat.\n" + "This program generates a synthetic audio and video stream, encodes and\n" + "muxes them into a file named output_file.\n" + "The output format is automatically guessed according to the file extension.\n" + "Raw images can also be output by using '%%d' in the filename.\n" + "\n", argv[0]); + return 1; + } + + filename = argv[1]; + if (argc > 3 && !strcmp(argv[2], "-flags")) { + av_dict_set(&opt, argv[2]+1, argv[3], 0); + } + + /* allocate the output media context */ + avformat_alloc_output_context2(&oc, NULL, NULL, filename); + if (!oc) { + printf("Could not deduce output format from file extension: using MPEG.\n"); + avformat_alloc_output_context2(&oc, NULL, "mpeg", filename); + } + if (!oc) + return 1; + + fmt = oc->oformat; + + /* Add the audio and video streams using the default format codecs + * and initialize the codecs. */ + if (fmt->video_codec != AV_CODEC_ID_NONE) { + add_stream(&video_st, oc, &video_codec, fmt->video_codec); + have_video = 1; + encode_video = 1; + } + if (fmt->audio_codec != AV_CODEC_ID_NONE) { + add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec); + have_audio = 1; + encode_audio = 1; + } + if (fmt->data_codec != AV_CODEC_ID_NONE) { + add_stream(&data_st, oc, &data_codec, fmt->data_codec); + have_data = 1; + mux_meta = 1; + } + + /* Now that all the parameters are set, we can open the audio and + * video codecs and allocate the necessary encode buffers. */ + if (have_video) + open_video(oc, video_codec, &video_st, opt); + + if (have_audio) + open_audio(oc, audio_codec, &audio_st, opt); + + if (have_data) + open_data(oc, data_codec, &data_st, opt); + + av_dump_format(oc, 0, filename, 1); + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE); + if (ret < 0) { + fprintf(stderr, "Could not open '%s': %s\n", filename, + av_err2str(ret)); + return 1; + } + } + + /* Write the stream header, if any. */ + ret = avformat_write_header(oc, &opt); + if (ret < 0) { + fprintf(stderr, "Error occurred when opening output file: %s\n", + av_err2str(ret)); + return 1; + } + + if (fmt->data_codec != AV_CODEC_ID_NONE) { + AVTimedMetadataInfo* metadata; + char* uri = "http://example.com/"; + void* ptr; + + AVTrackReferences* trefs; + char* msg = "test config data"; + int nb_tref_tracks = 2; + + metadata = (AVTimedMetadataInfo*) av_stream_new_side_data(oc->streams[2], AV_PKT_DATA_TIMED_METADATA_INFO, + sizeof(AVTimedMetadataInfo) + strlen(uri) + strlen(msg)); + + memcpy(metadata->meta_tag, "urim", 4); + metadata->meta_length = strlen(uri); + + memset(metadata->conf_tag, 0, sizeof(metadata->conf_tag)); + memcpy(metadata->conf_tag, "cfg ", 4); + metadata->conf_length = strlen(msg); + + ptr = (char*) (metadata + 1); + memcpy(ptr, uri, strlen(uri)); + ptr += strlen(uri); + memcpy(ptr, msg, strlen(msg)); + + trefs = (AVTrackReferences*) av_stream_new_side_data(oc->streams[2], AV_PKT_DATA_TRACK_REFERENCES, + sizeof(AVTrackReferences) + nb_tref_tracks * sizeof(int)); + trefs->next_tref_ofs = 0; + memcpy(trefs->tag, "cdsc", 4); + trefs->nb_tracks = nb_tref_tracks; + trefs->tracks[0] = 0; + trefs->tracks[1] = 1; + } + + while (encode_video || encode_audio) { + StreamState *stream = NULL; + int idx; + + for (idx = 0; idx < 3; ++idx) { + if (*streams[idx].flag && + (stream == NULL || + av_compare_ts(streams[idx].stream->next_pts, streams[idx].stream->enc->time_base, + stream->stream->next_pts, stream->stream->enc->time_base) <= 0)) { + stream = &streams[idx]; + } + } + + if (stream) { + *stream->flag = !stream->writer(oc, stream->stream); + } + } + + /* Write the trailer, if any. The trailer must be written before you + * close the CodecContexts open when you wrote the header; otherwise + * av_write_trailer() may try to use memory that was freed on + * av_codec_close(). */ + av_write_trailer(oc); + + /* Close each codec. */ + if (have_video) + close_stream(oc, &video_st); + if (have_audio) + close_stream(oc, &audio_st); + if (mux_meta) + close_stream(oc, &data_st); + + if (!(fmt->flags & AVFMT_NOFILE)) + /* Close the output file. */ + avio_closep(&oc->pb); + + /* free the stream */ + avformat_free_context(oc); + + return 0; +}