浏览代码

1.将音频编码器从flv库中移到1078协议库中

2.移除掉faac库
3.暂时去掉音频功能
tags/v1.1.0
SmallChi(Koike) 4 年前
父节点
当前提交
61a9f7e31d
共有 16 个文件被更改,包括 364 次插入378 次删除
  1. +22
    -22
      src/JT1078.Flv.Test/Audio/AudioTest.cs
  2. +0
    -123
      src/JT1078.Flv/Audio/G711ACodec.cs
  3. +10
    -38
      src/JT1078.Flv/FlvEncoder.cs
  4. +1
    -9
      src/JT1078.Flv/JT1078.Flv.csproj
  5. +1
    -48
      src/JT1078.Flv/JT1078.Flv.xml
  6. 二进制
      src/JT1078.Flv/nativelibs/x64/libfaac.dll
  7. 二进制
      src/JT1078.Flv/nativelibs/x86/libfaac.dll
  8. +4
    -2
      src/JT1078.Hls/JT1078.Hls.xml
  9. +116
    -114
      src/JT1078.Protocol/Audio/AdpcmCodec.cs
  10. +47
    -0
      src/JT1078.Protocol/Audio/AudioCodecFactory.cs
  11. +1
    -2
      src/JT1078.Protocol/Audio/FaacEncoder.cs
  12. +114
    -0
      src/JT1078.Protocol/Audio/G711ACodec.cs
  13. +21
    -19
      src/JT1078.Protocol/Audio/G711UCodec.cs
  14. +10
    -0
      src/JT1078.Protocol/Audio/IAudioAttachData.cs
  15. +11
    -0
      src/JT1078.Protocol/Audio/IAudioCodec.cs
  16. +6
    -1
      src/JT1078.Protocol/JT1078.Protocol.csproj

+ 22
- 22
src/JT1078.Flv.Test/Audio/AudioTest.cs 查看文件

@@ -1,5 +1,4 @@
using JT1078.Flv.Audio;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -26,28 +25,29 @@ namespace JT1078.Flv.Test.Audio
[Fact(DisplayName = "pcm编码aac")] [Fact(DisplayName = "pcm编码aac")]
public void Test1() public void Test1()
{ {
//todo:音频暂时先放下
ReadOnlySpan<byte> fileData = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Audio/Files/testpacket.pcm")); ReadOnlySpan<byte> fileData = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Audio/Files/testpacket.pcm"));
//注意 这里为了可以判断音频是否可用,因此使用adts,当网络传输的时候不应该使用adts //注意 这里为了可以判断音频是否可用,因此使用adts,当网络传输的时候不应该使用adts
var faac = new FaacEncoder_x64(8000, 1, 16, true);
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Audio\Files\testpacket.aac");
if (File.Exists(path)) File.Delete(path);
output.WriteLine(path);
var offset = 0;
var step = faac.frameSize;
var totalBytes = 0;
var stopwatch = new Stopwatch();
while (offset + step < fileData.Length)
{
stopwatch.Start();
var aacBuff = faac.Encode(fileData.Slice(offset, step).ToArray());
stopwatch.Stop();
if (aacBuff.Any())
aacBuff.AppendBytesToFile(path);
offset += step;
totalBytes += aacBuff.Length;
}
faac.Dispose();
output.WriteLine($"已编码字节数:{offset},剩余未编码字节数:{fileData.Length - offset},编码后字节数:{totalBytes},耗时:{stopwatch.Elapsed.Milliseconds}毫秒");
//var faac = new FaacEncoder_x64(8000, 1, 16, true);
//var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Audio\Files\testpacket.aac");
//if (File.Exists(path)) File.Delete(path);
//output.WriteLine(path);
//var offset = 0;
//var step = faac.frameSize;
//var totalBytes = 0;
//var stopwatch = new Stopwatch();
//while (offset + step < fileData.Length)
//{
// stopwatch.Start();
// var aacBuff = faac.Encode(fileData.Slice(offset, step).ToArray());
// stopwatch.Stop();
// if (aacBuff.Any())
// aacBuff.AppendBytesToFile(path);
// offset += step;
// totalBytes += aacBuff.Length;
//}
//faac.Dispose();
//output.WriteLine($"已编码字节数:{offset},剩余未编码字节数:{fileData.Length - offset},编码后字节数:{totalBytes},耗时:{stopwatch.Elapsed.Milliseconds}毫秒");
} }
} }
static class Ex static class Ex


+ 0
- 123
src/JT1078.Flv/Audio/G711ACodec.cs 查看文件

@@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace JT1078.Flv.Audio
{
public class G711ACodec
{
private readonly int SIGN_BIT = 0x80;
private readonly int QUANT_MASK = 0xf;
private readonly int SEG_SHIFT = 4;
private readonly int SEG_MASK = 0x70;
private readonly short[] seg_end = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF };

static short Search(short val, short[] table, short size)
{
for (short i = 0; i < size; i++)
{
if (val <= table[i])
{
return i;
}
}
return size;
}

byte LinearToAlaw(short pcm_val)
{
short mask;
short seg;
char aval;
if (pcm_val >= 0)
{
mask = 0xD5;
}
else
{
mask = 0x55;
pcm_val = (short)(-pcm_val - 1);
if (pcm_val < 0)
{
pcm_val = 32767;
}
}

//Convert the scaled magnitude to segment number.
seg = Search(pcm_val, seg_end, 8);

//Combine the sign, segment, and quantization bits.
if (seg >= 8)
{
//out of range, return maximum value.
return (byte)(0x7F ^ mask);
}
else
{
aval = (char)(seg << SEG_SHIFT);
if (seg < 2) aval |= (char)((pcm_val >> 4) & QUANT_MASK);
else aval |= (char)((pcm_val >> (seg + 3)) & QUANT_MASK);
return (byte)(aval ^ mask);
}
}


short AlawToLinear(byte value)
{
short t;
short seg;

value ^= 0x55;

t = (short)((value & QUANT_MASK) << 4);
seg = (short)((value & SEG_MASK) >> SEG_SHIFT);
switch (seg)
{
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
break;
}
return (value & SIGN_BIT) != 0 ? t : (short)-t;
}

/// <summary>
/// 转至PCM
/// </summary>
/// <param name="g711data"></param>
/// <returns></returns>
public byte[] ToPcm(byte[] g711data)
{
byte[] pcmdata = new byte[g711data.Length * 2];
for (int i = 0, offset = 0; i < g711data.Length; i++)
{
short value = AlawToLinear(g711data[i]);
pcmdata[offset++] = (byte)(value & 0xff);
pcmdata[offset++] = (byte)((value >> 8) & 0xff);
}
return pcmdata;
}

/// <summary>
/// 转至G711
/// </summary>
/// <param name="pcmdata"></param>
/// <returns></returns>
public byte[] ToG711(byte[] pcmdata)
{
byte[] g711data = new byte[pcmdata.Length / 2];
for (int i = 0, k = 0; i < pcmdata.Length; i += 2, k++)
{
short v = (short)((pcmdata[i + 1] << 8) | (pcmdata[i]));
g711data[k] = LinearToAlaw(v);
}
return g711data;
}
}
}

+ 10
- 38
src/JT1078.Flv/FlvEncoder.cs 查看文件

@@ -4,12 +4,12 @@ using JT1078.Flv.Metadata;
using JT1078.Protocol.Enums; using JT1078.Protocol.Enums;
using JT1078.Protocol.H264; using JT1078.Protocol.H264;
using JT1078.Protocol.MessagePack; using JT1078.Protocol.MessagePack;
using JT1078.Flv.Audio;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using JT1078.Protocol; using JT1078.Protocol;
using JT1078.Protocol.Audio;


[assembly: InternalsVisibleTo("JT1078.Flv.Test")] [assembly: InternalsVisibleTo("JT1078.Flv.Test")]
namespace JT1078.Flv namespace JT1078.Flv
@@ -35,20 +35,15 @@ namespace JT1078.Flv
/// 3、<see cref="EncoderVideoTag"/>第二个参数传true /// 3、<see cref="EncoderVideoTag"/>第二个参数传true
/// 4、<see cref="EncoderAudioTag"/>第二个参数传true /// 4、<see cref="EncoderAudioTag"/>第二个参数传true
/// </summary> /// </summary>
public class FlvEncoder : IDisposable
public class FlvEncoder
{ {
readonly IFaacEncoder faacEncoder;
readonly H264Decoder h264Decoder = new H264Decoder();
public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false)
readonly H264Decoder h264Decoder;
readonly AudioCodecFactory audioCodecFactory;
//public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false)
public FlvEncoder()
{ {
try
{
faacEncoder = new FaacEncoder_x86(sampleRate, channels, sampleBit, adts);
}
catch
{
faacEncoder = new FaacEncoder_x64(sampleRate, channels, sampleBit, adts);
}
audioCodecFactory = new AudioCodecFactory();
h264Decoder = new H264Decoder();
} }


/// <summary> /// <summary>
@@ -255,6 +250,7 @@ namespace JT1078.Flv
/// <param name="package"></param> /// <param name="package"></param>
/// <param name="needAacHeader">是否需要首帧音频</param> /// <param name="needAacHeader">是否需要首帧音频</param>
/// <returns></returns> /// <returns></returns>
[Obsolete("音频暂时去掉")]
public byte[] EncoderAudioTag(JT1078Package package, bool needAacHeader = false) public byte[] EncoderAudioTag(JT1078Package package, bool needAacHeader = false)
{ {
if (package.Label3.DataType != JT1078DataType.音频帧) throw new Exception("Incorrect parameter, package must be audio frame"); if (package.Label3.DataType != JT1078DataType.音频帧) throw new Exception("Incorrect parameter, package must be audio frame");
@@ -263,26 +259,7 @@ namespace JT1078.Flv
{ {
flvMessagePackWriter.WriteArray(EncoderFirstAudioTag(package.Timestamp)); flvMessagePackWriter.WriteArray(EncoderFirstAudioTag(package.Timestamp));
} }
byte[] aacFrameData = null;
switch (package.Label2.PT)
{
case JT1078AVType.ADPCM:
ReadOnlySpan<byte> adpcm = package.Bodies;
// 海思芯片编码的音频需要移除海思头,可能还有其他的海思头
if (adpcm.StartsWith(new byte[] { 0x00, 0x01, 0x52, 0x00 })) adpcm = adpcm.Slice(4);
aacFrameData = faacEncoder.Encode(new AdpcmCodec().ToPcm(adpcm.Slice(4).ToArray(), new State()
{
Valprev = (short)((adpcm[1] << 8) | adpcm[0]),
Index = adpcm[2],
Reserved = adpcm[3]
})); break;
case JT1078AVType.G711A:
aacFrameData = faacEncoder.Encode(new G711ACodec().ToPcm(package.Bodies));
break;
case JT1078AVType.AACLC:
aacFrameData = package.Bodies;
break;
}
byte[] aacFrameData = audioCodecFactory.Encode(package.Label2.PT, package.Bodies);
if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据 if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据
{ {
// Data Tag Frame // Data Tag Frame
@@ -372,10 +349,5 @@ namespace JT1078.Flv
FlvArrayPool.Return(buffer); FlvArrayPool.Return(buffer);
} }
} }

public void Dispose()
{
faacEncoder.Dispose();
}
} }
} }

+ 1
- 9
src/JT1078.Flv/JT1078.Flv.csproj 查看文件

@@ -14,7 +14,7 @@
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl>
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>1.0.0-preview7</Version>
<Version>1.0.0-preview8</Version>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -44,12 +44,4 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.7" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.7" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="nativelibs\x64\libfaac.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="nativelibs\x86\libfaac.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

+ 1
- 48
src/JT1078.Flv/JT1078.Flv.xml 查看文件

@@ -4,53 +4,6 @@
<name>JT1078.Flv</name> <name>JT1078.Flv</name>
</assembly> </assembly>
<members> <members>
<member name="P:JT1078.Flv.Audio.State.Valprev">
<summary>
上一个采样数据,当index为0是该值应该为未压缩的原数据
</summary>
</member>
<member name="P:JT1078.Flv.Audio.State.Reserved">
<summary>
保留数据(未使用)
</summary>
</member>
<member name="P:JT1078.Flv.Audio.State.Index">
<summary>
上一个block最后一个index,第一个block的index=0
</summary>
</member>
<member name="M:JT1078.Flv.Audio.AdpcmCodec.ToPcm(System.Byte[],JT1078.Flv.Audio.State)">
<summary>
将adpcm转为pcm
</summary>
<see cref="!:https://github.com/ctuning/ctuning-programs/blob/master/program/cbench-telecom-adpcm-d/adpcm.c"/>
<param name="data"></param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.Audio.AdpcmDecoderExtension.ToWav(System.Byte[],System.UInt32,System.Byte)">
<summary>
添加wav头
仅用于测试pcm是否转成成功,因此没考虑性能,因为播放器可播——#
</summary>
<param name="input">pcm数据</param>
<param name="frequency">采样率</param>
<param name="bitDepth">位深</param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.Audio.G711ACodec.ToPcm(System.Byte[])">
<summary>
转至PCM
</summary>
<param name="g711data"></param>
<returns></returns>
</member>
<member name="M:JT1078.Flv.Audio.G711ACodec.ToG711(System.Byte[])">
<summary>
转至G711
</summary>
<param name="pcmdata"></param>
<returns></returns>
</member>
<member name="T:JT1078.Flv.FlvBufferWriter"> <member name="T:JT1078.Flv.FlvBufferWriter">
<summary> <summary>
<see cref="!:System.Buffers.Writer"/> <see cref="!:System.Buffers.Writer"/>
@@ -210,7 +163,7 @@
Flv编码器 Flv编码器
一个客户端对应一个实例 一个客户端对应一个实例
<para> <para>
当实例不适用时,尽量手动调用下<see cref="M:JT1078.Flv.FlvEncoder.Dispose"/>
当实例不适用时,尽量手动调用下<see cref="!:Dispose"/>
</para> </para>
手动编码 手动编码


二进制
src/JT1078.Flv/nativelibs/x64/libfaac.dll 查看文件


二进制
src/JT1078.Flv/nativelibs/x86/libfaac.dll 查看文件


+ 4
- 2
src/JT1078.Hls/JT1078.Hls.xml 查看文件

@@ -114,7 +114,8 @@
<summary> <summary>
创建M3U8文件 创建M3U8文件
</summary> </summary>
<param name="curTsFileInfo"></param>
<param name="curTsFileInfo">当前ts文件信息</param>
<param name="tsFileInfoQueue">ts文件信息队列</param>
</member> </member>
<member name="M:JT1078.Hls.M3U8FileManage.CreateTsFileInfo(System.String)"> <member name="M:JT1078.Hls.M3U8FileManage.CreateTsFileInfo(System.String)">
<summary> <summary>
@@ -123,11 +124,12 @@
<param name="key"></param> <param name="key"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:JT1078.Hls.M3U8FileManage.CreateTsFile(System.String,System.Byte[])">
<member name="M:JT1078.Hls.M3U8FileManage.CreateTsFile(System.String,System.String,System.Byte[])">
<summary> <summary>
创建TS文件 创建TS文件
</summary> </summary>
<param name="fileName">ts文件路径</param> <param name="fileName">ts文件路径</param>
<param name="key">终端号_通道号(用作目录)</param>
<param name="data">文件内容</param> <param name="data">文件内容</param>
</member> </member>
<member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)"> <member name="M:JT1078.Hls.M3U8FileManage.Clear(System.String,System.Int32)">


src/JT1078.Flv/Audio/AdpcmCodec.cs → src/JT1078.Protocol/Audio/AdpcmCodec.cs 查看文件

@@ -2,26 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;


namespace JT1078.Flv.Audio
namespace JT1078.Protocol.Audio
{ {
public class State
{
/// <summary>
/// 上一个采样数据,当index为0是该值应该为未压缩的原数据
/// </summary>
public short Valprev { get; set; }

/// <summary>
/// 保留数据(未使用)
/// </summary>
public byte Reserved { get; set; }

/// <summary>
/// 上一个block最后一个index,第一个block的index=0
/// </summary>
public byte Index { get; set; }
}
public class AdpcmCodec
public class AdpcmCodec: IAudioCodec
{ {
static readonly int[] indexTable = { static readonly int[] indexTable = {
-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8,
@@ -39,119 +22,122 @@ namespace JT1078.Flv.Audio
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
}; };
public static byte[] ToAdpcm(short[] indata, State state)
{
int val; /* Current input sample value */
int sign; /* Current adpcm sign bit */
int delta; /* Current adpcm output value */
int diff; /* Difference between val and valprev */
int step; /* Stepsize */
int valpred; /* Predicted output value */
int vpdiff; /* Current change to valpred */
int index; /* Current step change index */
int outputbuffer = 0; /* place to keep previous 4-bit value */
int bufferstep; /* toggle between outputbuffer/output */


List<byte> outp = new List<byte>();
short[] inp = indata;
var len = indata.Length;
valpred = state.Valprev;
index = state.Index;
step = stepsizeTable[index];
//public static byte[] ToAdpcm(short[] indata, AdpcmState state)
//{
// int val; /* Current input sample value */
// int sign; /* Current adpcm sign bit */
// int delta; /* Current adpcm output value */
// int diff; /* Difference between val and valprev */
// int step; /* Stepsize */
// int valpred; /* Predicted output value */
// int vpdiff; /* Current change to valpred */
// int index; /* Current step change index */
// int outputbuffer = 0; /* place to keep previous 4-bit value */
// int bufferstep; /* toggle between outputbuffer/output */


bufferstep = 1;
// List<byte> outp = new List<byte>();
// short[] inp = indata;
// var len = indata.Length;
// valpred = state.Valprev;
// index = state.Index;
// step = stepsizeTable[index];


int k = 0;
for (int i = 0; len > 0; len--, i++)
{
val = inp[i];
// bufferstep = 1;


/* Step 1 - compute difference with previous value */
diff = val - valpred;
sign = (diff < 0) ? 8 : 0;
if (sign != 0) diff = (-diff);
// int k = 0;
// for (int i = 0; len > 0; len--, i++)
// {
// val = inp[i];


/* Step 2 - Divide and clamp */
/* Note:
** This code *approximately* computes:
** delta = diff*4/step;
** vpdiff = (delta+0.5)*step/4;
** but in shift step bits are dropped. The net result of this is
** that even if you have fast mul/div hardware you cannot put it to
** good use since the fixup would be too expensive.
*/
delta = 0;
vpdiff = (step >> 3);
// /* Step 1 - compute difference with previous value */
// diff = val - valpred;
// sign = (diff < 0) ? 8 : 0;
// if (sign != 0) diff = (-diff);


if (diff >= step)
{
delta = 4;
diff -= step;
vpdiff += step;
}
step >>= 1;
if (diff >= step)
{
delta |= 2;
diff -= step;
vpdiff += step;
}
step >>= 1;
if (diff >= step)
{
delta |= 1;
vpdiff += step;
}
// /* Step 2 - Divide and clamp */
// /* Note:
// ** This code *approximately* computes:
// ** delta = diff*4/step;
// ** vpdiff = (delta+0.5)*step/4;
// ** but in shift step bits are dropped. The net result of this is
// ** that even if you have fast mul/div hardware you cannot put it to
// ** good use since the fixup would be too expensive.
// */
// delta = 0;
// vpdiff = (step >> 3);


/* Step 3 - Update previous value */
if (sign != 0)
valpred -= vpdiff;
else
valpred += vpdiff;
// if (diff >= step)
// {
// delta = 4;
// diff -= step;
// vpdiff += step;
// }
// step >>= 1;
// if (diff >= step)
// {
// delta |= 2;
// diff -= step;
// vpdiff += step;
// }
// step >>= 1;
// if (diff >= step)
// {
// delta |= 1;
// vpdiff += step;
// }


/* Step 4 - Clamp previous value to 16 bits */
if (valpred > 32767)
valpred = 32767;
else if (valpred < -32768)
valpred = -32768;
// /* Step 3 - Update previous value */
// if (sign != 0)
// valpred -= vpdiff;
// else
// valpred += vpdiff;


/* Step 5 - Assemble value, update index and step values */
delta |= sign;
// /* Step 4 - Clamp previous value to 16 bits */
// if (valpred > 32767)
// valpred = 32767;
// else if (valpred < -32768)
// valpred = -32768;


index += indexTable[delta];
if (index < 0) index = 0;
if (index > 88) index = 88;
step = stepsizeTable[index];
// /* Step 5 - Assemble value, update index and step values */
// delta |= sign;


/* Step 6 - Output value */
if (bufferstep != 0)
{
outputbuffer = (delta << 4) & 0xf0;
}
else
{
outp.Add((byte)((delta & 0x0f) | outputbuffer));
}
bufferstep = bufferstep == 0 ? 1 : 0;
}
// index += indexTable[delta];
// if (index < 0) index = 0;
// if (index > 88) index = 88;
// step = stepsizeTable[index];


/* Output last step, if needed */
if (bufferstep == 0)
outp.Add((byte)outputbuffer);
// /* Step 6 - Output value */
// if (bufferstep != 0)
// {
// outputbuffer = (delta << 4) & 0xf0;
// }
// else
// {
// outp.Add((byte)((delta & 0x0f) | outputbuffer));
// }
// bufferstep = bufferstep == 0 ? 1 : 0;
// }


state.Valprev = (short)valpred;
state.Index = (byte)index;
return outp.ToArray();
}
// /* Output last step, if needed */
// if (bufferstep == 0)
// outp.Add((byte)outputbuffer);

// state.Valprev = (short)valpred;
// state.Index = (byte)index;
// return outp.ToArray();
//}


/// <summary> /// <summary>
/// 将adpcm转为pcm /// 将adpcm转为pcm
/// </summary> /// </summary>
/// <see cref="https://github.com/ctuning/ctuning-programs/blob/master/program/cbench-telecom-adpcm-d/adpcm.c"/> /// <see cref="https://github.com/ctuning/ctuning-programs/blob/master/program/cbench-telecom-adpcm-d/adpcm.c"/>
/// <param name="data"></param>
/// <param name="audio"></param>
/// <param name="audioAttachData"></param>
/// <returns></returns> /// <returns></returns>
public byte[] ToPcm(byte[] data, State state)
public byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData)
{ {
AdpcmState state = (AdpcmState)audioAttachData;
// signed char *inp; /* Input buffer pointer */ // signed char *inp; /* Input buffer pointer */
// short *outp; /* output buffer pointer */ // short *outp; /* output buffer pointer */
int sign; /* Current adpcm sign bit */ int sign; /* Current adpcm sign bit */
@@ -166,7 +152,7 @@ namespace JT1078.Flv.Audio
step = stepsizeTable[index]; step = stepsizeTable[index];


var outdata = new List<byte>(); var outdata = new List<byte>();
var len = data.Length * 2;
var len = audio.Length * 2;
for (int i = 0; len > 0; len--) for (int i = 0; len > 0; len--)
{ {
/* Step 1 - get the delta value */ /* Step 1 - get the delta value */
@@ -176,7 +162,7 @@ namespace JT1078.Flv.Audio
} }
else else
{ {
inputbuffer = data[i++];
inputbuffer = audio[i++];
delta = (inputbuffer >> 4) & 0xf; delta = (inputbuffer >> 4) & 0xf;
} }
bufferstep = !bufferstep; bufferstep = !bufferstep;
@@ -222,7 +208,23 @@ namespace JT1078.Flv.Audio
return outdata.ToArray(); return outdata.ToArray();
} }
} }
public class AdpcmState : IAudioAttachData
{
/// <summary>
/// 上一个采样数据,当index为0是该值应该为未压缩的原数据
/// </summary>
public short Valprev { get; set; }

/// <summary>
/// 保留数据(未使用)
/// </summary>
public byte Reserved { get; set; }


/// <summary>
/// 上一个block最后一个index,第一个block的index=0
/// </summary>
public byte Index { get; set; }
}
public static class AdpcmDecoderExtension public static class AdpcmDecoderExtension
{ {
/// <summary> /// <summary>

+ 47
- 0
src/JT1078.Protocol/Audio/AudioCodecFactory.cs 查看文件

@@ -0,0 +1,47 @@
using JT1078.Protocol.Enums;
using System;
using System.Collections.Generic;
using System.Text;

namespace JT1078.Protocol.Audio
{
public class AudioCodecFactory
{
private readonly AdpcmCodec adpcmCodec = new AdpcmCodec();
private readonly G711ACodec g711ACodec = new G711ACodec();
private readonly G711UCodec g711UCodec = new G711UCodec();
//海思芯片编码的音频需要移除海思头,可能还有其他的海思头
private static byte[] HI = new byte[] { 0x00, 0x01, 0x52, 0x00 };
public byte[] Encode(JT1078AVType aVType,byte[]bodies)
{
byte[] pcm = null;
switch (aVType)
{
case JT1078AVType.ADPCM:
ReadOnlySpan<byte> adpcm = bodies;
if (adpcm.StartsWith(HI)) adpcm = adpcm.Slice(4);
pcm = adpcmCodec.ToPcm(adpcm.Slice(4).ToArray(), new AdpcmState()
{
Valprev = (short)((adpcm[1] << 8) | adpcm[0]),
Index = adpcm[2],
Reserved = adpcm[3]
});
//todo:编码mp3
return pcm;
case JT1078AVType.G711A:
pcm=g711ACodec.ToPcm(bodies, null);
//todo:编码mp3
return pcm;
case JT1078AVType.AACLC:
//直接AAC出去
return bodies;
case JT1078AVType.MP3:
//直接MP3出去
return bodies;
default:

return bodies;
}
}
}
}

src/JT1078.Flv/Audio/FaacEncoder.cs → src/JT1078.Protocol/Audio/FaacEncoder.cs 查看文件

@@ -3,9 +3,8 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using JT1078.Flv.Extensions;


namespace JT1078.Flv.Audio
namespace JT1078.Protocol.Audio
{ {
public interface IFaacEncoder:IDisposable public interface IFaacEncoder:IDisposable
{ {

+ 114
- 0
src/JT1078.Protocol/Audio/G711ACodec.cs 查看文件

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace JT1078.Protocol.Audio
{
public class G711ACodec: IAudioCodec
{
private readonly int SIGN_BIT = 0x80;
private readonly int QUANT_MASK = 0xf;
private readonly int SEG_SHIFT = 4;
private readonly int SEG_MASK = 0x70;
//private readonly short[] seg_end = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF };
short AlawToLinear(byte value)
{
short t;
short seg;
value ^= 0x55;
t = (short)((value & QUANT_MASK) << 4);
seg = (short)((value & SEG_MASK) >> SEG_SHIFT);
switch (seg)
{
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
break;
}
return (value & SIGN_BIT) != 0 ? t : (short)-t;
}

public byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData)
{
byte[] pcmdata = new byte[audio.Length * 2];
for (int i = 0, offset = 0; i < audio.Length; i++)
{
short value = AlawToLinear(audio[i]);
pcmdata[offset++] = (byte)(value & 0xff);
pcmdata[offset++] = (byte)((value >> 8) & 0xff);
}
return pcmdata;
}

//static short Search(short val, short[] table, short size)
//{
// for (short i = 0; i < size; i++)
// {
// if (val <= table[i])
// {
// return i;
// }
// }
// return size;
//}

//byte LinearToAlaw(short pcm_val)
//{
// short mask;
// short seg;
// char aval;
// if (pcm_val >= 0)
// {
// mask = 0xD5;
// }
// else
// {
// mask = 0x55;
// pcm_val = (short)(-pcm_val - 1);
// if (pcm_val < 0)
// {
// pcm_val = 32767;
// }
// }

// //Convert the scaled magnitude to segment number.
// seg = Search(pcm_val, seg_end, 8);

// //Combine the sign, segment, and quantization bits.
// if (seg >= 8)
// {
// //out of range, return maximum value.
// return (byte)(0x7F ^ mask);
// }
// else
// {
// aval = (char)(seg << SEG_SHIFT);
// if (seg < 2) aval |= (char)((pcm_val >> 4) & QUANT_MASK);
// else aval |= (char)((pcm_val >> (seg + 3)) & QUANT_MASK);
// return (byte)(aval ^ mask);
// }
//}

///// <summary>
///// 转至G711
///// </summary>
///// <param name="pcmdata"></param>
///// <returns></returns>
//public byte[] ToG711(byte[] pcmdata)
//{
// byte[] g711data = new byte[pcmdata.Length / 2];
// for (int i = 0, k = 0; i < pcmdata.Length; i += 2, k++)
// {
// short v = (short)((pcmdata[i + 1] << 8) | (pcmdata[i]));
// g711data[k] = LinearToAlaw(v);
// }
// return g711data;
//}
}
}

src/JT1078.Flv/Audio/G711UCodec.cs → src/JT1078.Protocol/Audio/G711UCodec.cs 查看文件

@@ -2,13 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;


namespace JT1078.Flv.Audio
namespace JT1078.Protocol.Audio
{ {
public class G711UCodec
public class G711UCodec: IAudioCodec
{ {
/* 16384 entries per table (16 bit) */ /* 16384 entries per table (16 bit) */
readonly byte[] linearToUlawTable = new byte[65536]; readonly byte[] linearToUlawTable = new byte[65536];

/* 16384 entries per table (8 bit) */ /* 16384 entries per table (8 bit) */
readonly short[] ulawToLinearTable = new short[256]; readonly short[] ulawToLinearTable = new short[256];
readonly int SIGN_BIT = 0x80; readonly int SIGN_BIT = 0x80;
@@ -90,23 +89,26 @@ namespace JT1078.Flv.Audio
return pcmSamples; return pcmSamples;
} }


private byte[] Pcm16ToUlaw(byte[] pcmSamples)
{
short[] dst = new short[pcmSamples.Length / 2];
byte[] ulawSamples = new byte[pcmSamples.Length / 2];
for (int i = 0, k = 0; i < pcmSamples.Length;)
{
dst[k++] = (short)((pcmSamples[i++] & 0xff) | ((pcmSamples[i++] & 0xff) << 8));
}
for (int i = 0, k = 0; i < dst.Length; i++)
{
ulawSamples[k++] = Linear2ulaw(dst[i]);
}
return ulawSamples;
}
//private byte[] Pcm16ToUlaw(byte[] pcmSamples)
//{
// short[] dst = new short[pcmSamples.Length / 2];
// byte[] ulawSamples = new byte[pcmSamples.Length / 2];
// for (int i = 0, k = 0; i < pcmSamples.Length;)
// {
// dst[k++] = (short)((pcmSamples[i++] & 0xff) | ((pcmSamples[i++] & 0xff) << 8));
// }
// for (int i = 0, k = 0; i < dst.Length; i++)
// {
// ulawSamples[k++] = Linear2ulaw(dst[i]);
// }
// return ulawSamples;
//}


public byte[] ToPcm(byte[] data) => UlawToPcm16(data);
//public byte[] ToG711(byte[] data) => Pcm16ToUlaw(data);


public byte[] ToG711(byte[] data) => Pcm16ToUlaw(data);
public byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData)
{
return UlawToPcm16(audio);
}
} }
} }

+ 10
- 0
src/JT1078.Protocol/Audio/IAudioAttachData.cs 查看文件

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace JT1078.Protocol.Audio
{
public interface IAudioAttachData
{
}
}

+ 11
- 0
src/JT1078.Protocol/Audio/IAudioCodec.cs 查看文件

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace JT1078.Protocol.Audio
{
public interface IAudioCodec
{
byte[] ToPcm(byte[] audio, IAudioAttachData audioAttachData);
}
}

+ 6
- 1
src/JT1078.Protocol/JT1078.Protocol.csproj 查看文件

@@ -14,7 +14,7 @@
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl>
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>1.0.3</Version>
<Version>1.0.4-preview1</Version>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -24,6 +24,11 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup> </ItemGroup>


<ItemGroup>
<Compile Remove="Audio\FaacEncoder.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\LICENSE"> <None Include="..\..\LICENSE">


正在加载...
取消
保存