-
Notifications
You must be signed in to change notification settings - Fork 3
/
MakeTest.java
202 lines (198 loc) · 6.39 KB
/
MakeTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.test;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.apache.log4j.Logger;
import org.junit.Test;
import com.ttProject.container.flv.FlvHeaderTag;
import com.ttProject.container.flv.FlvTagWriter;
import com.ttProject.frame.AudioFrame;
import com.ttProject.frame.IFrame;
import com.ttProject.frame.pcmalaw.PcmalawFrameAnalyzer;
import com.ttProject.nio.channels.ByteReadChannel;
import com.ttProject.nio.channels.IReadChannel;
import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IAudioSamples.Format;
import com.xuggle.xuggler.IStreamCoder.Direction;
/**
* beep音からG711のflvを作りたい
* @author taktod
*/
public class MakeTest {
/** ロガー */
private Logger logger = Logger.getLogger(MakeTest.class);
/** audioデータの動作カウンター */
private int audioCounter = 0;
@Test
public void test() {
try {
logger.info("開始");
init();
IStreamCoder coder = IStreamCoder.make(Direction.ENCODING, ICodec.ID.CODEC_ID_PCM_ALAW);
coder.setSampleRate(8000);
coder.setChannels(1);
processConvert(coder);
logger.info("おわり");
}
catch(Exception e) {
logger.error("例外発生", e);
}
}
/**
* 初期化
*/
private void init() {
audioCounter = 0;
}
/**
* 変換実施
*/
private void processConvert(IStreamCoder audioCoder) {
IAudioResampler resampler = null;
try {
if(audioCoder != null) {
ICodec codec = audioCoder.getCodec();
IAudioSamples.Format findFormat = null;
for(IAudioSamples.Format format : codec.getSupportedAudioSampleFormats()) {
if(findFormat == null) {
findFormat = format;
}
if(findFormat == IAudioSamples.Format.FMT_S16) {
findFormat = format;
break;
}
}
if(findFormat == null) {
throw new Exception("対応している音声フォーマットが不明です。");
}
audioCoder.setSampleFormat(findFormat);
if(audioCoder.open(null, null) < 0) {
throw new Exception("音声コーダーが開けませんでした。");
}
}
IPacket packet = IPacket.make();
PcmalawFrameAnalyzer analyzer = new PcmalawFrameAnalyzer();
FlvTagWriter writer = new FlvTagWriter("output.flv");
FlvHeaderTag headerTag = new FlvHeaderTag();
headerTag.setAudioFlag(true);
headerTag.setVideoFlag(false);
writer.addContainer(headerTag);
long pts = 0;
while(true) {
if(audioCoder != null) {
IAudioSamples samples = samples();
if(samples.getSampleRate() != audioCoder.getSampleRate()
|| samples.getFormat() != audioCoder.getSampleFormat()
|| samples.getChannels() != audioCoder.getChannels()) {
if(resampler == null) {
// resamplerを作る必要あり。
resampler = IAudioResampler.make(
audioCoder.getChannels(), samples.getChannels(),
audioCoder.getSampleRate(), samples.getSampleRate(),
audioCoder.getSampleFormat(), samples.getFormat());
}
IAudioSamples spl = IAudioSamples.make(1024, audioCoder.getChannels());
int retVal = resampler.resample(spl, samples, samples.getNumSamples());
if(retVal <= 0) {
throw new Exception("音声サンプル失敗しました。");
}
samples = spl;
}
int samplesConsumed = 0;
while(samplesConsumed < samples.getNumSamples()) {
int retval = audioCoder.encodeAudio(packet, samples, samplesConsumed);
if(retval < 0) {
throw new Exception("変換失敗");
}
samplesConsumed += retval;
if(packet.isComplete()) {
packet.setDts(packet.getPts());
logger.info(packet.getPts());
logger.info(1 / packet.getTimeBase().getDouble());
IReadChannel readChannel = new ByteReadChannel(packet.getByteBuffer());
IFrame frame = null;
while((frame = analyzer.analyze(readChannel)) != null) {
logger.info(frame);
if(frame instanceof AudioFrame) {
AudioFrame f = (AudioFrame)frame;
f.setPts(pts);
f.setTimebase(f.getSampleRate());
pts += f.getSampleNum();
writer.addFrame(8, frame);
}
}
frame = analyzer.getRemainFrame();
if(frame != null) {
logger.info(frame);
if(frame instanceof AudioFrame) {
AudioFrame f = (AudioFrame)frame;
f.setPts(pts);
f.setTimebase(f.getSampleRate());
pts += f.getSampleNum();
writer.addFrame(8, frame);
}
}
}
}
if(samples.getPts() > 1000000) {
break;
}
}
else {
break;
}
}
writer.prepareTailer();
}
catch(Exception e) {
logger.error("例外発生", e);
}
finally {
if(audioCoder.isOpen()) {
audioCoder.close();
}
}
}
/**
* ラの音のaudioデータをつくって応答する。
* @return
*/
private IAudioSamples samples() {
// とりあえずラの音で1024サンプル数つくることにする。
int samplingRate = 44100;
int tone = 440;
int bit = 16;
int channels = 2;
int samplesNum = 1024;
ByteBuffer buffer = ByteBuffer.allocate((int)samplesNum * bit * channels / 8);
double rad = tone * 2 * Math.PI / samplingRate; // 各deltaごとの回転数
double max = (1 << (bit - 2)) - 1; // 振幅の大きさ(音の大きさ)
buffer.order(ByteOrder.LITTLE_ENDIAN); // xuggleで利用するデータはlittleEndianなのでlittleEndianを使うようにする。
long startPos = 1000 * audioCounter / 44100 * 1000;
for(int i = 0;i < samplesNum / 8;i ++, audioCounter ++) {
short data = (short)(Math.sin(rad * audioCounter) * max);
for(int j = 0;j < channels;j ++) {
buffer.putShort(data);
}
}
buffer.flip();
int snum = (int)(buffer.remaining() * 8/bit/channels);
IAudioSamples samples = IAudioSamples.make(snum, channels, Format.FMT_S16);
samples.getData().put(buffer.array(), 0, 0, buffer.remaining());
samples.setComplete(true, snum, samplingRate, channels, Format.FMT_S16, 0);
// このtimestampの設定は必要っぽい
samples.setTimeStamp(startPos);
// こっちはいらないっぽい。ただし別の関数っぽいので、やっとくにこしたことはなさそうな・・・
samples.setPts(startPos);
return samples;
}
}