From patchwork Fri Jun 16 22:20:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Niedermayer X-Patchwork-Id: 42162 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp1872023pzb; Fri, 16 Jun 2023 15:45:28 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ48weqd+/WjAf/7nr03u0IjD+lm4DtceRuSLvfFuzXgZq4Wf8MXX1y07WRANmrnWY+H/rlw X-Received: by 2002:a05:6402:164a:b0:516:7f02:92c0 with SMTP id s10-20020a056402164a00b005167f0292c0mr2388109edx.39.1686955527829; Fri, 16 Jun 2023 15:45:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686955527; cv=none; d=google.com; s=arc-20160816; b=Zg9PTYyl/EQ9ta1j8tK2BoeYkwkieQ7Bdq13/mUELKZlhOOukrk/SCLu7jVuHWh0Gg oUs/CnOdEndTvG1UZhkpYVk+QPSRD31jIb+/5IH39kaI1sQ8Jj5YX0BLpn5b5qlAKrmX pGCrYyIzqckpwvUFxmwOGpHlN5XAt1wutc6Xyy6m7pnxHi0cGFGnvd1RD7UD8jB+pLlC DoYELiZjFiYYCBlxgBv/tN8WobIvpc2ZDIUOPuyIYfMcAR9BdifRv5c5LJX/XRxLXBmF Cv3+cqPgczp/GD4nJJPsqDd9AWrIDg4sZzpkyTMuuv9B9tQn3HG3m4q+KC9C7GfPfc2s SL/g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:delivered-to; bh=iVXEththxI00GRLSGhfQXUgDGLfnrUJ7oDmJ3UCUAVE=; b=NiOPIgw2n753HFCi+V7KiYYPLHa57pPV/ixKaIhT5FNPRYMeeNP+rlkLUjn3giZ5ub W7FX/Tjg2qYA9o7BW3TR1jJ3INMjBwfcmpNaXOd/pouVpIadpyFwkwja2/88JmqpxD0T r3Jui+s6qa+ZmcWDe5HjWMF6+8gkV6Y/i2zTUYu+af0G75pr2YxgBWIY1RwJgQ2Jwszv pvPUkV4CzZN7FWiL9XiWBp0lTuFNoH2MEqjELcwZRLNYSqc6gUG6UQMg2Xqnl9NqhX50 JLIQxkYHlY3Ld05Y0JZBQQaBoBL/rlTdYqHccx/jzAITbf22OXvSFs3QIUaOX+zCG12F R3OQ== ARC-Authentication-Results: i=1; mx.google.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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id r3-20020aa7da03000000b0051a2f46cdb8si1897692eds.322.2023.06.16.15.45.27; Fri, 16 Jun 2023 15:45:27 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 85E0268C1BC; Sat, 17 Jun 2023 01:45:23 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-01-1.mymagenta.at (mail-01-1.mymagenta.at [80.109.253.246]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DF8F668C02A for ; Sat, 17 Jun 2023 01:45:16 +0300 (EEST) Received: from [192.168.232.136] (helo=ren-mail-psmtp-mg02.) by mail-01.mymagenta.at with esmtp (Exim 4.93) (envelope-from ) id 1qAHoV-0099B7-M5 for ffmpeg-devel@ffmpeg.org; Sat, 17 Jun 2023 00:21:03 +0200 Received: from localhost ([84.115.40.24]) by ren-mail-psmtp-mg02. with ESMTP id AHoXqebwubZLDAHoXqtm6H; Sat, 17 Jun 2023 00:21:05 +0200 X-Env-Mailfrom: michael@niedermayer.cc X-Env-Rcptto: ffmpeg-devel@ffmpeg.org X-SourceIP: 84.115.40.24 X-CNFS-Analysis: v=2.4 cv=Ufwy9IeN c=1 sm=1 tr=0 ts=648ce051 a=4thelYDX6rwh+ygQwvsI+Q==:117 a=4thelYDX6rwh+ygQwvsI+Q==:17 a=MKtGQD3n3ToA:10 a=1oJP67jkp3AA:10 a=GEAsPZ9sns4A:10 a=70bDiBxVjl2u6EBR22QA:9 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Sat, 17 Jun 2023 00:20:44 +0200 Message-Id: <20230616222048.6562-2-michael@niedermayer.cc> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230616222048.6562-1-michael@niedermayer.cc> References: <20230616222048.6562-1-michael@niedermayer.cc> X-CMAE-Envelope: MS4xfHsTPnVMZV/Pgr5dbSQNsjmOhmUGGLckm1rRI0k159HKJkTUrploVPr6w0nMtYNyTp6NTBc7Yk9hcONNDPF657bD0T+0cVC7PW+C/GhwKiprmX4uoLiI WyMEZW71s8rwf9ED7vcwTv4+T0RYvwplgv8crAxVyPlC8yOIsRUYsknuzoet+zM+9lZlC9i5D7b/3Q== Subject: [FFmpeg-devel] [PATCH 1/5] avutil/tx_template: extend to 2M X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Ffjd3IJqjvoY Signed-off-by: Michael Niedermayer --- libavutil/tx_template.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libavutil/tx_template.c b/libavutil/tx_template.c index 983de75a47..c4ec9502e0 100644 --- a/libavutil/tx_template.c +++ b/libavutil/tx_template.c @@ -43,6 +43,10 @@ SR_TABLE(32768) \ SR_TABLE(65536) \ SR_TABLE(131072) \ + SR_TABLE(262144) \ + SR_TABLE(524288) \ + SR_TABLE(1048576) \ + SR_TABLE(2097152) \ #define SR_TABLE(len) \ TABLE_DEF(len, len/4 + 1); @@ -717,6 +721,10 @@ DECL_SR_CODELET(16384,8192,4096) DECL_SR_CODELET(32768,16384,8192) DECL_SR_CODELET(65536,32768,16384) DECL_SR_CODELET(131072,65536,32768) +DECL_SR_CODELET(262144,131072,65536) +DECL_SR_CODELET(524288,262144,131072) +DECL_SR_CODELET(1048576,524288,262144) +DECL_SR_CODELET(2097152,1048576,524288) static av_cold int TX_NAME(ff_tx_fft_init)(AVTXContext *s, const FFTXCodelet *cd, @@ -1947,6 +1955,10 @@ const FFTXCodelet * const TX_NAME(ff_tx_codelet_list)[] = { &TX_NAME(ff_tx_fft32768_ns_def), &TX_NAME(ff_tx_fft65536_ns_def), &TX_NAME(ff_tx_fft131072_ns_def), + &TX_NAME(ff_tx_fft262144_ns_def), + &TX_NAME(ff_tx_fft524288_ns_def), + &TX_NAME(ff_tx_fft1048576_ns_def), + &TX_NAME(ff_tx_fft2097152_ns_def), /* Prime factor codelets */ &TX_NAME(ff_tx_fft3_ns_def), From patchwork Fri Jun 16 22:20:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Niedermayer X-Patchwork-Id: 42163 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp1873568pzb; Fri, 16 Jun 2023 15:50:01 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4jbrrKjK7KJr7g4Vd+Tl+YbZqBIrV72tvYmUvgfLxaFcZvI0OaDPdhC1PUu4aZ6wE68VyL X-Received: by 2002:a17:907:6d83:b0:973:d3ee:6bdf with SMTP id sb3-20020a1709076d8300b00973d3ee6bdfmr2910824ejc.43.1686955800805; Fri, 16 Jun 2023 15:50:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686955800; cv=none; d=google.com; s=arc-20160816; b=Zr3/XYNQ1u081hsGBZ1ngv8zbePxZDIMQTjKbidBaxiYJ6yedVI6chfhsXap51u6jb HPJrQhHnNUK6BFEqrqBrZi73w8EN8nWpZsxi/vIvs7SJq4p9KEv+zykkJO5IjxX9+rtO 2ZE2rSr30gEWyuqZ2XgCJWf74z7mmcDq24qSxa7YVMuodP5TTOgcTe1k9JxTlc9Zjk6p PkyP+5r5ckuITDpcI9ZrVFA0kYCgJUrKl51Bc4yvXdRbex9kFcwndBPDeNRIWoxyKM+p Mvz2Jd67mIw1BBFAn0BVBwc2XtS/4VmjzJN5QkLxLDYsves8P+36LIjsO0pEiw/tWTBB oxuQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:delivered-to; bh=iptFhSVQ5FFwHU8wHv8f+fl/jiZ8FTy1AToqMyHJ3Ws=; b=btSVR9vm/NbQBV44FZLuC52591PH6/1hG6HziiAKUOfN87f7wjYV+HCCe9iBc4aOw1 766xNhHSptc4PcsVPNIjzkt52dWQto66xJXjEQ2pZOZ5wCe2RiyFPCMIBiH37WMM2AWi adsWj07Ja2dgA35b4xCTtwhmndl5XRFtH5Apnu0V78PDxsr/s9d1AGSPBym2LKt505lc 2n+/tHQG3OEX+sClEojMX1I+3EUgAzsOWuAvhIS+jEhe/tX+8Vh6mpiRKSgm0KflLDA5 /129nALfjBbaPBBvKmkH3+2iNSf+WgCy34y9iFPY1vVTzNbU/ghuDslT5YNhifou7BCH NOAw== ARC-Authentication-Results: i=1; mx.google.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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j13-20020a170906410d00b00982c76829f9si2170093ejk.192.2023.06.16.15.50.00; Fri, 16 Jun 2023 15:50:00 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CA71D68BF68; Sat, 17 Jun 2023 01:49:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-02-2.mymagenta.at (mail-02-2.mymagenta.at [80.109.253.249]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4291668BF68 for ; Sat, 17 Jun 2023 01:49:51 +0300 (EEST) Received: from [192.168.232.136] (helo=ren-mail-psmtp-mg02.) by mail-02.mymagenta.at with esmtp (Exim 4.93) (envelope-from ) id 1qAHoW-00FbXc-T8 for ffmpeg-devel@ffmpeg.org; Sat, 17 Jun 2023 00:21:04 +0200 Received: from localhost ([84.115.40.24]) by ren-mail-psmtp-mg02. with ESMTP id AHoXqebwybZLDAHoXqtm6I; Sat, 17 Jun 2023 00:21:05 +0200 X-Env-Mailfrom: michael@niedermayer.cc X-Env-Rcptto: ffmpeg-devel@ffmpeg.org X-SourceIP: 84.115.40.24 X-CNFS-Analysis: v=2.4 cv=Ufwy9IeN c=1 sm=1 tr=0 ts=648ce051 a=4thelYDX6rwh+ygQwvsI+Q==:117 a=4thelYDX6rwh+ygQwvsI+Q==:17 a=MKtGQD3n3ToA:10 a=1oJP67jkp3AA:10 a=GEAsPZ9sns4A:10 a=-z4jQ-M_9hlOjjBgS6EA:9 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Sat, 17 Jun 2023 00:20:45 +0200 Message-Id: <20230616222048.6562-3-michael@niedermayer.cc> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230616222048.6562-1-michael@niedermayer.cc> References: <20230616222048.6562-1-michael@niedermayer.cc> X-CMAE-Envelope: MS4xfHsTPnVMZV/Pgr5dbSQNsjmOhmUGGLckm1rRI0k159HKJkTUrploVPr6w0nMtYNyTp6NTBc7Yk9hcONNDPF657bD0T+0cVC7PW+C/GhwKiprmX4uoLiI WyMEZW71s8rwf9ED7vcwTv4+T0RYvwplgv8crAxVyPlC8yOIsRUYsknuzoet+zM+9lZlC9i5D7b/3Q== Subject: [FFmpeg-devel] [PATCH 2/5] avcodec/pcm: allow Changing parameters X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: KIvcbBrevBnJ SDR needs this for switching between mono and stereo stations Signed-off-by: Michael Niedermayer --- libavcodec/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index 23955ba2dd..467ecb4fe0 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -578,7 +578,7 @@ const FFCodec ff_ ## name_ ## _decoder = { \ .priv_data_size = sizeof(PCMDecode), \ .init = pcm_decode_init, \ FF_CODEC_DECODE_CB(pcm_decode_frame), \ - .p.capabilities = AV_CODEC_CAP_DR1, \ + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_PARAM_CHANGE, \ .p.sample_fmts = (const enum AVSampleFormat[]){ sample_fmt_, \ AV_SAMPLE_FMT_NONE }, \ } From patchwork Fri Jun 16 22:20:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Niedermayer X-Patchwork-Id: 42164 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp1885778pzb; Fri, 16 Jun 2023 16:18:00 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7Q1rYqZKjbW2RuaoZZ1dsW3VmIFiegL5m+GozVTsGacEVAklceQ5Cr4oBoSnM8+1Skadlv X-Received: by 2002:a17:907:84d:b0:978:6be4:7efa with SMTP id ww13-20020a170907084d00b009786be47efamr3001819ejb.18.1686957480394; Fri, 16 Jun 2023 16:18:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686957480; cv=none; d=google.com; s=arc-20160816; b=m+dLjNIaQ06T08LeCqVUQZ9y3Tr6Bg99RgaGN+ehwk/wJyBQ+VKDh/0Q988J7YYgrV E/Z07lKx26XVHMfgDQa6ourUnHrTtk9gBYb6JyHLP7IWq7nrb8HdwLc8bzQP2HyzPSCd VX5KbMnrF9tmwUIQs1obdT8yDWh0f6SZ9c1/UbuGENCf7mvEvIh0wPXcIDdazErIlGfp dxDjUQqdCgOScMkS3s+zB9bzDNsWgUbAH2KuQu/zw+vjxCamxcSPbR7ctFTkwxtjiEfT 6U+Uuhcu0vPzTzDXsfQePH6DYSbuxcS40RBRlvAj9l+9myXqLcedzV15KFC7E/iQ9Efx B5/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:delivered-to; bh=0jq7vJC9LqJ34AA0Qj/TXrDqud0D7BY0HFOK0EGNfHk=; b=ZLEWfWcSDq2mcNISIR0CgswwrjTA5VgEibtjH0RTaPIB1k4R7NroI23t56vculUF5J uyEIHop/uTsOjcYnLcbiqB4ew0NIkXcyAjeNfqcwlx72I3gUZPt/SmS/e8Ni6xmNEBfv PUfgu7dID9kTEgBFse8FpTtHYBr+F7Q8rLla32F4FLiqIelZKXbiPGdj4xN/Whk/HUn+ 1jzfqoZhDkVgMk3QqxSCCTPlS05mM1JSIj2v62EY570ERjCTY1IOXFTfqyfRbwXrINux HtjGnlo0vfYWKVx8j1RZ98vLpAZZav5RLZ7cscgrxbSC3MX52xWCTh0yed55scwbIAuC kJpQ== ARC-Authentication-Results: i=1; mx.google.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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id k6-20020a170906680600b009868469470esi942436ejr.491.2023.06.16.16.17.59; Fri, 16 Jun 2023 16:18:00 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 14E4D68C1BC; Sat, 17 Jun 2023 02:17:57 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-02-2.mymagenta.at (mail-02-2.mymagenta.at [80.109.253.249]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A541768BF73 for ; Sat, 17 Jun 2023 02:17:50 +0300 (EEST) Received: from [192.168.232.136] (helo=ren-mail-psmtp-mg02.) by mail-02.mymagenta.at with esmtp (Exim 4.93) (envelope-from ) id 1qAHoX-00Fbvp-92 for ffmpeg-devel@ffmpeg.org; Sat, 17 Jun 2023 00:21:05 +0200 Received: from localhost ([84.115.40.24]) by ren-mail-psmtp-mg02. with ESMTP id AHoYqebx5bZLDAHoYqtm6J; Sat, 17 Jun 2023 00:21:06 +0200 X-Env-Mailfrom: michael@niedermayer.cc X-Env-Rcptto: ffmpeg-devel@ffmpeg.org X-SourceIP: 84.115.40.24 X-CNFS-Analysis: v=2.4 cv=Ufwy9IeN c=1 sm=1 tr=0 ts=648ce052 a=4thelYDX6rwh+ygQwvsI+Q==:117 a=4thelYDX6rwh+ygQwvsI+Q==:17 a=MKtGQD3n3ToA:10 a=1oJP67jkp3AA:10 a=GEAsPZ9sns4A:10 a=ezWIuvjslLAyMOsoAWUA:9 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Sat, 17 Jun 2023 00:20:46 +0200 Message-Id: <20230616222048.6562-4-michael@niedermayer.cc> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230616222048.6562-1-michael@niedermayer.cc> References: <20230616222048.6562-1-michael@niedermayer.cc> X-CMAE-Envelope: MS4xfCAT7N5wgBO4c9DxY5d39U++tGHNrkiTqhLtZEA5CURkVpHzL2/pZDtnPtfMpk8zkAN/9T2Lv8EGCzCIzs4slj+1ig6/KF0b9v32NFgrh5sh9Xel0iUA YFvuAJfK2b2MiWQ6r9Ps/nHQCArx8jeZvJTdvqvxWrj3tabNiSayNElowaZ59DH9IMSYNqDmRFZ7JQ== Subject: [FFmpeg-devel] [PATCH 3/5] avcodec/kbdwin: Support arbitrary sized windows X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 3vKsmiMl8taS Signed-off-by: Michael Niedermayer --- libavcodec/kbdwin.c | 22 ++++++++++++++-------- libavcodec/kbdwin.h | 8 +++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/libavcodec/kbdwin.c b/libavcodec/kbdwin.c index 163f9d5251..82755874d4 100644 --- a/libavcodec/kbdwin.c +++ b/libavcodec/kbdwin.c @@ -19,20 +19,23 @@ #include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavutil/attributes.h" +#include "libavutil/mem.h" #include "kbdwin.h" -av_cold static void kbd_window_init(float *float_window, int *int_window, float alpha, int n) +av_cold static int kbd_window_init(float *float_window, int *int_window, float alpha, int n) { int i; double sum = 0.0, tmp; double scale = 0.0; - double temp[FF_KBD_WINDOW_MAX / 2 + 1]; + double temp_small[FF_KBD_WINDOW_MAX / 2 + 1]; + double *temp= n<=FF_KBD_WINDOW_MAX ? temp_small : av_malloc((n/2+1) * sizeof(*temp)); double alpha2 = 4 * (alpha * M_PI / n) * (alpha * M_PI / n); - av_assert0(n <= FF_KBD_WINDOW_MAX); + if (!temp) + return AVERROR(ENOMEM); for (i = 0; i <= n / 2; i++) { - tmp = i * (n - i) * alpha2; + tmp = alpha2 * i * (n - i); temp[i] = av_bessel_i0(sqrt(tmp)); scale += temp[i] * (1 + (i && i X-Patchwork-Id: 42165 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp1897516pzb; Fri, 16 Jun 2023 16:50:36 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6A2qf8oDO+xhQIvSjWQS+CjQi841RsqTmpCzYRnjn+W29ocsI7O7bzRSIRvtyor1d7taOD X-Received: by 2002:a17:907:783:b0:982:79fa:4532 with SMTP id xd3-20020a170907078300b0098279fa4532mr3401825ejb.53.1686959436347; Fri, 16 Jun 2023 16:50:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686959436; cv=none; d=google.com; s=arc-20160816; b=GnUG7ff2biR21zms2qF6GgWEPhPpf4VJftPf8UnB+NDo3pe+UxCzHSoHah4nyPuYk3 EfLPJhH4Anx1YnobjLVWGIzRA3+g4WnWyLT6ZYIZSdu6JbofyVjPMi8HcKRmeznKq6X3 Xwq6NmZ8r4sFmM6VMHJdxr+5SaPc0MqBxDAg9mJMzMn+2OhWvHogTK12X3Bi5GA+07fy MViSObnwGhKByksXPjpKc5M2fPDbMTQDMVNLjL1rsytxGXjoIANpD7KyScQXaQoMSoWV iUIAidKsQ5e8HVFoPe1t5NV9rsuhHqT2C0rXAYHxpeTiRl7Fcsx+xVf8iA/H+7p/7k8e /1fg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:delivered-to; bh=pKIxr2jaLCpdc6UNBqWW+rOySCazLLQm4+SFFxdi28w=; b=eDHWUJ9GbKTOb1wYwLGhbfCb1hc5TRG6ya5Clr4w9d6+zDbNIKUkrJhy9Nn/MNY6tn isN7eSEUtAeOYdj6fgYp/GQDnyoEi7h0v5wod3OLGkc1W3WLaRBPC6GDXdSF2ZwjSaQm 2NMPcGBJItETO8oViwqDXX8SDMmzWvNky11VbR9OJFaPQf/mbKNAhWTEywh3F8EEGj7g Hmq54c169r5SP6dmTaYDKl73315zhuv8nLyTixQ0Z5iVStAvBsOd6MNtoTzUnDBb3yHx TLP59xmW0wq/cgoKJxc6F9n1Vb73f987CyFwxJ1bOzP9Z7V4DzO2K1bUa5tVRlzACFL3 FBLQ== ARC-Authentication-Results: i=1; mx.google.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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id ot10-20020a170906ccca00b0098700f22ba7si611255ejb.447.2023.06.16.16.50.35; Fri, 16 Jun 2023 16:50:36 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 5164768C2A6; Sat, 17 Jun 2023 02:50:32 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-01-1.mymagenta.at (mail-01-1.mymagenta.at [80.109.253.246]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3301C68B9FC for ; Sat, 17 Jun 2023 02:50:26 +0300 (EEST) Received: from [192.168.232.136] (helo=ren-mail-psmtp-mg02.) by mail-01.mymagenta.at with esmtp (Exim 4.93) (envelope-from ) id 1qAHoW-0098iq-PM for ffmpeg-devel@ffmpeg.org; Sat, 17 Jun 2023 00:21:04 +0200 Received: from localhost ([84.115.40.24]) by ren-mail-psmtp-mg02. with ESMTP id AHoYqebx8bZLDAHoYqtm6K; Sat, 17 Jun 2023 00:21:06 +0200 X-Env-Mailfrom: michael@niedermayer.cc X-Env-Rcptto: ffmpeg-devel@ffmpeg.org X-SourceIP: 84.115.40.24 X-CNFS-Analysis: v=2.4 cv=Ufwy9IeN c=1 sm=1 tr=0 ts=648ce052 a=4thelYDX6rwh+ygQwvsI+Q==:117 a=4thelYDX6rwh+ygQwvsI+Q==:17 a=zB7U8cq8O5-ZcbuM:21 a=MKtGQD3n3ToA:10 a=1oJP67jkp3AA:10 a=GEAsPZ9sns4A:10 a=zZmhFHvFYi-6r2WuerYA:9 a=waQwdFMgd5enuITLDeoS:22 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Sat, 17 Jun 2023 00:20:47 +0200 Message-Id: <20230616222048.6562-5-michael@niedermayer.cc> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230616222048.6562-1-michael@niedermayer.cc> References: <20230616222048.6562-1-michael@niedermayer.cc> X-CMAE-Envelope: MS4xfCAT7N5wgBO4c9DxY5d39U++tGHNrkiTqhLtZEA5CURkVpHzL2/pZDtnPtfMpk8zkAN/9T2Lv8EGCzCIzs4slj+1ig6/KF0b9v32NFgrh5sh9Xel0iUA YFvuAJfK2b2MiWQ6r9Ps/nHQCArx8jeZvJTdvqvxWrj3tabNiSayNElowaZ59DH9IMSYNqDmRFZ7JQ== Subject: [FFmpeg-devel] [PATCH 4/5] avcodec: Rename ff_kbd_window_init() as it will be needed from outside libavcodec X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: QdrA5VVlwIpa Signed-off-by: Michael Niedermayer --- libavcodec/aacdec_template.c | 8 ++++---- libavcodec/aactab.c | 4 ++-- libavcodec/ac3dec.c | 2 +- libavcodec/ac3enc_fixed.c | 2 +- libavcodec/ac3enc_float.c | 2 +- libavcodec/dolby_e.c | 4 ++-- libavcodec/kbdwin.c | 4 ++-- libavcodec/kbdwin.h | 6 +++--- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libavcodec/aacdec_template.c b/libavcodec/aacdec_template.c index 444dc4fa9d..2385ea58b0 100644 --- a/libavcodec/aacdec_template.c +++ b/libavcodec/aacdec_template.c @@ -1159,8 +1159,8 @@ static av_cold void aac_static_table_init(void) 352); // window initialization - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME(aac_kbd_long_960), 4.0, 960); - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME(aac_kbd_short_120), 6.0, 120); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME(aac_kbd_long_960), 4.0, 960); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME(aac_kbd_short_120), 6.0, 120); #if !USE_FIXED AAC_RENAME(ff_sine_window_init)(AAC_RENAME(sine_960), 960); @@ -1168,8 +1168,8 @@ static av_cold void aac_static_table_init(void) AAC_RENAME(ff_init_ff_sine_windows)(9); ff_aac_float_common_init(); #else - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME2(aac_kbd_long_1024), 4.0, 1024); - AAC_RENAME(ff_kbd_window_init)(AAC_RENAME2(aac_kbd_short_128), 6.0, 128); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME2(aac_kbd_long_1024), 4.0, 1024); + AAC_RENAME(avpriv_kbd_window_init)(AAC_RENAME2(aac_kbd_short_128), 6.0, 128); init_sine_windows_fixed(); #endif diff --git a/libavcodec/aactab.c b/libavcodec/aactab.c index 0f4941d5df..d0006eac35 100644 --- a/libavcodec/aactab.c +++ b/libavcodec/aactab.c @@ -48,8 +48,8 @@ DECLARE_ALIGNED(32, float, ff_aac_kbd_short_128)[128]; static av_cold void aac_float_common_init(void) { - ff_kbd_window_init(ff_aac_kbd_long_1024, 4.0, 1024); - ff_kbd_window_init(ff_aac_kbd_short_128, 6.0, 128); + avpriv_kbd_window_init(ff_aac_kbd_long_1024, 4.0, 1024); + avpriv_kbd_window_init(ff_aac_kbd_short_128, 6.0, 128); ff_init_ff_sine_windows(10); ff_init_ff_sine_windows(7); } diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c index fc0cbeb493..8c63f015f4 100644 --- a/libavcodec/ac3dec.c +++ b/libavcodec/ac3dec.c @@ -228,7 +228,7 @@ static av_cold int ac3_decode_init(AVCodecContext *avctx) if ((ret = av_tx_init(&s->tx_256, &s->tx_fn_256, IMDCT_TYPE, 1, 256, &scale, 0))) return ret; - AC3_RENAME(ff_kbd_window_init)(s->window, 5.0, 256); + AC3_RENAME(avpriv_kbd_window_init)(s->window, 5.0, 256); ff_bswapdsp_init(&s->bdsp); #if (USE_FIXED) diff --git a/libavcodec/ac3enc_fixed.c b/libavcodec/ac3enc_fixed.c index 88dfd66b91..079a43dc39 100644 --- a/libavcodec/ac3enc_fixed.c +++ b/libavcodec/ac3enc_fixed.c @@ -82,7 +82,7 @@ static av_cold int ac3_fixed_mdct_init(AC3EncodeContext *s) if (!iwin) return AVERROR(ENOMEM); - ff_kbd_window_init(fwin, 5.0, AC3_BLOCK_SIZE); + avpriv_kbd_window_init(fwin, 5.0, AC3_BLOCK_SIZE); for (int i = 0; i < AC3_BLOCK_SIZE; i++) iwin[i] = lrintf(fwin[i] * (1 << 22)); diff --git a/libavcodec/ac3enc_float.c b/libavcodec/ac3enc_float.c index ae351a535e..9664adbf63 100644 --- a/libavcodec/ac3enc_float.c +++ b/libavcodec/ac3enc_float.c @@ -92,7 +92,7 @@ static av_cold int ac3_float_mdct_init(AC3EncodeContext *s) return AVERROR(ENOMEM); } - ff_kbd_window_init(window, 5.0, AC3_BLOCK_SIZE); + avpriv_kbd_window_init(window, 5.0, AC3_BLOCK_SIZE); s->mdct_window = window; return av_tx_init(&s->tx, &s->tx_fn, AV_TX_FLOAT_MDCT, 0, diff --git a/libavcodec/dolby_e.c b/libavcodec/dolby_e.c index 921c33f3ba..b8dac0fa3f 100644 --- a/libavcodec/dolby_e.c +++ b/libavcodec/dolby_e.c @@ -1200,7 +1200,7 @@ static av_cold void init_tables(void) gain_tab[i] = exp2f((i - 960) / 64.0f); // short 1 - ff_kbd_window_init(window, 3.0f, 128); + avpriv_kbd_window_init(window, 3.0f, 128); for (i = 0; i < 128; i++) window[128 + i] = window[127 - i]; @@ -1227,7 +1227,7 @@ static av_cold void init_tables(void) window[1088 + i] = 1.0f; // long - ff_kbd_window_init(window + 1408, 3.0f, 256); + avpriv_kbd_window_init(window + 1408, 3.0f, 256); for (i = 0; i < 640; i++) window[1664 + i] = 1.0f; for (i = 0; i < 256; i++) diff --git a/libavcodec/kbdwin.c b/libavcodec/kbdwin.c index 82755874d4..eacdb46774 100644 --- a/libavcodec/kbdwin.c +++ b/libavcodec/kbdwin.c @@ -56,12 +56,12 @@ av_cold static int kbd_window_init(float *float_window, int *int_window, float a return 0; } -av_cold int ff_kbd_window_init(float *window, float alpha, int n) +av_cold int avpriv_kbd_window_init(float *window, float alpha, int n) { return kbd_window_init(window, NULL, alpha, n); } -av_cold int ff_kbd_window_init_fixed(int32_t *window, float alpha, int n) +av_cold int avpriv_kbd_window_init_fixed(int32_t *window, float alpha, int n) { return kbd_window_init(NULL, window, alpha, n); } diff --git a/libavcodec/kbdwin.h b/libavcodec/kbdwin.h index 452fc46596..0cb2073c5f 100644 --- a/libavcodec/kbdwin.h +++ b/libavcodec/kbdwin.h @@ -22,7 +22,7 @@ #include /** - * Maximum window size for ff_kbd_window_init. + * Maximum window size for avpriv_kbd_window_init. */ #define FF_KBD_WINDOW_MAX 1024 @@ -34,7 +34,7 @@ * * @return if n is larger than FF_KBD_WINDOW_MAX then AVERROR(ENOMEM) is possible */ -int ff_kbd_window_init(float *window, float alpha, int n); -int ff_kbd_window_init_fixed(int32_t *window, float alpha, int n); +int avpriv_kbd_window_init(float *window, float alpha, int n); +int avpriv_kbd_window_init_fixed(int32_t *window, float alpha, int n); #endif /* AVCODEC_KBDWIN_H */ From patchwork Fri Jun 16 22:20:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Niedermayer X-Patchwork-Id: 42166 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp1917435pzb; Fri, 16 Jun 2023 17:38:45 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5r3hHlJNYG3D7TCluB30qtKiKInRs+G6nC3NiK8FGILM9wfFE3TyNGZmZ4gA5ec4wqhBHC X-Received: by 2002:a17:907:94cf:b0:971:fa86:27e with SMTP id dn15-20020a17090794cf00b00971fa86027emr3225999ejc.16.1686962325719; Fri, 16 Jun 2023 17:38:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686962325; cv=none; d=google.com; s=arc-20160816; b=pGMvAVg6o9v3g9j/R2P2T1Dg7nTdgjViTdZtvPcCLNeSa7ip2+CBEIFJkBvEP5AHXU DfVRYQ4ALGez4JFD/BZrms9OrF85QnxaQ8JMizbIdM6lm9XsaeNjtAdYQDxcQcWzTYJG 6TKJpRHNktDYSl105KuShjUSlyeA50+kSxRW11yCh+we9ihIVU4vTDkfQhXc9wSk8fAU HCI6MIAD6pXRxISGp1cXy2lK3DVP7LOsekNZRRXx4q3rMjzG6UqhFRqDC21S3XFtWxKj Xr9XOI1ooV7vgHW/Pw/arb9w6i88a7P7vnqSl8jrNHdDoq52dSzqXNGOA1daTvZQ8vr7 lKqQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:delivered-to; bh=CflVbgCdjzmJrqJp8zXlrOvbDN3rXaHyKZ/qj+mjxyU=; b=0qQ/foUxKUKZxbVMyC4RJ6Vk9sq/aogsZkYVvceE8Cj4+xne04ihV94sp98mX8J73y coCV/UxHkXvo6vq8bXjhyYEIvqtckww+5U7TXbmNCgi/j1tsCWzJDGMD0IhtZmMv6MHn /UrpJK1+AqT7xdxmTKsb22S/glFXwza5PK7g/tU4vbea0gxgfjBu20rXH6Y0TvFE1N4a j0pwpTEEH3E0YfkKWccEzsgp+KO8TjxinImVosq49rrLce+EXFN3oIULYjacNtqVkXY2 yQpTOTOaNNRjJTyEyOgBBde6dOA0RADqk5exZ8h5RwWU1rCLnXTRzW9Tiq7Q4IL2C84a eNNA== ARC-Authentication-Results: i=1; mx.google.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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id n5-20020a17090695c500b0098770b8882dsi199887ejy.1030.2023.06.16.17.38.45; Fri, 16 Jun 2023 17:38:45 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 189BB68C337; Sat, 17 Jun 2023 03:38:41 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-01-1.mymagenta.at (mail-01-1.mymagenta.at [80.109.253.246]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B933168C19B for ; Sat, 17 Jun 2023 03:38:34 +0300 (EEST) Received: from [192.168.232.135] (helo=ren-mail-psmtp-mg01.) by mail-01.mymagenta.at with esmtp (Exim 4.93) (envelope-from ) id 1qAHoX-0098OM-0b for ffmpeg-devel@ffmpeg.org; Sat, 17 Jun 2023 00:21:05 +0200 Received: from localhost ([84.115.40.24]) by ren-mail-psmtp-mg01. with ESMTP id AHoWqtJlNOG5ZAHoWqNC7s; Sat, 17 Jun 2023 00:21:04 +0200 X-Env-Mailfrom: michael@niedermayer.cc X-Env-Rcptto: ffmpeg-devel@ffmpeg.org X-SourceIP: 84.115.40.24 X-CNFS-Analysis: v=2.4 cv=KJo5sHJo c=1 sm=1 tr=0 ts=648ce050 a=4thelYDX6rwh+ygQwvsI+Q==:117 a=4thelYDX6rwh+ygQwvsI+Q==:17 a=MKtGQD3n3ToA:10 a=1oJP67jkp3AA:10 a=GEAsPZ9sns4A:10 a=yCoMtmpDAAAA:8 a=_FAQ27NxJJozcFkhqEgA:9 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Sat, 17 Jun 2023 00:20:48 +0200 Message-Id: <20230616222048.6562-6-michael@niedermayer.cc> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230616222048.6562-1-michael@niedermayer.cc> References: <20230616222048.6562-1-michael@niedermayer.cc> X-CMAE-Envelope: MS4xfHZUUrBnzRgkNDHs3OhJunusqsR5I1LoxHu3n1m+Tafq4zuNRI9CRaNTMep+E1jPuR1e8Xh0tw6RQ4b4tKTFwRofw2hDxes2abkrL3+aJXccPU0WWuw5 xlKlC75jsI0BbncDilqnOo7XkLH565QRcH16P70F28kj20kt6BdO+uH5/BCFZSewPikgvAzGBZDpGw== Subject: [FFmpeg-devel] [PATCH 5/5] avformat: add sdr support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 8We72Miq5XxL Signed-off-by: Michael Niedermayer --- configure | 5 + doc/demuxers.texi | 71 ++ libavformat/Makefile | 2 + libavformat/allformats.c | 2 + libavformat/sdrdemux.c | 1710 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1790 insertions(+) create mode 100644 libavformat/sdrdemux.c diff --git a/configure b/configure index 4ac7cc6c0b..48b4693de5 100755 --- a/configure +++ b/configure @@ -269,6 +269,7 @@ External library support: --enable-libshine enable fixed-point MP3 encoding via libshine [no] --enable-libsmbclient enable Samba protocol via libsmbclient [no] --enable-libsnappy enable Snappy compression, needed for hap encoding [no] + --enable-libsoapysdr enable SoapySDR, needed for connecting to SDR HW [no] --enable-libsoxr enable Include libsoxr resampling [no] --enable-libspeex enable Speex de/encoding via libspeex [no] --enable-libsrt enable Haivision SRT protocol via libsrt [no] @@ -1888,6 +1889,7 @@ EXTERNAL_LIBRARY_LIST=" libshine libsmbclient libsnappy + libsoapysdr libsoxr libspeex libsrt @@ -3540,6 +3542,8 @@ rtsp_muxer_select="rtp_muxer http_protocol rtp_protocol rtpenc_chain" sap_demuxer_select="sdp_demuxer" sap_muxer_select="rtp_muxer rtp_protocol rtpenc_chain" sdp_demuxer_select="rtpdec" +sdr_demuxer_deps="libsoapysdr" +sdrfile_demuxer_deps="libsoapysdr" smoothstreaming_muxer_select="ismv_muxer" spdif_demuxer_select="adts_header" spdif_muxer_select="adts_header" @@ -6776,6 +6780,7 @@ enabled libshine && require_pkg_config libshine shine shine/layer3.h sh enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || require libsmbclient libsmbclient.h smbc_init -lsmbclient; } enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ +enabled libsoapysdr && require libsoapysdr SoapySDR/Device.h SoapySDRDevice_enumerate -lSoapySDR enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr enabled libssh && require_pkg_config libssh libssh libssh/sftp.h sftp_init enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 2d33b47a56..fa8da99911 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -899,6 +899,77 @@ the script is directly played, the actual times will match the absolute timestamps up to the sound controller's clock accuracy, but if the user somehow pauses the playback or seeks, all times will be shifted accordingly. +@section sdr / sdrfile + +Software Defined Radio Demuxer. The sdr demuxer will demux radio streams +through the use of a libsoapy compatible software defined radio. sdrfile +will do the same from a file. +The short seek forward and backward commands can be used to select the next and +previous radio stations. Seeking to specific radio stations and similar will be +supported in the future. + +@table @option + +@item block_size +size of FFT to use, leave at 0 to choose automatically + +@item mode + +@table @samp + +@item single_mode +Demodulate 1 station + +@item all_mode +Demodulate all stations in view + +@end table + +@item station_freq +Initial station to pick, if multiple are probed. + +@item driver +libsoapy driver to use. Leave empty to attempt autodetection. + +@item sdr_sr +SDR sample rate, this must be a value supported by the hardware. Leave 0 to attempt +autodetection + +@item sdr_freq +initial SDR center frequency, this must be a value supported by the hardware + +@item min_freq +Minimum frequency, leave at 0 for autodetection + +@item max_freq +Maximum frequency, leave at 0 for autodetection + +@item dumpurl +URL to dump RAW SDR stream to, this can be played back later with sdrfile. Useful +for debuging. + +@item kbd_alpha +Kaiser Bessel derived window parameter + +@item am_mode +AM Demodulation method. Several different methods are supported. +@table @samp +@item am_inphase +Demodulate mono signal that is in phase with the carrier. This works well if there is one +transmitter at the frequency and there is nothing disturbing the signal. + +@item am_midside +Demodulate AM into a stereo signal so that the mid is from the in phase and side is from the +quadrature signal. This can be used if there are Multiple transmitters that transmit at the same +frequency. Or just for fun. + +@item am_envelope +Demodulate mono signal that is basically the energy of the spectrum. + +@end table + +@end table + @section tedcaptions JSON captions used for @url{http://www.ted.com/, TED Talks}. diff --git a/libavformat/Makefile b/libavformat/Makefile index 05434a0f82..1efb51c613 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -528,6 +528,8 @@ OBJS-$(CONFIG_SCC_MUXER) += sccenc.o OBJS-$(CONFIG_SCD_DEMUXER) += scd.o OBJS-$(CONFIG_SDNS_DEMUXER) += sdns.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o +OBJS-$(CONFIG_SDR_DEMUXER) += sdrdemux.o +OBJS-$(CONFIG_SDRFILE_DEMUXER) += sdrdemux.o OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o OBJS-$(CONFIG_SDX_DEMUXER) += sdxdec.o pcm.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 96443a7272..a0393e6523 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -410,6 +410,8 @@ extern const FFOutputFormat ff_scc_muxer; extern const AVInputFormat ff_scd_demuxer; extern const AVInputFormat ff_sdns_demuxer; extern const AVInputFormat ff_sdp_demuxer; +extern const AVInputFormat ff_sdr_demuxer; +extern const AVInputFormat ff_sdrfile_demuxer; extern const AVInputFormat ff_sdr2_demuxer; extern const AVInputFormat ff_sds_demuxer; extern const AVInputFormat ff_sdx_demuxer; diff --git a/libavformat/sdrdemux.c b/libavformat/sdrdemux.c new file mode 100644 index 0000000000..f31b16e07a --- /dev/null +++ b/libavformat/sdrdemux.c @@ -0,0 +1,1710 @@ +/* + * SDR Demuxer / Demodulator + * Copyright (c) 2023 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * + * + */ + +/** + * TODO + * * FM + * * DAB + * * DVB + * * Improve probing using multiple detections and differential detection + * + */ + +#include +#include +#include +#include +#include +#include +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/fifo.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "libavutil/thread.h" +#include "libavutil/tx.h" +#include "libavcodec/kbdwin.h" +#include "avformat.h" +#include "demux.h" +#include "internal.h" + +#ifdef SYN_TEST +#include "libavutil/lfg.h" +#endif + +#define FREQ_BITS 22 +#define TIMEBASE ((48000ll / 128) << FREQ_BITS) +#define MAX_CHANNELS 4 + +#define STATION_TIMEOUT 100 ///< The number of frames after which a station is removed if it was not detected + +/* + * 100 detects nothing + * 50 detects a good bit but not all + */ +#define AM_THRESHOLD 40 + +#define AM_MAX23 0.03 //smaller causes failure on synthetic signals +#define AM_MAX4 0.01 + +#define FM_THRESHOLD .8 //TODO adjust + +#define INDEX2F(INDEX) (((INDEX) - sdr->block_size + 0.5) * 0.5 * sdr->sdr_sample_rate / sdr->block_size + sdr->block_center_freq) +#define F2INDEX(F) ((( F) - sdr->block_center_freq) * 2 * sdr->block_size / sdr->sdr_sample_rate + sdr->block_size - 0.5) + +typedef enum Mode { + SingleStationMode, //< demodulate 1 stations for Radio like usage + AllStationMode, //< demodulate all stations in current input + ModeNB, +} Mode; + +typedef enum AMMode { + AMMidSide, + AMLeftRight, + AMInPhase, + AMEnvelope, + AMModeNB, +} AMMode; + +typedef enum Modulation { + AM, + FM, + OFDM_DQPSK, //DAB + //QAM, PSK, ... +} Modulation; + +typedef struct Station { + char *name; + enum Modulation modulation; + double frequency; + int64_t bandwidth; + float score; + int timeout; //since how many blocks was this detectable but not detected + int multiplex_index; //DAB can have multiple stations on one frequency + + struct SDRStream *stream; +} Station; + +typedef struct SDRStream { + AVTXContext *ifft_ctx; + av_tx_fn ifft; + int block_size; + int processing_index; + float *out_buf; + AVComplexFloat *block; + AVComplexFloat *iblock; + AVComplexFloat *icarrier; + float *window; + Station *station; + + int frame_size; + int frame_buffer_line; + uint8_t *frame_buffer; +} SDRStream; + +typedef struct SDRContext { + const AVClass *class; + AVFormatContext *avfmt; + SoapySDRDevice *soapy; + SoapySDRStream *soapyRxStream; + AVTXContext *fft_ctx; + av_tx_fn fft; + Mode mode; + AVRational fps; + char *driver_name; + char *dump_url; + int fileheader_size; + AVIOContext *dump_avio; + Station **station; + int nb_stations; + int width, height; + int single_ch_audio_st_index; + int64_t freq; + int64_t min_freq; + int64_t max_freq; + int64_t min_center_freq; + int64_t max_center_freq; + int sdr_sample_rate; + int64_t bandwidth; + int64_t last_pts; + int64_t pts; + int block_size; + int kbd_alpha; + AVComplexFloat *windowed_block; + int64_t block_center_freq; ///< center frequency the current block contains + int64_t station_freq; + + int am_mode; ///< AMMode but using int for generic option access + + pthread_t soapy_thread; + int thread_started; + pthread_mutex_t mutex; ///< Mutex to protect common variable between mainthread and soapy_thread, and also to protect soapy from concurrent calls + AVFifo *empty_block_fifo; + AVFifo *full_block_fifo; + atomic_int close_requested; + int64_t wanted_freq; ///< center frequency we want the hw to provide next + int seek_direction; ///< if a seek is requested this is -1 or 1 otherwise 0 + int skip_probe; + + AVComplexFloat *block; + float *len2block; + float *window; + + int missing_streams; +} SDRContext; + +typedef struct ModulationDescriptor { + const char *name; + enum Modulation modulation; + enum AVMediaType media_type; + + /** + * Scan all of the current sdr->block and call create_station() for each found station + */ + int (*probe)(SDRContext *sdr); + + /** + * Demodulate given station into packet + */ + int (*demodulate)(SDRContext *sdr, int stream_index, AVPacket *pkt); +} ModulationDescriptor; + +typedef struct FIFOElement { + int64_t center_frequency; + AVComplexFloat *halfblock; +} FIFOElement; + +static float len2(AVComplexFloat c) +{ + return c.re*c.re + c.im*c.im; +} + +static int create_station(SDRContext *sdr, enum Modulation modulation, double freq, int64_t bandwidth, float score) { + Station *station; + void *tmp; + int i; + double best_distance = INT64_MAX; + Station *best_station = NULL; + int best_station_index = -1; + float drift = modulation == AM ? sdr->sdr_sample_rate / (float)sdr->block_size + 1 : (bandwidth/4.0); + + for (i=0; inb_stations; i++) { + double delta = fabs(sdr->station[i]->frequency - freq); + // Station already added, or we have 2 rather close stations + //FIXME we want to make sure that the stronger station is not skiped but we also dont want to add a station twice + if (modulation == sdr->station[i]->modulation && delta < best_distance) { + best_distance = delta; + best_station = sdr->station[i]; + best_station_index = i; + } + } + if (best_station) { + if (best_distance <= drift) + return best_station_index; + } + + tmp = av_realloc_array(sdr->station, sdr->nb_stations+1, sizeof(*sdr->station)); + if (!tmp) + return AVERROR(ENOMEM); + sdr->station = tmp; + + station = av_mallocz(sizeof(*station)); + if (!station) + return AVERROR(ENOMEM); + + sdr->station[sdr->nb_stations++] = station; + + station->modulation = modulation; + station->frequency = freq; + station->bandwidth = bandwidth; + station->score = score; + + // if we just found a new station lets also probe the next frame + sdr->skip_probe = 0; + + av_log(sdr, AV_LOG_INFO, "create_station %d f:%f bw:%"PRId64" score: %f\n", modulation, freq, bandwidth, score); + + return sdr->nb_stations - 1; +} + +static void free_station(Station *station) +{ + av_freep(&station->name); + if (station->stream) + station->stream->station = NULL; + av_free(station); +} + +/** + * remove stations which we no longer receive well + * Especially with AM and weather conditions stations disapear, this keeps things a bit more tidy + */ +static void decay_stations(SDRContext *sdr) +{ + for (int i=0; inb_stations; i++) { + Station *station = sdr->station[i]; + + if (station->frequency - station->bandwidth/2 < sdr->block_center_freq - sdr->bandwidth/2 || + station->frequency + station->bandwidth/2 > sdr->block_center_freq + sdr->bandwidth/2) + continue; + if (station->stream) + continue; + + if (station->timeout++ > STATION_TIMEOUT) { + free_station(station); + sdr->station[i] = sdr->station[--sdr->nb_stations]; + } + } +} + +static void probe_common(SDRContext *sdr) +{ + for(int i = 0; i < 2*sdr->block_size; i++) { + sdr->len2block[i] = len2(sdr->block[i]); + } +} + +// simple and dumb implementation for max we could do it faster but it doesnzt matter ATM +static float max_in_range(SDRContext *sdr, unsigned start, unsigned end) +{ + float max = sdr->len2block[start]; + for (; start <= end; start++) + max = fmax(max, sdr->len2block[start]); + return max; +} + +static double find_peak(SDRContext *sdr, const float *data, int index, int len) +{ + double y[3], b, a; + + if (index == 0) { + index = 1; + } else if (index >= len - 1) + index = len - 2; + + //We use a simple quadratic to find the maximum at higher precission than the available samples + //ax^2 + bx + c + //dy/dx = 2ax + b = 0 + y[0] = data[index-1]; + y[1] = data[index]; + y[2] = data[index+1]; + + b = (y[2] - y[0]) * 0.5; + a = (y[0] + y[2]) * 0.5 - y[1]; + + //This should not happen + if (a >= 0.0) + return INT_MIN; + + return index -0.5 * b / a; + //TODO theres some simplification possible above but the fm_probe() using this needs to be tuned first so we dont optimize the wrong algorithm +} + +static double find_peak_macleod(SDRContext *sdr, const AVComplexFloat *data, int index, int len, float *phase) { + AVComplexFloat ref; + double r0, r1, r2, rd, g; + double ab[16][2] = { + {1.525084, 3.376388}, {1.773260, 3.129280}, {1.970200, 2.952300}, {2.122700, 2.825800}, + {2.243600, 2.731620}, {2.341880, 2.658740}, {2.423310, 2.600750}, {2.492110, 2.553360}, + {2.551000, 2.513930}, {2.602300, 2.480400}, {2.646880, 2.451880}, {2.686200, 2.427200}, + {2.721880, 2.405120}, {2.753600, 2.385800}, {2.781800, 2.368900}, {2.808580, 2.352920}, + }; + + double a = ab[sdr->kbd_alpha-1][0]; + double b = ab[sdr->kbd_alpha-1][1]; + + if (index == 0) { + index = 1; + } else if (index >= len - 1) + index = len - 2; + + /* Baed on Macleod, M.D., "Fast Nearly ML Estimation of the Parameters + * of Real or Complex Single Tones or Resolved Multiple Tones," + * IEEE Trans. Sig. Proc. Vol 46 No 1, + * January 1998, pp141-148. + * + * We use the 3 point estimator without corrections, the corrections + * provide insignificant performance increase. We add compensation due to + * the window used, this performs substantially better than the original + * with a rectangular window. + */ + ref = data[index]; + r0 = data[index-1].re * ref.re + data[index-1].im * ref.im; + r1 = ref.re * ref.re + ref.im * ref.im; + r2 = data[index+1].re * ref.re + data[index+1].im * ref.im; + rd = 2*r1 + r0 + r2; + if (r2 > r1 || r0 > r1 || rd <=0) // rounding and float numeric issues + return -1; + g = (r0-r2) / rd + DBL_MIN; + g = (sqrt(a*a + 8*g*g)-a)/(b*g); + + if (phase) { + AVComplexFloat t; + if (g < 0){ + t.re = ref.re * (1+g) + data[index-1].re * g; + t.im = ref.im * (1+g) + data[index-1].im * g; + } else { + t.re = ref.re * (1-g) - data[index+1].re * g; + t.im = ref.im * (1-g) - data[index+1].im * g; + } + *phase = atan2(t.im, t.re) - M_PI*g; + } + + return index + g; +} + +static int probe_am(SDRContext *sdr) +{ + int i; + int bandwidth_f = 6000; + int half_bw_i = bandwidth_f * (int64_t)sdr->block_size / sdr->sdr_sample_rate; + double avg = 0; + + if (2*half_bw_i > 2*sdr->block_size) + return 0; + + for (i = 0; i<2*half_bw_i; i++) + avg += sdr->len2block[i]; + + for (i = half_bw_i; i<2*sdr->block_size - half_bw_i; i++) { + float mid = sdr->len2block[i]; + double score; + avg += sdr->len2block[i + half_bw_i]; + score = half_bw_i * mid / (avg - mid); + avg -= sdr->len2block[i - half_bw_i]; + //TODO also check for symmetry in the spectrum + if (mid > 0 && score > AM_THRESHOLD && + sdr->len2block[i - 1] < mid && sdr->len2block[i + 1] <= mid && + sdr->len2block[i - 2] < mid*AM_MAX23 && sdr->len2block[i + 2] < mid*AM_MAX23 && + sdr->len2block[i - 3] < mid*AM_MAX23 && sdr->len2block[i + 3] < mid*AM_MAX23 + ){ + if (max_in_range(sdr, i-half_bw_i, i-4) < mid*AM_MAX4 && + max_in_range(sdr, i+4, i+half_bw_i) < mid*AM_MAX4) { + int station_index; + double peak_i = find_peak_macleod(sdr, sdr->block, i, 2*sdr->block_size, NULL); + if (peak_i < 0) + continue; + if (fabs(peak_i-i) > 1.0) { + av_log(sdr->avfmt, AV_LOG_WARNING, "peak detection failure\n"); + continue; + } + + station_index = create_station(sdr, AM, INDEX2F(peak_i), bandwidth_f, score); + + if (station_index >= 0) { + Station *station = sdr->station[station_index]; + station->timeout = 0; + } + } + } + } + + return 0; +} + +static int demodulate_am(SDRContext *sdr, int stream_index, AVPacket *pkt) +{ + AVStream *st = sdr->avfmt->streams[stream_index]; + SDRStream *sst = st->priv_data; + double freq = sst->station->frequency; + int64_t bandwidth = sst->station->bandwidth; + int index = lrint(F2INDEX(freq)); + int len = (bandwidth * 2ll * sdr->block_size + sdr->sdr_sample_rate/2) / sdr->sdr_sample_rate; + float *newbuf; + float scale; + int sample_rate = sdr->sdr_sample_rate * (int64_t)sst->block_size / sdr->block_size; + int ret, i; + int i_max; + double current_station_i; + double avg = 0; + float score; + float mid=0; + float limits[2] = {-0.0, 0.0}; + float clip = 1.0; + enum AMMode am_mode = sdr->am_mode; + +#define CARRIER_SEARCH 2 + if (index + len + CARRIER_SEARCH>= 2*sdr->block_size || + index - len - CARRIER_SEARCH < 0 || + 2*len + 1 > 2*sst->block_size) + return AVERROR(ERANGE); + + for(int i = index-CARRIER_SEARCH; ilen2block[i] = len2(sdr->block[i]); // we only update the array when probing so we need to compute this here + if (sdr->len2block[i] > sdr->len2block[i_max]) + i_max = i; + } + mid = sdr->len2block[i_max]; + + for (i = -len; i < len+1; i++) + avg += sdr->len2block[i + index]; + score = len * mid / (avg - mid); + //find optimal frequency for this block if we have a carrier + if (score > AM_THRESHOLD / 4) { + i_max = index; + + current_station_i = find_peak_macleod(sdr, sdr->block, i_max, 2*sdr->block_size, NULL); + if (current_station_i >= 0 && fabs(current_station_i - i_max) < 1.0) { + av_log(sdr->avfmt, AV_LOG_DEBUG, "adjusting frequency %f, max index: %ld\n", INDEX2F(current_station_i) - freq, lrint(F2INDEX(freq)) - index); + freq = INDEX2F(current_station_i); + index = lrint(F2INDEX(freq)); + } + } else { + // We have no carrier so we cannot decode Synchronously to a carrier + am_mode = AMEnvelope; + } + + newbuf = av_malloc(sizeof(*sst->out_buf) * 2 * sst->block_size); + if (!newbuf) + return AVERROR(ENOMEM); +#define SEPC 4 + + i = 2*len+1; + memcpy(sst->block, sdr->block + index - len, sizeof(*sst->block) * i); + memset(sst->block + i, 0, sizeof(*sst->block) * (2 * sst->block_size - i)); + + sst->ifft(sst->ifft_ctx, sst->iblock , sst->block, sizeof(AVComplexFloat)); + + if (am_mode == AMEnvelope) { + double vdotw = 0; + double wdot = 0; // could be precalculated + for (i = 0; i<2*sst->block_size; i++) { + float w = sst->window[i]; + float v = sqrt(len2(sst->iblock[i])); + sst->iblock[i].re = v; + sst->iblock[i].im = 0; + + vdotw += w*v; + wdot += w*w; + } + + vdotw /= wdot ; + for (i = 0; i<2*sst->block_size; i++) { + float w = sst->window[i]; + sst->iblock[i].re -= w*vdotw; + } + + scale = 0.9/vdotw; + } else { + // Synchronous demodulation + memset(sst->block, 0, sizeof(*sst->block) * i); + for (i = len-SEPC+1; iblock[i] = sdr->block[index + i - len]; + sst->ifft(sst->ifft_ctx, sst->icarrier, sst->block, sizeof(AVComplexFloat)); + + for (i = 0; i<2*sst->block_size; i++) { + AVComplexFloat c = sst->icarrier[i]; + AVComplexFloat s = sst->iblock[i]; + float w = sst->window[i]; + float den = w / (c.re*c.re + c.im*c.im); + av_assert0(c.re*c.re + c.im*c.im > 0); + + sst->iblock[i].re = (s.im*c.im + s.re*c.re) * den; + sst->iblock[i].im = (s.im*c.re - s.re*c.im) * den; + sst->iblock[i].re -= w; + } + scale = 0.9; + } + + for(int i = 0; i<2*sst->block_size; i++) { + av_assert0(isfinite(sst->iblock[i].re)); + av_assert0(isfinite(sst->iblock[i].im)); + limits[0] = FFMIN(limits[0], FFMIN(sst->iblock[i].re - sst->iblock[i].im, sst->iblock[i].re + sst->iblock[i].im)); + limits[1] = FFMAX(limits[1], FFMAX(sst->iblock[i].re - sst->iblock[i].im, sst->iblock[i].re + sst->iblock[i].im)); + } + av_assert1(FFMAX(limits[1], -limits[0]) >= 0); + scale = FFMIN(scale, 0.98 / FFMAX(limits[1], -limits[0])); + + for(i = 0; iblock_size; i++) { + float m, q; + + m = sst->out_buf[2*i+0] + (sst->iblock[i ].re) * sst->window[i ] * scale; + newbuf[2*i+0] = (sst->iblock[i + sst->block_size].re) * sst->window[i + sst->block_size] * scale; + + switch(am_mode) { + case AMMidSide: + case AMLeftRight: + q = sst->out_buf[2*i+1] + sst->iblock[i ].im * sst->window[i ] * scale; + newbuf[2*i+1] = sst->iblock[i + sst->block_size].im * sst->window[i + sst->block_size] * scale; + switch(am_mode) { + case AMMidSide: + q *= 0.5; + sst->out_buf[2*i+0] = m + q; + sst->out_buf[2*i+1] = m - q; + break; + case AMLeftRight: + sst->out_buf[2*i+0] = m; + sst->out_buf[2*i+1] = q; + break; + } + break; + + case AMEnvelope: + case AMInPhase: + sst->out_buf[2*i+0] = + sst->out_buf[2*i+1] = m; + break; + } + + if (fabs(sst->out_buf[i]) > clip) { + av_log(sdr->avfmt, AV_LOG_WARNING, "CLIP %f\n", sst->out_buf[i]); + clip = fabs(sst->out_buf[i]) * 1.1; + } + } + + //why is this taking uint8_t and noit void ?! + ret = av_packet_from_data(pkt, (void*)sst->out_buf, sizeof(*sst->out_buf) * 2 * sst->block_size); + if (ret < 0) + av_free(sst->out_buf); + sst->out_buf = newbuf; + + if (st->codecpar->ch_layout.nb_channels != 2 || + st->codecpar->sample_rate != sample_rate + ) { + av_log(sdr->avfmt, AV_LOG_INFO, "set channel parameters %d %d %d\n", st->codecpar->ch_layout.nb_channels, st->codecpar->sample_rate, sample_rate); + if (st->codecpar->sample_rate == 0) + sdr->missing_streams--; + ff_add_param_change(pkt, 2, 0, sample_rate, 0, 0); + } + + return ret; +} + +static int probe_fm(SDRContext *sdr) +{ + int i; + int bandwidth_f = 200*1000; + int half_bw_i = bandwidth_f * (int64_t)sdr->block_size / sdr->sdr_sample_rate; + double avg[2] = {0}, tri = 0; + float last_score[3] = {FLT_MAX, FLT_MAX, FLT_MAX}; + + if (2*half_bw_i > 2*sdr->block_size) + return 0; + + for (i = 0; ilen2block[i]; + tri += i*sdr->len2block[i]; + } + for (; i<2*half_bw_i; i++) { + avg[1] += sdr->len2block[i]; + tri += (2*half_bw_i-i)*sdr->len2block[i]; + } + + for(i = half_bw_i; i<2*sdr->block_size - half_bw_i; i++) { + double b = avg[0] + sdr->len2block[i]; + avg[0] += sdr->len2block[i] - sdr->len2block[i - half_bw_i]; + avg[1] -= sdr->len2block[i] - sdr->len2block[i + half_bw_i]; + b += avg[1]; + tri += avg[1] - avg[0]; + + last_score[2] = last_score[1]; + last_score[1] = last_score[0]; + last_score[0] = tri / (b * half_bw_i); + + if (last_score[1] >= last_score[0] && + last_score[1] > last_score[2] && + last_score[1] > FM_THRESHOLD) { + + // as secondary check, we could check that without the center 3 samples we are still having a strong signal FIXME + + double peak_i = find_peak(sdr, last_score, 1, 3) + i - 1; + if (peak_i < 0) + continue; + av_assert0(fabs(peak_i-i) < 2); +//Disabled as we dont have demodulate yet and its wrong sometimes +// create_station(sdr, FM, peak_i * 0.5 * sdr->sdr_sample_rate / sdr->block_size + sdr->block_center_freq - sdr->sdr_sample_rate/2, bandwidth_f, last_score[1]); + + } + } + + return 0; +} + +ModulationDescriptor modulation_descs[] = { + {"Amplitude Modulation", AM, AVMEDIA_TYPE_AUDIO, probe_am, demodulate_am}, + {"Frequency Modulation", FM, AVMEDIA_TYPE_AUDIO, probe_fm, NULL}, +}; + +static int set_sdr_freq(SDRContext *sdr, int64_t freq) +{ + freq = av_clip64(freq, sdr->min_center_freq, sdr->max_center_freq); + + if (sdr->soapy && SoapySDRDevice_setFrequency(sdr->soapy, SOAPY_SDR_RX, 0, freq, NULL) != 0) { + av_log(sdr->avfmt, AV_LOG_ERROR, "setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return AVERROR_EXTERNAL; + } + + sdr->freq = freq; + + return 0; +} + +static void free_stream(SDRContext *sdr, int stream_index) +{ + AVFormatContext *s = sdr->avfmt; + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + + av_tx_uninit(&sst->ifft_ctx); + sst->ifft = NULL; + sst->block_size = 0; + + av_freep(&sst->out_buf); + av_freep(&sst->block); + av_freep(&sst->iblock); + av_freep(&sst->icarrier); + av_freep(&sst->window); + +} + +static int setup_stream(SDRContext *sdr, int stream_index, Station *station) +{ + AVFormatContext *s = sdr->avfmt; + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + int ret; + + //For now we expect each station to be only demodulated once, nothing should break though if its done more often + av_assert0(station->stream == NULL || station->stream == sst); + + if (sst->station) + sst->station->stream = NULL; + + sst->station = station; + station->stream = sst; + + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + free_stream(sdr, stream_index); + + for (sst->block_size = 4; 4ll *sst->station->bandwidth * sdr->block_size / sdr->sdr_sample_rate > sst->block_size; sst->block_size <<= 1) + ; + sst->block_size = FFMIN(sdr->block_size, sst->block_size); + + ret = av_tx_init(&sst->ifft_ctx, &sst->ifft, AV_TX_FLOAT_FFT, 1, 2*sst->block_size, NULL, 0); + if (ret < 0) + return ret; + + sst->out_buf = av_malloc(sizeof(*sst->out_buf) * 2 * sst->block_size); + sst->block = av_malloc(sizeof(*sst-> block) * 2 * sst->block_size); + sst->iblock = av_malloc(sizeof(*sst->iblock) * 2 * sst->block_size); + sst->icarrier = av_malloc(sizeof(*sst->icarrier) * 2 * sst->block_size); + sst->window = av_malloc(sizeof(*sst->window) * 2 * sst->block_size); + if (!sst->out_buf || !sst->block || !sst->iblock || !sst->icarrier || !sst->window) + return AVERROR(ENOMEM); + + avpriv_kbd_window_init(sst->window, 8, sst->block_size); + for(int i = sst->block_size; i < 2 * sst->block_size; i++) { + sst->window[i] = sst->window[2*sst->block_size - i - 1]; + } + } + + return 0; +} + +static void inject_block_into_fifo(SDRContext *sdr, AVFifo *fifo, FIFOElement *fifo_element, const char *error_message) +{ + int ret; + + pthread_mutex_lock(&sdr->mutex); + ret = av_fifo_write(fifo, fifo_element, 1); + pthread_mutex_unlock(&sdr->mutex); + if (ret < 0) { + av_log(sdr->avfmt, AV_LOG_DEBUG, "%s", error_message); + av_freep(&fifo_element->halfblock); + } +} + +static void flush_fifo(SDRContext *sdr, AVFifo *fifo) +{ + FIFOElement fifo_element; + + pthread_mutex_lock(&sdr->mutex); + while(fifo) { + int ret = av_fifo_read(fifo, &fifo_element, 1); + if (ret < 0) + break; + av_freep(&fifo_element.halfblock); + } + pthread_mutex_unlock(&sdr->mutex); +} + +/** + * Grab data from soapy and put it in a bigger buffer. + * This thread would not be needed if libsoapy internal buffering was not restricted + * to a few milli seconds. libavformat cannot guarntee that it will get called from the user + * application every 10-20ms or something like that so we need a thread to pull data from soapy + * and put it in a larger buffer that we can then read from at the rate the code is called + * by the user + */ +static void *soapy_needs_bigger_buffers_worker(SDRContext *sdr) +{ + AVFormatContext *avfmt = sdr->avfmt; + unsigned block_counter = 0; + int remaining_file_block_size = 0; + ff_thread_setname("sdrdemux - soapy rx"); + + while(!atomic_load(&sdr->close_requested)) { + FIFOElement fifo_element; + int remaining, ret; + int empty_blocks, full_blocks; + + //i wish av_fifo was thread safe + pthread_mutex_lock(&sdr->mutex); + ret = av_fifo_read(sdr->empty_block_fifo, &fifo_element, 1); + empty_blocks = av_fifo_can_read(sdr->empty_block_fifo); + full_blocks = av_fifo_can_read(sdr->full_block_fifo); + pthread_mutex_unlock(&sdr->mutex); + + if (ret < 0) { + av_log(avfmt, AV_LOG_WARNING, "Allocating new block due to lack of space full:%d empty:%d\n", full_blocks, empty_blocks); + fifo_element.halfblock = av_malloc(sdr->block_size * sizeof(fifo_element.halfblock)); + if (!fifo_element.halfblock) { + av_log(avfmt, AV_LOG_ERROR, "Allocation failed, waiting for free space\n"); + // we wait 10ms here, tests have shown soapy to loose data when it is not serviced for 40-50ms + av_usleep(10*1000); + continue; + } + } + + block_counter ++; + pthread_mutex_lock(&sdr->mutex); + // we try to get 2 clean blocks after windowing, to improve chances scanning doesnt miss too much + // First block after parameter change is not reliable, we do not assign it any frequency + // 2 blocks are needed with windowing to get a clean FFT output + // Thus > 3 is the minimum for the next frequency update if we want to do something reliable with the data + if (sdr->seek_direction && block_counter > 3) { + int64_t new_freq = av_clip64(sdr->wanted_freq + sdr->seek_direction*sdr->bandwidth, sdr->min_center_freq, sdr->max_center_freq); + + // did we hit an end ? + if (new_freq == sdr->wanted_freq) { + //simply wrap around + new_freq = new_freq == sdr->min_center_freq ? sdr->max_center_freq : sdr->min_center_freq; + } + sdr->wanted_freq = new_freq; + } + if (sdr->wanted_freq != sdr->freq) { + //We could use a seperate MUTEX for the FIFO and for soapy + set_sdr_freq(sdr, sdr->wanted_freq); + //This shouldnt really cause any problem if we just continue on error except that we continue returning data with the previous target frequency range + //And theres not much else we can do, an error message was already printed by set_sdr_freq() in that case + block_counter = 0; // we just changed the frequency, do not trust the next blocks content + } + pthread_mutex_unlock(&sdr->mutex); + + fifo_element.center_frequency = block_counter > 0 ? sdr->freq : 0; + + remaining = sdr->block_size; + while (remaining && !atomic_load(&sdr->close_requested)) { + void *soapy_buffers[MAX_CHANNELS] = {NULL}; + long long soapy_ts; + int soapy_flags; + + soapy_buffers[0] = fifo_element.halfblock + sdr->block_size - remaining; + + if (sdr->soapy) { + ret = SoapySDRDevice_readStream(sdr->soapy, sdr->soapyRxStream, + soapy_buffers, + remaining, + &soapy_flags, + &soapy_ts, + 10*1000); + + if (ret == SOAPY_SDR_TIMEOUT) { + continue; + } else if (ret == SOAPY_SDR_OVERFLOW) { + av_log(avfmt, AV_LOG_WARNING, "SOAPY OVERFLOW\n"); + continue; + } else if (ret < 0) { + av_log(avfmt, AV_LOG_ERROR, "SoapySDRDevice_readStream() Failed with (%d) %s\n", ret, SoapySDRDevice_lastError()); + av_usleep(10*1000); + continue; + } + } else { + float scale = 1 / 32768.0; + int64_t size; + float *outp; + int16_t *inp; + if (!remaining_file_block_size) { + int block_size; + avio_skip(avfmt->pb, 16 + 4); //FFSDR00Xint16BE + block_size = avio_rb32(avfmt->pb); + fifo_element.center_frequency = av_int2double(avio_rb64(avfmt->pb)); + avio_rb64(avfmt->pb); //pts + avio_skip(avfmt->pb, sdr->fileheader_size - 40); + remaining_file_block_size = block_size; + } + pthread_mutex_lock(&sdr->mutex); + + //We have no FIFO API to check how much we can write + size = sdr->sdr_sample_rate / sdr->block_size - av_fifo_can_read(sdr->full_block_fifo); + pthread_mutex_unlock(&sdr->mutex); + if (size <= 0) { + av_usleep(10*1000); + continue; + } + size = FFMIN(remaining, remaining_file_block_size) * sizeof(int16_t) * 2; + outp = soapy_buffers[0]; + inp = (void*)((uint8_t*)soapy_buffers[0] + size); // used as temporary buffer + ret = avio_read(avfmt->pb, (void*)inp, size); + if (ret == AVERROR_EOF || (ret > 0 && ret % (sizeof(int16_t) * 2))) { + avio_seek(avfmt->pb, SEEK_SET, 0); + av_log(avfmt, AV_LOG_INFO, "EOF, will wraparound\n"); + continue; + } else if (ret == AVERROR(EAGAIN)) { + av_log(avfmt, AV_LOG_DEBUG, "read EAGAIN\n"); + continue; + } else if (ret < 0) { + av_log(avfmt, AV_LOG_ERROR, "read Failed with (%d)\n", ret); + continue; + } + + ret /= sizeof(int16_t) * 2; + for(int i= 0; ifull_block_fifo, &fifo_element, "block fifo overflow, discarding block\n"); + } + av_assert0(atomic_load(&sdr->close_requested) == 1); + + return NULL; +} + +static int sdr_read_header(AVFormatContext *s) +{ + SDRContext *sdr = s->priv_data; + AVStream *st; + SDRStream *sst; + int ret, i; + + size_t length; + char** names; + int64_t max_sample_rate; + + sdr->avfmt = s; + + if (sdr->width>1 && sdr->height>1) { + /* video stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + sst = av_mallocz(sizeof(SDRStream)); + if (!sst) + return AVERROR(ENOMEM); + st->priv_data = sst; + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codecpar->codec_tag = 0; /* no fourcc */ + st->codecpar->format = AV_PIX_FMT_BGRA; + st->codecpar->width = sdr->width; + st->codecpar->height = sdr->height; + avpriv_set_pts_info(st, 64, 1, (48000/128) << FREQ_BITS); + } + + if (sdr->mode == SingleStationMode) { + /* audio stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + sst = av_mallocz(sizeof(SDRStream)); + if (!sst) + return AVERROR(ENOMEM); + st->priv_data = sst; + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_tag = 0; /* no fourcc */ + st->codecpar->codec_id = HAVE_BIGENDIAN ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE; + st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + st->codecpar->sample_rate = 0; //will be set later + avpriv_set_pts_info(st, 64, 1, (48000/128) << FREQ_BITS); + sdr->single_ch_audio_st_index = st->index; + sdr->missing_streams++; + } + + if (!strcmp(s->iformat->name, "sdr")) { + SoapySDRRange *ranges; + SoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length); + SoapySDRKwargs args = {}; + + for (i = 0; i < length; i++) { + int usable = 1; + for (int j = 0; j < results[i].size; j++) { + if (!strcmp("driver", results[i].keys[j])) { + if (!strcmp("audio", results[i].vals[j])) { + usable = 0; + } else if (!sdr->driver_name) { + sdr->driver_name = av_strdup(results[i].vals[j]); + if (!sdr->driver_name) + return AVERROR(ENOMEM); + } + } + } + if (!usable) + continue; + av_log(s, AV_LOG_INFO, "Soapy enumeration %d\n", i); + for (int j = 0; j < results[i].size; j++) { + av_log(s, AV_LOG_INFO, " results %s = %s\n", results[i].keys[j], results[i].vals[j]); + } + } + SoapySDRKwargsList_clear(results, length); + + av_log(s, AV_LOG_INFO, "Opening %s\n", sdr->driver_name); + if (!sdr->driver_name) + return AVERROR(EINVAL); //No driver specified and none found + SoapySDRKwargs_set(&args, "driver", sdr->driver_name); + sdr->soapy = SoapySDRDevice_make(&args); + SoapySDRKwargs_clear(&args); + + if (sdr->soapy == NULL) { + av_log(s, AV_LOG_ERROR, "SoapySDRDevice_make fail: %s\n", SoapySDRDevice_lastError()); + return AVERROR_EXTERNAL; + } + + names = SoapySDRDevice_listAntennas(sdr->soapy, SOAPY_SDR_RX, 0, &length); + av_log(s, AV_LOG_INFO, "Antennas: "); + for (i = 0; i < length; i++) + av_log(s, AV_LOG_INFO, "%s, ", names[i]); + av_log(s, AV_LOG_INFO, "\n"); + SoapySDRStrings_clear(&names, length); + + names = SoapySDRDevice_listGains(sdr->soapy, SOAPY_SDR_RX, 0, &length); + av_log(s, AV_LOG_INFO, "Rx Gains: "); + for (i = 0; i < length; i++) + av_log(s, AV_LOG_INFO, "%s, ", names[i]); + av_log(s, AV_LOG_INFO, "\n"); + SoapySDRStrings_clear(&names, length); + + ranges = SoapySDRDevice_getFrequencyRange(sdr->soapy, SOAPY_SDR_RX, 0, &length); + av_log(s, AV_LOG_INFO, "Rx freq ranges: "); + for (i = 0; i < length; i++) { + av_log(s, AV_LOG_INFO, "[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum); + if (sdr->max_freq > ranges[i].maximum) + continue; + if (sdr->min_freq && sdr->min_freq < ranges[i].minimum) + continue; + break; + } + av_log(s, AV_LOG_INFO, "\n"); + if (i == length) { + av_log(s, AV_LOG_ERROR, "Invalid frequency range\n"); + return AVERROR(EINVAL); + } + if (!sdr->max_freq) + sdr->max_freq = ranges[i].maximum; + if (!sdr->min_freq) + sdr->min_freq = ranges[i].minimum; + free(ranges); ranges = NULL; + av_log(s, AV_LOG_INFO, "frequency range: %"PRId64" - %"PRId64"\n", sdr->min_freq, sdr->max_freq); + //TODO do any drivers support multiple distinct frequency ranges ? if so we pick just one, thats not ideal + + ranges = SoapySDRDevice_getSampleRateRange(sdr->soapy, SOAPY_SDR_RX, 0, &length); + max_sample_rate = 0; + av_log(s, AV_LOG_INFO, "SampleRate ranges: "); + for (i = 0; i < length; i++) { + av_log(s, AV_LOG_INFO, "[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum); + if (sdr->sdr_sample_rate && + (sdr->sdr_sample_rate < ranges[i].minimum || sdr->sdr_sample_rate > ranges[i].maximum)) + continue; + max_sample_rate = FFMAX(max_sample_rate, ranges[i].maximum); + } + av_log(s, AV_LOG_INFO, "\n"); + free(ranges); ranges = NULL; + if (!sdr->sdr_sample_rate) { + sdr->sdr_sample_rate = max_sample_rate; + } + + // We disallow odd sample rates as they result in either center or endpoint frequencies to be non integer. No big deal but simpler is better + if (!max_sample_rate || sdr->sdr_sample_rate%2) { + av_log(s, AV_LOG_ERROR, "Invalid sdr sample rate\n"); + return AVERROR(EINVAL); + } + + ranges = SoapySDRDevice_getBandwidthRange(sdr->soapy, SOAPY_SDR_RX, 0, &length); + av_log(s, AV_LOG_INFO, "Bandwidth ranges: "); + for (i = 0; i < length; i++) { + av_log(s, AV_LOG_INFO, "[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum); + } + av_log(s, AV_LOG_INFO, "\n"); + free(ranges); ranges = NULL; + + //apply settings + if (SoapySDRDevice_setSampleRate(sdr->soapy, SOAPY_SDR_RX, 0, sdr->sdr_sample_rate) != 0) { + av_log(s, AV_LOG_ERROR, "setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + return AVERROR_EXTERNAL; + } + ret = set_sdr_freq(sdr, sdr->wanted_freq); + if (ret < 0) + return ret; + + //setup a stream (complex floats) +#if SOAPY_SDR_API_VERSION < 0x00080000 // The old version is still widely used so we must support it + if (SoapySDRDevice_setupStream(sdr->soapy, &sdr->soapyRxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL)) + sdr->soapyRxStream = NULL; +#else + sdr->soapyRxStream = SoapySDRDevice_setupStream(sdr->soapy, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL); +#endif + if (!sdr->soapyRxStream) { + av_log(s, AV_LOG_ERROR, "setupStream fail: %s\n", SoapySDRDevice_lastError()); + return AVERROR_EXTERNAL; + } + + sdr->bandwidth = SoapySDRDevice_getBandwidth(sdr->soapy, SOAPY_SDR_RX, 0); + av_log(s, AV_LOG_INFO, "bandwidth %"PRId64"\n", sdr->bandwidth); + + SoapySDRDevice_activateStream(sdr->soapy, sdr->soapyRxStream, 0, 0, 0); + } else { + int version; + avio_skip(s->pb, 5); //FFSDR + version = avio_rb24(s->pb); //000 + avio_skip(s->pb, 8); //int16BE + sdr->sdr_sample_rate = avio_rb32(s->pb); + avio_rb32(s->pb); //block_size + sdr->wanted_freq = av_int2double(avio_rb64(s->pb)); + sdr->pts = avio_rb64(s->pb); + if (version > AV_RB24("000")) { + sdr->bandwidth = avio_rb64(s->pb); + sdr->fileheader_size = avio_rb32(s->pb); + } else { + sdr->bandwidth = sdr->sdr_sample_rate; + sdr->fileheader_size = 40; + } + + avio_seek(s->pb, 0, SEEK_SET); + + ret = set_sdr_freq(sdr, sdr->wanted_freq); + if (ret < 0) + return ret; + } + + sdr->min_center_freq = sdr->min_freq + sdr->sdr_sample_rate / 2; + sdr->max_center_freq = sdr->max_freq + sdr->sdr_sample_rate / 2; + + + if(!sdr->block_size) { + for(sdr->block_size = 1; 25*sdr->block_size < sdr->sdr_sample_rate; sdr->block_size <<=1) + ; + } else if(sdr->block_size & (sdr->block_size - 1)) { + av_log(s, AV_LOG_ERROR, "Block size must be a power of 2\n"); + return AVERROR(EINVAL); + } + av_log(s, AV_LOG_INFO, "Block size %d\n", sdr->block_size); + + + sdr->windowed_block = av_malloc(sizeof(*sdr->windowed_block) * 2 * sdr->block_size); + sdr->block = av_malloc(sizeof(*sdr->block ) * 2 * sdr->block_size); + sdr->len2block = av_malloc(sizeof(*sdr->len2block) * 2 * sdr->block_size); + sdr->window = av_malloc(sizeof(*sdr->window ) * 2 * sdr->block_size); + if (!sdr->windowed_block || !sdr->len2block || !sdr->block || !sdr->window) + return AVERROR(ENOMEM); + + ret = av_tx_init(&sdr->fft_ctx, &sdr->fft, AV_TX_FLOAT_FFT, 0, 2*sdr->block_size, NULL, 0); + if (ret < 0) + return ret; + + avpriv_kbd_window_init(sdr->window, 8, sdr->block_size); + + for(int i = sdr->block_size; i < 2 * sdr->block_size; i++) { + sdr->window[i] = sdr->window[2*sdr->block_size - i - 1]; + } + for (int i = 0; i < 2 * sdr->block_size; i++) + sdr->window[i] *= ((i&1) ? 1:-1); + + for (int stream_index = 0; stream_index < s->nb_streams; stream_index++) { + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + // Setup streams which are independant of stations and modulations + // Others cannot be setup yet + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + sst->frame_size = sdr->width * sdr->height * 4; + sst->frame_buffer = av_malloc(sst->frame_size * 2); + if (!sst->frame_buffer) + return AVERROR(ENOMEM); + } + } + + sdr->pts = 0; + + sdr->empty_block_fifo = av_fifo_alloc2(1, sizeof(FIFOElement), AV_FIFO_FLAG_AUTO_GROW); + sdr-> full_block_fifo = av_fifo_alloc2(1, sizeof(FIFOElement), AV_FIFO_FLAG_AUTO_GROW); + if (!sdr->empty_block_fifo || !sdr-> full_block_fifo) + return AVERROR(ENOMEM); + //Limit fifo to 1second to avoid OOM + av_fifo_auto_grow_limit(sdr->empty_block_fifo, sdr->sdr_sample_rate / sdr->block_size); + av_fifo_auto_grow_limit(sdr-> full_block_fifo, sdr->sdr_sample_rate / sdr->block_size); + + atomic_init(&sdr->close_requested, 0); + ret = pthread_mutex_init(&sdr->mutex, NULL); + if (ret) { + av_log(s, AV_LOG_ERROR, "pthread_mutex_init failed: %s\n", strerror(ret)); + return AVERROR(ret); + } + ret = pthread_create(&sdr->soapy_thread, NULL, (void*)soapy_needs_bigger_buffers_worker, sdr); + if (ret != 0) { + av_log(s, AV_LOG_ERROR, "pthread_create failed : %s\n", strerror(ret)); + pthread_mutex_destroy(&sdr->mutex); + return AVERROR(ret); + } + sdr->thread_started = 1; + + if(sdr->dump_url) { + ret = avio_open2(&sdr->dump_avio, sdr->dump_url, AVIO_FLAG_WRITE, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "Unable to open %s\n", sdr->dump_url); + return ret; + } + } + + return 0; +} + +static inline void draw_point_component(uint8_t *frame_buffer, ptrdiff_t stride, int x, int y, int r, int g, int b, int w, int h) +{ + uint8_t *p; + + if (x<0 || y<0 || x>=w || y>=h) + return; + p = frame_buffer + 4*x + stride*y; + + p[0] = av_clip_uint8(p[0] + (b>>16)); + p[1] = av_clip_uint8(p[1] + (g>>16)); + p[2] = av_clip_uint8(p[2] + (r>>16)); +} + +// Draw a point with subpixel precission, (it looked bad otherwise) +static void draw_point(uint8_t *frame_buffer, ptrdiff_t stride, int x, int y, int r, int g, int b, int w, int h) +{ + int px = x>>8; + int py = y>>8; + int sx = x&255; + int sy = y&255; + int s; + + s = (256 - sx) * (256 - sy); + draw_point_component(frame_buffer, stride, px , py , r*s, g*s, b*s, w, h); + s = sx * (256 - sy); + draw_point_component(frame_buffer, stride, px+1, py , r*s, g*s, b*s, w, h); + s = (256 - sx) * sy; + draw_point_component(frame_buffer, stride, px , py+1, r*s, g*s, b*s, w, h); + s = sx * sy; + draw_point_component(frame_buffer, stride, px+1, py+1, r*s, g*s, b*s, w, h); +} + +static int sdr_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + SDRContext *sdr = s->priv_data; + int ret, i, full_blocks, seek_direction; + FIFOElement fifo_element[2]; + +process_next_block: + + for (int stream_index = 0; stream_index < s->nb_streams; stream_index++) { + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + + if (sst->processing_index) { + int skip = 1; + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + int w = st->codecpar->width; + int h = st->codecpar->height; + int h2 = FFMIN(64, h / 4); + int frame_index = av_rescale(sdr->pts, sdr->fps.num, sdr->fps.den * TIMEBASE); + int last_index = av_rescale(sdr->last_pts, sdr->fps.num, sdr->fps.den * TIMEBASE); + skip = frame_index == last_index || sdr->missing_streams; + + for(int x= 0; xframe_buffer_line*w); + int bindex = x * 2ll * sdr->block_size / w; + int bindex2 = (x+1) * 2ll * sdr->block_size / w; + float a = 0; + av_assert0(bindex2 <= 2 * sdr->block_size); + for (int i = bindex; i < bindex2; i++) { + AVComplexFloat sample = sdr->block[i]; + a += len2(sample); + } + color = lrintf(log(a)*8 + 32); + + sst->frame_buffer[idx + 0] = color; + sst->frame_buffer[idx + 1] = color; + sst->frame_buffer[idx + 2] = color; + sst->frame_buffer[idx + 3] = 255; + } + + // Display locations of all vissible stations + for(int station_index = 0; station_indexnb_stations; station_index++) { + Station *s = sdr->station[station_index]; + double f = s->frequency; +// int bw = s->bandwidth; +// int xleft = 256*((f-bw) - sdr->block_center_freq + sdr->sdr_sample_rate/2) * w / sdr->sdr_sample_rate; +// int xright= 256*((f+bw) - sdr->block_center_freq + sdr->sdr_sample_rate/2) * w / sdr->sdr_sample_rate; + int xmid = 256*( f - sdr->block_center_freq + sdr->sdr_sample_rate/2) * w / sdr->sdr_sample_rate; + int g = s->modulation == AM ? 50 : 0; + int b = s->modulation == AM ? 0 : 70; + int r = s->stream ? 50 : 0; + + draw_point(sst->frame_buffer, 4*w, xmid, 256*(sst->frame_buffer_line+1), r, g, b, w, h); + } + + if (!skip) { + ret = av_new_packet(pkt, sst->frame_size); + if (ret < 0) + return ret; + + for(int y= 0; yblock_size / (w * h2); + int bindex2 = (idx+1) * 2ll * sdr->block_size / (w * h2); + float a = 0; + av_assert0(bindex2 <= 2 * sdr->block_size); + for (int i = bindex; i < bindex2; i++) { + AVComplexFloat sample = sdr->block[i]; + a += len2(sample); + } + color = lrintf(log(a)*9 + 64); + + idx_t *= 4; + + pkt->data[idx_t+0] = color; + pkt->data[idx_t+1] = color; + pkt->data[idx_t+2] = color; + pkt->data[idx_t+3] = 255; + } + } + for (int y= h2; ydata + 4*y*w, sst->frame_buffer + 4*(y + sst->frame_buffer_line - h2)*w, 4*w); + } + + if (!sst->frame_buffer_line) { + memcpy(sst->frame_buffer + sst->frame_size, sst->frame_buffer, sst->frame_size); + sst->frame_buffer_line = h-1; + } else + sst->frame_buffer_line--; + +//TODO +// draw RDS* +// draw frequencies + } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (sst->station) { + skip = 0; + ret = modulation_descs[ sst->station->modulation ].demodulate(sdr, stream_index, pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "demodulation failed ret = %d\n", ret); + } + } + } else + av_assert0(0); + sst->processing_index = 0; + if (pkt && !skip) { + pkt->stream_index = stream_index; + pkt->dts = (sdr->pts & (-1<station) + pkt->dts += lrint(sst->station->frequency/1000); + pkt->pts = pkt->dts; + + return 0; + } + } + } + + pthread_mutex_lock(&sdr->mutex); + full_blocks = av_fifo_can_read(sdr->full_block_fifo) - 1; + ret = av_fifo_peek(sdr->full_block_fifo, &fifo_element, 2, 0); + if (ret >= 0) + av_fifo_drain2(sdr->full_block_fifo, 1); + seek_direction = sdr->seek_direction; //This doesnt need a mutex here at all but tools might complain + pthread_mutex_unlock(&sdr->mutex); + + if (ret < 0) { + av_log(s, AV_LOG_DEBUG, "EAGAIN on not enough data\n"); + return AVERROR(EAGAIN); + } + if (fifo_element[0].center_frequency != fifo_element[1].center_frequency) { + av_log(s, AV_LOG_DEBUG, "Mismatching frequency blocks\n"); +// fifo_element[0].center_frequency = 0; +// inject_block_into_fifo(sdr, sdr->empty_block_fifo, &fifo_element, "Cannot pass next buffer, freeing it\n"); + //Its simpler to just continue than to discard this, but we must make sure that on seeking we have matching blocks for each block we want to scan + sdr->block_center_freq = 0; + sdr->skip_probe = 1; // we want to probe the next block as it will have new frequencies + } else + sdr->block_center_freq = fifo_element[0].center_frequency; + + if (sdr->dump_avio) { + uint8_t header[48] = "FFSDR001int16BE"; + uint8_t *tmp = (void*)sdr->windowed_block; //We use an unused array as temporary here + + AV_WB32(header+16, sdr->sdr_sample_rate); + AV_WB32(header+20, sdr->block_size); + AV_WB64(header+24, av_double2int(fifo_element[0].center_frequency)); + AV_WB64(header+32, sdr->pts); + AV_WB32(header+40, sdr->bandwidth); + AV_WB32(header+44, sizeof(header)); + + avio_write(sdr->dump_avio, header, sizeof(header)); + + //We do ask Soapy for 32bit complex floats as they are easier to work with but really the hardware generally produces integers and no 32bits + //For storage int16 is supperior as it needs less space + for(int i=0; iblock_size; i++) { + AV_WB16(tmp + 4*i + 0, lrint(fifo_element[0].halfblock[i].re * 32768)); + AV_WB16(tmp + 4*i + 2, lrint(fifo_element[0].halfblock[i].im * 32768)); + } + avio_write(sdr->dump_avio, tmp, 4*sdr->block_size); + } + + for (i = 0; iblock_size; i++) { + sdr->windowed_block[i].re = fifo_element[0].halfblock[i].re * sdr->window[i]; + sdr->windowed_block[i].im = fifo_element[0].halfblock[i].im * sdr->window[i]; + } + for (i = sdr->block_size; i<2*sdr->block_size; i++) { + sdr->windowed_block[i].re = fifo_element[1].halfblock[i - sdr->block_size].re * sdr->window[i]; + sdr->windowed_block[i].im = fifo_element[1].halfblock[i - sdr->block_size].im * sdr->window[i]; + } + + inject_block_into_fifo(sdr, sdr->empty_block_fifo, &fifo_element[0], "Cannot pass next buffer, freeing it\n"); +#ifdef SYN_TEST //synthetic test signal + static int64_t synp=0; + AVLFG avlfg; + if(!synp) + av_lfg_init(&avlfg, 0); + for(int i = 0; i<2*sdr->block_size; i++) { + double f = F2INDEX(7123456.78901234567); + int64_t synp2 = synp % (40*sdr->block_size); + double fsig0 = 0.00002; + double fsig2= 123; + + if (!i) + av_log(0,0, "i= %f %f\n", f, INDEX2F(f)); + double noise[2] = {0,0}; + double sig0 = 1.0 + 0.25*sin(synp2*fsig0*synp2*M_PI / sdr->block_size); + double sig2 = 0.1 + 0.03*cos(synp*fsig2*M_PI / sdr->block_size); + if (i & 256) + av_bmg_get(&avlfg, noise); + sdr->windowed_block[i].re = (noise[0]*0.0000001 + 0.00001*sig0*sin(synp*M_PI*(f)/sdr->block_size) + + 0.00001*sig2*cos(synp*M_PI*(f)/sdr->block_size) + ) * fabs(sdr->window[i]); + sdr->windowed_block[i].im = noise[1]*0.0000001 * sdr->window[i]; + synp++; + } + synp -= sdr->block_size; +#endif + + + sdr->fft(sdr->fft_ctx, sdr->block, sdr->windowed_block, sizeof(AVComplexFloat)); + // windowed_block is unused now, we can fill it with the next blocks data + + if (sdr->block_center_freq) { + if (sdr->skip_probe-- <= 0) { + //Probing takes a bit of time, lets not do it every time + sdr->skip_probe = 5; + probe_common(sdr); + + for(int i = 0; i < FF_ARRAY_ELEMS(modulation_descs); i++) { + ModulationDescriptor *md = &modulation_descs[i]; + md->probe(sdr); + av_assert0(i == md->modulation); + } + + decay_stations(sdr); + } + + if (sdr->mode == SingleStationMode) { + AVStream *st = s->streams[sdr->single_ch_audio_st_index]; + SDRStream *sst = st->priv_data; + + if (!sst->station || seek_direction) { + double current_freq; + double best_distance = INT64_MAX; + Station *best_station = NULL; + + if (sst->station) { + current_freq = sst->station->frequency; + } else if (sdr->station_freq) { + current_freq = sdr->station_freq; + } else + current_freq = sdr->block_center_freq; + + for(int i = 0; inb_stations; i++) { + Station *station = sdr->station[i]; + double distance = station->frequency - current_freq; + + if (distance * seek_direction < 0 || station == sst->station) + continue; + distance = fabs(distance); + if (distance < best_distance) { + best_distance = distance; + best_station = station; + } + } + av_assert0(!best_station || best_station != sst->station); + if (best_station) { + ret = setup_stream(sdr, sdr->single_ch_audio_st_index, best_station); + if (ret < 0) { + av_log(s, AV_LOG_DEBUG, "setup_stream failed\n"); + return ret; + } + + pthread_mutex_lock(&sdr->mutex); + sdr->seek_direction = + seek_direction = 0; + sdr->wanted_freq = lrint(best_station->frequency + 213*1000); // We target a bit off teh exact frequency to avoid artifacts + //200*1000 had artifacts + + av_log(s, AV_LOG_DEBUG, "request f = %"PRId64"\n", sdr->wanted_freq); + pthread_mutex_unlock(&sdr->mutex); + } + } + } else { + av_assert0(sdr->mode == AllStationMode); + for(int i = 0; inb_stations; i++) { + Station *station = sdr->station[i]; + if (!station->stream) { + /* audio stream */ + AVStream *st = avformat_new_stream(s, NULL); + SDRStream *sst; + if (!st) + return AVERROR(ENOMEM); + sst = av_mallocz(sizeof(*sst)); + if (!sst) + return AVERROR(ENOMEM); + st->priv_data = sst; + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_tag = 0; /* no fourcc */ + st->codecpar->codec_id = HAVE_BIGENDIAN ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE; + st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + st->codecpar->sample_rate = 0; // will be set later + avpriv_set_pts_info(st, 64, 1, (48000/128) << FREQ_BITS); + ret = setup_stream(sdr, st->index, station); + if (ret < 0) + return ret; + sdr->missing_streams++; + } + } + } + + // new data is available for all streams, lets tell them + //TODO with mixed blocks during scanning theres more than one block_center_freq, we may want to try to support them as they likely have demodulatable data + for (int stream_index = 0; stream_index < s->nb_streams; stream_index++) { + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + sst->processing_index += sdr->block_size; + } + } + + sdr->last_pts = sdr->pts; + sdr->pts += av_rescale(sdr->block_size, TIMEBASE, sdr->sdr_sample_rate); + + //some user apps force a delay on EAGAIN so we cannot always return EAGAIN or we overflow the fifos + if (full_blocks >= 2) + goto process_next_block; + + return AVERROR(EAGAIN); +} + +static int sdr_read_seek(AVFormatContext *s, int stream_index, + int64_t target, int flags) +{ + SDRContext *sdr = s->priv_data; + int64_t freq, step; + int dir = (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1; + AVStream *st = s->streams[stream_index]; + SDRStream *sst = st->priv_data; + + if (sdr->mode != SingleStationMode) { + return AVERROR(ENOTSUP); + } + + av_assert0(stream_index >= 0); + + step = FFABS(target - sdr->pts); + if (step < 35 * TIMEBASE) { + target = (sdr->pts & (-1<station) + target += lrint(sst->station->frequency/1000); + }else if (step < 330 * TIMEBASE) + return AVERROR(ENOTSUP); // Seek to next/prev band + else + return AVERROR(ENOTSUP); // Reserved for future ideas + + freq = (target & ((1<mutex); + sdr->seek_direction = dir; + pthread_mutex_unlock(&sdr->mutex); + flush_fifo(sdr, sdr->full_block_fifo); + return 0; +} + +static int sdr_read_close(AVFormatContext *s) +{ + SDRContext *sdr = s->priv_data; + int i; + + atomic_store(&sdr->close_requested, 1); + + if(sdr->thread_started) + pthread_join(sdr->soapy_thread, NULL); + + flush_fifo(sdr, sdr->empty_block_fifo); //needs mutex to be still around + flush_fifo(sdr, sdr-> full_block_fifo); //needs mutex to be still around + + if(sdr->thread_started) + pthread_mutex_destroy(&sdr->mutex); + + av_fifo_freep2(&sdr->empty_block_fifo); + av_fifo_freep2(&sdr->full_block_fifo); + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + SDRStream *sst = st->priv_data; + + free_stream(sdr, i); + + av_freep(&sst->frame_buffer); + sst->frame_size = 0; + } + + for (i = 0; i < sdr->nb_stations; i++) { + free_station(sdr->station[i]); + } + sdr->nb_stations = 0; + av_freep(&sdr->station); + + if (sdr->soapy) { + if (sdr->soapyRxStream) { + SoapySDRDevice_deactivateStream(sdr->soapy, sdr->soapyRxStream, 0, 0); + SoapySDRDevice_closeStream(sdr->soapy, sdr->soapyRxStream); + sdr->soapyRxStream = NULL; + } + + SoapySDRDevice_unmake(sdr->soapy); + sdr->soapy = NULL; + } + + av_freep(&sdr->windowed_block); + av_freep(&sdr->block); + av_freep(&sdr->len2block); + av_freep(&sdr->window); + + av_tx_uninit(&sdr->fft_ctx); + sdr->fft = NULL; + + avio_close(sdr->dump_avio); + + return 0; +} + +static int sdrfile_probe(const AVProbeData *p) +{ + if (!memcmp(p->buf , "FFSDR00", 7) && + !memcmp(p->buf+8, "int16BE", 7)) { + return AVPROBE_SCORE_MAX; + } else + return 0; +} + +#define OFFSET(x) offsetof(SDRContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "video_size", "set frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = "800x600"}, 0, 0, DEC }, + { "framerate" , "set frame rate", OFFSET(fps), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX,DEC }, + { "block_size", "FFT block size", OFFSET(block_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC}, + { "mode", "" , OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = SingleStationMode}, 0, ModeNB-1, DEC, "mode"}, + { "single_mode", "Demodulate 1 station", 0, AV_OPT_TYPE_CONST, {.i64 = SingleStationMode}, 0, 0, DEC, "mode"}, + { "all_mode" , "Demodulate all station", 0, AV_OPT_TYPE_CONST, {.i64 = AllStationMode}, 0, 0, DEC, "mode"}, + + { "station_freq", "current station/channel/stream frequency", OFFSET(station_freq), AV_OPT_TYPE_INT64, {.i64 = 88000000}, 0, INT64_MAX, DEC}, + + { "driver" , "sdr driver name" , OFFSET(driver_name), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC}, + { "sdr_sr" , "sdr sample rate" , OFFSET(sdr_sample_rate ), AV_OPT_TYPE_INT , {.i64 = 0}, 0, INT_MAX, DEC}, + { "sdr_freq", "sdr frequency" , OFFSET(wanted_freq), AV_OPT_TYPE_INT64 , {.i64 = 9000000}, 0, INT64_MAX, DEC}, + { "min_freq", "minimum frequency", OFFSET(min_freq ), AV_OPT_TYPE_INT64 , {.i64 = 0}, 0, INT64_MAX, DEC}, + { "max_freq", "maximum frequency", OFFSET(max_freq ), AV_OPT_TYPE_INT64 , {.i64 = 0}, 0, INT64_MAX, DEC}, + + { "dumpurl", "url to dump soapy output to" , OFFSET(dump_url), AV_OPT_TYPE_STRING , {.str = NULL}, 0, 0, DEC}, + { "kbd_alpha", "Kaiser Bessel window parameter" , OFFSET(kbd_alpha), AV_OPT_TYPE_INT , {.i64 = 8}, 1, 16, DEC}, + + + { "am_mode", "AM Demodulation Mode", OFFSET(am_mode ), AV_OPT_TYPE_INT , {.i64 = AMMidSide}, 0, AMModeNB-1, DEC, "am_mode"}, + { "am_leftright", "AM Demodulation Left Right", 0, AV_OPT_TYPE_CONST, {.i64 = AMLeftRight}, 0, 0, DEC, "am_mode"}, + { "am_midside", "AM Demodulation Mid Side", 0, AV_OPT_TYPE_CONST, {.i64 = AMMidSide}, 0, 0, DEC, "am_mode"}, + { "am_inphase", "AM Demodulation In Phase", 0, AV_OPT_TYPE_CONST, {.i64 = AMInPhase}, 0, 0, DEC, "am_mode"}, + { "am_envelope","AM Demodulation EnvelopeDC", 0, AV_OPT_TYPE_CONST, {.i64 = AMEnvelope}, 0, 0, DEC, "am_mode"}, + + { NULL }, +}; + +static const AVClass sdr_demuxer_class = { + .class_name = "sdr", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +const AVInputFormat ff_sdr_demuxer = { + .name = "sdr", + .long_name = NULL_IF_CONFIG_SMALL("Software Defined Radio Demodulator"), + .priv_data_size = sizeof(SDRContext), + .read_header = sdr_read_header, + .read_packet = sdr_read_packet, + .read_close = sdr_read_close, + .read_seek = sdr_read_seek, + .flags = AVFMT_NOFILE, + .flags_internal = FF_FMT_INIT_CLEANUP, + .priv_class = &sdr_demuxer_class, +}; + +static const AVClass sdrfile_demuxer_class = { + .class_name = "sdrfile", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +const AVInputFormat ff_sdrfile_demuxer = { + .name = "sdrfile", + .long_name = NULL_IF_CONFIG_SMALL("Software Defined Radio Demodulator (Using a file for testing)"), + .priv_data_size = sizeof(SDRContext), + .read_probe = sdrfile_probe, + .read_header = sdr_read_header, + .read_packet = sdr_read_packet, + .read_close = sdr_read_close, + .read_seek = sdr_read_seek, + .flags_internal = FF_FMT_INIT_CLEANUP, + .priv_class = &sdrfile_demuxer_class, +};