Browse Source

实现音频部分,适用于windows x64和x86平台

pull/14/head
Pangpang Don 3 years ago
parent
commit
0415735fed
5 changed files with 220 additions and 82 deletions
  1. +27
    -25
      src/JT1078.Flv.Test/Audio/AudioTest.cs
  2. +13
    -5
      src/JT1078.Protocol/Audio/AudioCodecFactory.cs
  3. +87
    -47
      src/JT1078.Protocol/Audio/FaacEncoder.cs
  4. +93
    -0
      src/JT1078.Protocol/Extensions/InteropExtensions.cs
  5. +0
    -5
      src/JT1078.Protocol/JT1078.Protocol.csproj

+ 27
- 25
src/JT1078.Flv.Test/Audio/AudioTest.cs View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using JT1078.Protocol.Audio;
using Xunit;
using Xunit.Abstractions;

@@ -11,7 +12,7 @@ namespace JT1078.Flv.Test.Audio
{
public class AudioTest
{
readonly ITestOutputHelper output;
private readonly ITestOutputHelper output;

public AudioTest(ITestOutputHelper output)
{
@@ -19,8 +20,8 @@ namespace JT1078.Flv.Test.Audio
}

/// <summary>
/// 8000采样,单通道,16位pcm->aac
/// <para>8k-1ch-16bit</para>
/// 8000采样,单通道,16位pcm-&gt;aac
/// <para> 8k-1ch-16bit </para>
/// </summary>
[Fact(DisplayName = "pcm编码aac")]
public void Test1()
@@ -28,29 +29,30 @@ namespace JT1078.Flv.Test.Audio
//todo:音频暂时先放下
ReadOnlySpan<byte> fileData = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Audio/Files/testpacket.pcm"));
//注意 这里为了可以判断音频是否可用,因此使用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(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

internal static class Ex
{
public static void AppendBytesToFile(this byte[] fileBytes, string fileName)
{
@@ -59,4 +61,4 @@ namespace JT1078.Flv.Test.Audio
fileStream.Close();
}
}
}
}

+ 13
- 5
src/JT1078.Protocol/Audio/AudioCodecFactory.cs View File

@@ -10,9 +10,13 @@ namespace JT1078.Protocol.Audio
private readonly AdpcmCodec adpcmCodec = new AdpcmCodec();
private readonly G711ACodec g711ACodec = new G711ACodec();
private readonly G711UCodec g711UCodec = new G711UCodec();

private readonly IFaacEncoder faacEncoder = new FaacEncoder(8000, 1, 16);

//海思芯片编码的音频需要移除海思头,可能还有其他的海思头
private static byte[] HI = new byte[] { 0x00, 0x01, 0x52, 0x00 };
public byte[] Encode(JT1078AVType aVType,byte[]bodies)

public byte[] Encode(JT1078AVType aVType, byte[] bodies)
{
byte[] pcm = null;
switch (aVType)
@@ -27,21 +31,25 @@ namespace JT1078.Protocol.Audio
Reserved = adpcm[3]
});
//todo:编码mp3
return pcm;
return faacEncoder.Encode(pcm);

case JT1078AVType.G711A:
pcm=g711ACodec.ToPcm(bodies, null);
pcm = g711ACodec.ToPcm(bodies, null);
//todo:编码mp3
return pcm;
return faacEncoder.Encode(pcm);

case JT1078AVType.AACLC:
//直接AAC出去
return bodies;

case JT1078AVType.MP3:
//直接MP3出去
return bodies;

default:

return bodies;
}
}
}
}
}

+ 87
- 47
src/JT1078.Protocol/Audio/FaacEncoder.cs View File

@@ -3,20 +3,71 @@ using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Runtime.InteropServices;
using JT1078.Protocol.Extensions;

namespace JT1078.Protocol.Audio
{
public interface IFaacEncoder:IDisposable
public interface IFaacEncoder : IDisposable
{
public int frameSize { get; }

byte[] Encode(byte[] bytes);
}

public class FaacEncoder : IFaacEncoder
{
private IFaacEncoder encoder { get; }

public int frameSize => encoder.frameSize;

public FaacEncoder(int sampleRate, int channels, int sampleBit, bool adts = false)
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
case PlatformID.WinCE:
if (Environment.Is64BitProcess)
{
encoder = new FaacEncoder_x64(sampleRate, channels, sampleBit, adts);
}
else
{
encoder = new FaacEncoder_x86(sampleRate, channels, sampleBit, adts);
}
break;

case PlatformID.MacOSX:
case PlatformID.Unix:
//TODO: 适配linux
break;

default:
throw new ApplicationException("system not support.");
break;
}
}

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

public byte[] Encode(byte[] bytes)
{
return encoder.Encode(bytes);
}
}

public class FaacEncoder_x86 : IFaacEncoder
{
private IntPtr faacEncHandle = IntPtr.Zero;
private readonly int inputSamples;
private readonly int maxOutput;
public readonly int frameSize;
public int frameSize { get; }
private List<byte> frameCache = new List<byte>();

public FaacEncoder_x86(int sampleRate, int channels, int sampleBit, bool adts = false)
{
var inputSampleBytes = new byte[4];
@@ -65,42 +116,34 @@ namespace JT1078.Protocol.Audio
}
}

const string DLLFile = @"/nativelibs/x86/libfaac.dll";
private const string DLLFile = @"\nativelibs\x86\libfaac.dll";

[DllImport(DLLFile, EntryPoint = "faacEncGetVersion", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncGetVersion(char **faac_id_string, char **faac_copyright_string);
private extern static int FaacEncGetVersion(ref IntPtr faac_id_string, ref IntPtr faac_copyright_string);
private static extern int FaacEncGetVersion(ref IntPtr faac_id_string, ref IntPtr faac_copyright_string);

[DllImport(DLLFile, EntryPoint = "faacEncGetCurrentConfiguration", CallingConvention = CallingConvention.StdCall)]
//faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
private extern static IntPtr FaacEncGetCurrentConfiguration(IntPtr hEncoder);

private static extern IntPtr FaacEncGetCurrentConfiguration(IntPtr hEncoder);

[DllImport(DLLFile, EntryPoint = "faacEncSetConfiguration", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder,faacEncConfigurationPtr config);
private extern static int FaacEncSetConfiguration(IntPtr hEncoder, IntPtr config);
private static extern int FaacEncSetConfiguration(IntPtr hEncoder, IntPtr config);

[DllImport(DLLFile, EntryPoint = "faacEncOpen", CallingConvention = CallingConvention.StdCall)]
//faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes);
private extern static IntPtr FaacEncOpen(int sampleRate, int numChannels, byte[] inputSamples, byte[] maxOutputBytes);

private static extern IntPtr FaacEncOpen(int sampleRate, int numChannels, byte[] inputSamples, byte[] maxOutputBytes);

[DllImport(DLLFile, EntryPoint = "faacEncGetDecoderSpecificInfo", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer,unsigned long *pSizeOfDecoderSpecificInfo);
private extern static IntPtr FaacEncGetDecoderSpecificInfo(IntPtr hEncoder, ref IntPtr ppBuffer, ref int pSizeOfDecoderSpecificInfo);

private static extern IntPtr FaacEncGetDecoderSpecificInfo(IntPtr hEncoder, ref IntPtr ppBuffer, ref int pSizeOfDecoderSpecificInfo);

[DllImport(DLLFile, EntryPoint = "faacEncEncode", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize);
private extern static int FaacEncEncode(IntPtr hEncoder, IntPtr inputBuffer, int samplesInput, IntPtr outputBuffer, int bufferSize);
private static extern int FaacEncEncode(IntPtr hEncoder, IntPtr inputBuffer, int samplesInput, IntPtr outputBuffer, int bufferSize);
[DllImport(DLLFile, EntryPoint = "faacEncEncode", CallingConvention = CallingConvention.StdCall)]
private extern static int FaacEncEncode(IntPtr hEncoder, byte[] inputBuffer, int samplesInput, byte[] outputBuffer, int bufferSize);
private static extern int FaacEncEncode(IntPtr hEncoder, byte[] inputBuffer, int samplesInput, byte[] outputBuffer, int bufferSize);

[DllImport(DLLFile, EntryPoint = "faacEncClose", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncClose(faacEncHandle hEncoder);
private extern static IntPtr FaacEncClose(IntPtr hEncoder);
private static extern IntPtr FaacEncClose(IntPtr hEncoder);

#region 配置结构

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct FaacEncConfiguration
{
@@ -183,21 +226,24 @@ namespace JT1078.Protocol.Audio
WAVE 4.0 2, 0, 1, 3
WAVE 5.0 2, 0, 1, 3, 4
WAVE 5.1 2, 0, 1, 4, 5, 3
AIFF 5.1 2, 0, 3, 1, 4, 5
AIFF 5.1 2, 0, 3, 1, 4, 5
*/

[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 64)]
public int[] channel_map;

}
#endregion

#endregion 配置结构
}

public class FaacEncoder_x64 : IFaacEncoder
{
private IntPtr faacEncHandle = IntPtr.Zero;
private readonly int inputSamples;
private readonly int maxOutput;
public readonly int frameSize;
public int frameSize { get; }
private List<byte> frameCache = new List<byte>();

public FaacEncoder_x64(int sampleRate, int channels, int sampleBit, bool adts = false)
{
var inputSampleBytes = new byte[4];
@@ -246,41 +292,34 @@ namespace JT1078.Protocol.Audio
}
}

const string DLLFile = @"/nativelibs/x64/libfaac.dll";
private const string DLLFile = @"\nativelibs\x64\libfaac.dll";

[DllImport(DLLFile, EntryPoint = "faacEncGetVersion", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncGetVersion(char **faac_id_string, char **faac_copyright_string);
private extern static int FaacEncGetVersion(ref IntPtr faac_id_string, ref IntPtr faac_copyright_string);
private static extern int FaacEncGetVersion(ref IntPtr faac_id_string, ref IntPtr faac_copyright_string);

[DllImport(DLLFile, EntryPoint = "faacEncGetCurrentConfiguration", CallingConvention = CallingConvention.StdCall)]
//faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
private extern static IntPtr FaacEncGetCurrentConfiguration(IntPtr hEncoder);
private static extern IntPtr FaacEncGetCurrentConfiguration(IntPtr hEncoder);

[DllImport(DLLFile, EntryPoint = "faacEncSetConfiguration", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder,faacEncConfigurationPtr config);
private extern static int FaacEncSetConfiguration(IntPtr hEncoder, IntPtr config);
private static extern int FaacEncSetConfiguration(IntPtr hEncoder, IntPtr config);

[DllImport(DLLFile, EntryPoint = "faacEncOpen", CallingConvention = CallingConvention.StdCall)]
//faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes);
private extern static IntPtr FaacEncOpen(int sampleRate, int numChannels, byte[] inputSamples, byte[] maxOutputBytes);

private static extern IntPtr FaacEncOpen(int sampleRate, int numChannels, byte[] inputSamples, byte[] maxOutputBytes);

[DllImport(DLLFile, EntryPoint = "faacEncGetDecoderSpecificInfo", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer,unsigned long *pSizeOfDecoderSpecificInfo);
private extern static IntPtr FaacEncGetDecoderSpecificInfo(IntPtr hEncoder, ref IntPtr ppBuffer, ref int pSizeOfDecoderSpecificInfo);

private static extern IntPtr FaacEncGetDecoderSpecificInfo(IntPtr hEncoder, ref IntPtr ppBuffer, ref int pSizeOfDecoderSpecificInfo);

[DllImport(DLLFile, EntryPoint = "faacEncEncode", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize);
private extern static int FaacEncEncode(IntPtr hEncoder, IntPtr inputBuffer, int samplesInput, IntPtr outputBuffer, int bufferSize);
private static extern int FaacEncEncode(IntPtr hEncoder, IntPtr inputBuffer, int samplesInput, IntPtr outputBuffer, int bufferSize);
[DllImport(DLLFile, EntryPoint = "faacEncEncode", CallingConvention = CallingConvention.StdCall)]
private extern static int FaacEncEncode(IntPtr hEncoder, byte[] inputBuffer, int samplesInput, byte[] outputBuffer, int bufferSize);
private static extern int FaacEncEncode(IntPtr hEncoder, byte[] inputBuffer, int samplesInput, byte[] outputBuffer, int bufferSize);

[DllImport(DLLFile, EntryPoint = "faacEncClose", CallingConvention = CallingConvention.StdCall)]
//int FAACAPI faacEncClose(faacEncHandle hEncoder);
private extern static IntPtr FaacEncClose(IntPtr hEncoder);
private static extern IntPtr FaacEncClose(IntPtr hEncoder);

#region 配置结构

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct FaacEncConfiguration
{
@@ -363,12 +402,13 @@ namespace JT1078.Protocol.Audio
WAVE 4.0 2, 0, 1, 3
WAVE 5.0 2, 0, 1, 3, 4
WAVE 5.1 2, 0, 1, 4, 5, 3
AIFF 5.1 2, 0, 3, 1, 4, 5
AIFF 5.1 2, 0, 3, 1, 4, 5
*/

[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 64)]
public int[] channel_map;

}
#endregion

#endregion 配置结构
}
}
}

+ 93
- 0
src/JT1078.Protocol/Extensions/InteropExtensions.cs View File

@@ -0,0 +1,93 @@
using System;
using System.Runtime.InteropServices;

namespace JT1078.Protocol.Extensions
{
public static class InteropExtensions
{
public static T BytesToStruct<T>(byte[] bytes, int startIndex, int length)
{
T local;
T local2;
if (bytes == null)
{
local2 = default;
local = local2;
}
else if (bytes.Length <= 0)
{
local2 = default;
local = local2;
}
else
{
IntPtr destination = Marshal.AllocHGlobal(length);
try
{
Marshal.Copy(bytes, startIndex, destination, length);
local = (T)Marshal.PtrToStructure(destination, typeof(T));
}
catch (Exception exception)
{
throw new Exception("Error in BytesToStruct ! " + exception.Message);
}
finally
{
Marshal.FreeHGlobal(destination);
}
}
return local;
}

public static void IntPtrSetValue(IntPtr intptr, object structObj)
{
IntPtrSetValue(intptr, StructToBytes(structObj));
}

public static void IntPtrSetValue(IntPtr intptr, byte[] bytes)
{
Marshal.Copy(bytes, 0, intptr, bytes.Length);
}

public static T IntPtrToStruct<T>(IntPtr intptr)
{
int index = 0;
return IntPtrToStruct<T>(intptr, index, Marshal.SizeOf(typeof(T)));
}

public static T IntPtrToStruct<T>(IntPtr intptr, int index, int length)
{
byte[] destination = new byte[length];
Marshal.Copy(intptr, destination, index, length);
return BytesToStruct<T>(destination, 0, destination.Length);
}

public static byte[] StructToBytes(object structObj)
{
int size = Marshal.SizeOf(structObj);
return StructToBytes(structObj, size);
}

public static byte[] StructToBytes(object structObj, int size)
{
byte[] buffer2;
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structObj, ptr, false);
byte[] destination = new byte[size];
Marshal.Copy(ptr, destination, 0, size);
buffer2 = destination;
}
catch (Exception exception)
{
throw new Exception("Error in StructToBytes ! " + exception.Message);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return buffer2;
}
}
}

+ 0
- 5
src/JT1078.Protocol/JT1078.Protocol.csproj View File

@@ -25,11 +25,6 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>


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


Loading…
Cancel
Save