原文地址:http://blog.csdn.net/qq_25497621/article/details/61920062
公司最近在做自己的项目,关于音视频编辑,还有图片的编辑方面。上网搜了一下,哇!很烦,大都用的FFmpeg进行编码、解码,再进行
相应的操作!国外也有大牛,封装了jar,大家搜一下就很多了!在这也不多说了,用FFmpeg进行格式转换,裁剪等等操作的,也可以在
GitHub上搜一下,有安卓版的已经编译好的开源项目demo(大多用的FFmpeg的命令行进行操作)!好吧!本来还想多向公司争取点时间
对这方面好好研究一下!既然都有现成的了,就拉过来改吧改吧!(一向讨厌伸手主义,但是貌似自己也入坑了!妈蛋,谁让时间不够用
呢!)
音频录制
效果图如下:
![这里写图片描述](https://img-blog.csdn.net/20170313161042066?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjU0OTc2MjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
音频编辑分两块:一、录音;二、音频编辑
录音的界面如下(很炫!有没有!哈哈):
(这是我从GitHub上找的一个demo,改了很长时间才实现的)
主要功能:
(1)、音频的录制
(2)、录制过程中添加标记
(3)、录音的暂停开始
(4)、录音完成(pcm格式转为wav格式)
下面对界面的制作稍加分析:
注:源代码也不是我写的,我只是在其上作了修改和添加(勿喷)!
画布类:
package com.jwzt.jwzt_procaibian.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 该类只是一个初始化surfaceview的封装
* @author tcx
*/
public class WaveSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder holder;
private int line_off;//上下边距距离
public int getLine_off() {
return line_off;
}
public void setLine_off(int line_off) {
this.line_off = line_off;
}
public WaveSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.holder = getHolder();
holder.addCallback(this);
}
/**
* @author tcx
* init surfaceview
*/
public void initSurfaceView( final SurfaceView sfv){
new Thread(){
public void run() {
Canvas canvas = sfv.getHolder().lockCanvas(
new Rect(0, 0, sfv.getWidth(), sfv.getHeight()));// 关键:获取画布
if(canvas==null){
return;
}
//canvas.drawColor(Color.rgb(241, 241, 241));// 清除背景
canvas.drawARGB(255, 42, 53, 82);
int height = sfv.getHeight()-line_off;
Paint paintLine =new Paint();
Paint centerLine =new Paint();
Paint circlePaint = new Paint();
circlePaint.setColor(Color.rgb(246, 131, 126));
paintLine.setColor(Color.rgb(255, 255, 255));
paintLine.setStrokeWidth(2);
circlePaint.setAntiAlias(true);
canvas.drawLine(sfv.getWidth()/2, 0, sfv.getWidth()/2, sfv.getHeight(), circlePaint);//垂直的线
centerLine.setColor(Color.rgb(39, 199, 175));
canvas.drawLine(0, line_off/2, sfv.getWidth(), line_off/2, paintLine);//最上面的那根线
canvas.drawLine(0, sfv.getHeight()-line_off/2-1, sfv.getWidth(), sfv.getHeight()-line_off/2-1, paintLine);//最下面的那根线
canvas.drawLine(0, height*0.5f+line_off/2, sfv.getWidth() ,height*0.5f+line_off/2, centerLine);//中心线
sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
};
}.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
initSurfaceView(this);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
- 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
}
上面的这个类就很简单了,就是利用surfaceView制作一个画布
接下来就开始在画布上搞事情了
先初始化好录音机:
/**
* 初始化录音
*/
private void initAudio(){
recBufSize = AudioRecord.getMinBufferSize(FREQUENCY,
CHANNELCONGIFIGURATION, AUDIOENCODING);//设置录音缓冲区(一般为20ms,1280)
audioRecord = new AudioRecord(AUDIO_SOURCE,// 指定音频来源,这里为麦克风
FREQUENCY, // 16000HZ采样频率
CHANNELCONGIFIGURATION,// 录制通道
AUDIO_SOURCE,// 录制编码格式
recBufSize);
waveCanvas = new WaveCanvas();//在下面哦
waveCanvas.baseLine = waveSfv.getHeight() / 2;
waveCanvas.Start(audioRecord, recBufSize, waveSfv, mFileName, U.DATA_DIRECTORY, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return true;
}
},(swidth-DensityUtil.dip2px(10))/2,this);
恩,接着看浪线怎么画:
**WaveCanvas类在此**
package com.jwzt.jwzt_procaibian.widget;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Rect;
import android.media.AudioRecord;
import android.os.AsyncTask;
import android.os.Handler.Callback;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceView;
import com.jwzt.jwzt_procaibian.R;
import com.jwzt.jwzt_procaibian.inter.CurrentPosInterface;
import com.jwzt.jwzt_procaibian.utils.Pcm2Wav;
/**
* 录音和写入文件使用了两个不同的线程,以免造成卡机现象
* 录音波形绘制
* @author tcx
*
*/
public class WaveCanvas {
private ArrayList<Short> inBuf = new ArrayList<Short>();//缓冲区数据
private ArrayList<byte[]> write_data = new ArrayList<byte[]>();//写入文件数据
public boolean isRecording = false;// 录音线程控制标记
private boolean isWriting = false;// 录音线程控制标记
private int line_off ;//上下边距的距离
public int rateX = 30;//控制多少帧取一帧
public int rateY = 1; // Y轴缩小的比例 默认为1
public int baseLine = 0;// Y轴基线
private AudioRecord audioRecord;
int recBufSize;
private int marginRight=30;//波形图绘制距离右边的距离
private int draw_time = 1000 / 200;//两次绘图间隔的时间
private float divider = 0.1f;//为了节约绘画时间,每0.2个像素画一个数据
long c_time;
private String savePcmPath ;//保存pcm文件路径
private String saveWavPath;//保存wav文件路径
private Paint circlePaint;
private Paint center;
private Paint paintLine;
private Paint mPaint;
private Context mContext;
private ArrayList<Float> markList=new ArrayList<Float>();
private int readsize;
private Map<Integer,Integer> markMap=new HashMap<Integer,Integer>();
private boolean isPause=false;
private CurrentPosInterface mCurrentPosInterface;
private Paint progressPaint;
private Paint paint;
private Paint bottomHalfPaint;
private Paint darkPaint;
private Paint markTextPaint;
private Bitmap markIcon;
private int bitWidth;
private int bitHeight;
private int start;
/**
* 开始录音
* @param audioRecord
* @param recBufSize
* @param sfv
* @param audioName
*/
public void Start(AudioRecord audioRecord, int recBufSize, SurfaceView sfv
,String audioName,String path,Callback callback,int width,Context context) {
this.audioRecord = audioRecord;
isRecording = true;
isWriting = true;
this.recBufSize = recBufSize;
savePcmPath = path + audioName +".pcm";
saveWavPath = path + audioName +".wav";
this.mContext=context;
init();
new Thread(new WriteRunnable()).start();//开线程写文件
new RecordTask(audioRecord, recBufSize, sfv, mPaint,callback).execute();
this.marginRight=width;
}
public void init(){
circlePaint = new Paint();//画圆
circlePaint.setColor(Color.rgb(246, 131, 126));//设置上圆的颜色
center = new Paint();
center.setColor(Color.rgb(39, 199, 175));// 画笔为color
center.setStrokeWidth(1);// 设置画笔粗细
center.setAntiAlias(true);
center.setFilterBitmap(true);
center.setStyle(Style.FILL);
paintLine =new Paint();
paintLine.setColor(Color.rgb(255, 255, 255));
paintLine.setStrokeWidth(2);// 设置画笔粗细
mPaint = new Paint();
mPaint.setColor(Color.rgb(39, 199, 175));// 画笔为color
mPaint.setStrokeWidth(1);// 设置画笔粗细
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
mPaint.setStyle(Paint.Style.FILL);
//标记 部分画笔
progressPaint=new Paint();
progressPaint.setColor(mContext.getResources().getColor(R.color.vine_green));
paint=new Paint();
bottomHalfPaint=new Paint();
darkPaint=new Paint();
darkPaint.setColor(mContext.getResources().getColor(R.color.dark_black));
bottomHalfPaint.setColor(mContext.getResources().getColor(R.color.hui));
markTextPaint=new Paint();
markTextPaint.setColor(mContext.getResources().getColor(R.color.hui));
markTextPaint.setTextSize(18);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setFilterBitmap(true);
markIcon=((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.edit_mark)).getBitmap();
bitWidth = markIcon.getWidth();
bitHeight = markIcon.getHeight();
}
/**
* 停止录音
*/
public void Stop() {
isRecording = false;
isPause=true;
audioRecord.stop();
}
/**
* pause recording audio
*/
public void pause(){
isPause=true;
}
/**
* restart recording audio
*/
public void reStart(){
isPause=false;
}
/**
* 清楚数据
*/
public void clear(){
inBuf.clear();// 清除
}
/**
* 异步录音程序
* @author cokus
*
*/
class RecordTask extends AsyncTask<Object, Object, Object> {
private int recBufSize;
private AudioRecord audioRecord;
private SurfaceView sfv;// 画板
private Paint mPaint;// 画笔
private Callback callback;
private boolean isStart =false;
private Rect srcRect;
private Rect destRect;
private Rect bottomHalfBgRect;
public RecordTask(AudioRecord audioRecord, int recBufSize,
SurfaceView sfv, Paint mPaint,Callback callback) {
this.audioRecord = audioRecord;
this.recBufSize = recBufSize;
this.sfv = sfv;
line_off = ((WaveSurfaceView)sfv).getLine_off();
this.mPaint = mPaint;
this.callback = callback;
inBuf.clear();// 清除
}
@Override
protected Object doInBackground(Object... params) {
try {
short[] buffer = new short[recBufSize];
audioRecord.startRecording();// 开始录制
while (isRecording) {
while(!isPause){
// 从MIC保存数据到缓冲区
readsize = audioRecord.read(buffer, 0,
recBufSize);
synchronized (inBuf) {
for (int i = 0; i < readsize; i += rateX) {
inBuf.add(buffer[i]);
}
}
publishProgress();//更新主线程中的UI
if (AudioRecord.ERROR_INVALID_OPERATION != readsize) {
synchronized (write_data) {
byte bys[] = new byte[readsize*2];
//因为arm字节序问题,所以需要高低位交换
for (int i = 0; i < readsize; i++) {
byte ss[] = getBytes(buffer[i]);
bys[i*2] =ss[0];
bys[i*2+1] = ss[1];
}
write_data.add(bys);
}
}
}
}
isWriting = false;
} catch (Throwable t) {
Message msg = new Message();
msg.arg1 =-2;
msg.obj=t.getMessage();
callback.handleMessage(msg);
}
return null;
}
@Override
protected void onProgressUpdate(Object... values) {
long time = new Date().getTime();
if(time - c_time >= draw_time){
ArrayList<Short> buf = new ArrayList<Short>();
synchronized (inBuf) {
if (inBuf.size() == 0)
return;
while(inBuf.size() > (sfv.getWidth()-marginRight) / divider){
inBuf.remove(0);
}
buf = (ArrayList<Short>) inBuf.clone();// 保存
}
SimpleDraw(buf, sfv.getHeight()/2);// 把缓冲区数据画出来
c_time = new Date().getTime();
}
super.onProgressUpdate(values);
}
public byte[] getBytes(short s)
{
byte[] buf = new byte[2];
for (int i = 0; i < buf.length; i++)
{
buf[i] = (byte) (s & 0x00ff);
s >>= 8;
}
return buf;
}
/**
* 绘制指定区域
*
* @param buf
* 缓冲区
* @param baseLine
* Y轴基线
*/
void SimpleDraw(ArrayList<Short> buf, int baseLine) {
if (!isRecording)
return;
rateY = (65535 /2/ (sfv.getHeight()-line_off));
for (int i = 0; i < buf.size(); i++) {
byte bus[] = getBytes(buf.get(i));
buf.set(i, (short)((0x0000 | bus[1]) << 8 | bus[0]));//高低位交换
}
Canvas canvas = sfv.getHolder().lockCanvas(
new Rect(0, 0, sfv.getWidth(), sfv.getHeight()));// 关键:获取画布
if(canvas==null)
return;
// canvas.drawColor(Color.rgb(241, 241, 241));// 清除背景
canvas.drawARGB(255, 42, 53, 82);
start = (int) ((buf.size())* divider);
float py = baseLine;
float y;
if(sfv.getWidth() - start <= marginRight){//如果超过预留的右边距距离
start = sfv.getWidth() -marginRight;//画的位置x坐标
}
//TODO
canvas.drawLine(marginRight, 0, marginRight, sfv.getHeight(), circlePaint);//垂直的线
int height = sfv.getHeight()-line_off;
canvas.drawLine(0, line_off/2, sfv.getWidth(), line_off/2, paintLine);//最上面的那根线
mCurrentPosInterface.onCurrentPosChanged(start);
canvas.drawLine(0, height*0.5f+line_off/2, sfv.getWidth() ,height*0.5f+line_off/2, center);//中心线
canvas.drawLine(0, sfv.getHeight()-line_off/2-1, sfv.getWidth(), sfv.getHeight()-line_off/2-1, paintLine);//最下面的那根线
Map<Integer,Integer> newMarkMap=new HashMap<Integer,Integer>();
Iterator<Integer> iterator = markMap.keySet().iterator();
while(iterator.hasNext()){
int key=iterator.next();
int pos = markMap.get(key);
destRect=new Rect((int)(pos-bitWidth/4), 0, (int)(pos-bitWidth/4)+bitWidth/2, bitHeight/2);
canvas.drawBitmap(markIcon, null, destRect, null);
String text=(key+1)+"";
float textWidth = markTextPaint.measureText(text);
FontMetricsInt fontMetricsInt = markTextPaint.getFontMetricsInt();
int fontHeight=fontMetricsInt.bottom-fontMetricsInt.top;
canvas.drawText(text, (pos-textWidth/2), fontHeight-8, markTextPaint);
canvas.drawLine(pos-bitWidth/16+2, bitHeight/2-2,pos-bitWidth/16+2, sfv.getWidth()-bitHeight/2,bottomHalfPaint );
newMarkMap.put(key, pos-3);
}
markMap=newMarkMap;
for (int i = 0; i < buf.size(); i++) {
y =buf.get(i)/rateY + baseLine;// 调节缩小比例,调节基准线
float x=(i) * divider;
if(sfv.getWidth() - (i-1) * divider <=marginRight){
x = sfv.getWidth()-marginRight;
}
canvas.drawLine(x, y, x,sfv.getHeight()-y, mPaint);//中间出波形
}
sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
}
}
/**
* 添加音频的标记点
*/
public void addCurrentPostion(){
markMap.put(markMap.size(), start);
}
/**
* 清除标记位置点
*/
public void clearMarkPosition(){
markMap.clear();
}
/**
* 当前位置变化监听
*/
public void setCurrentPostChangerLisener(CurrentPosInterface currentPosInterface){
mCurrentPosInterface = currentPosInterface;
}
/**
* 异步写文件
* @author cokus
*
*/
class WriteRunnable implements Runnable {
@Override
public void run() {
try {
FileOutputStream fos2wav = null;
File file2wav = null;
try {
file2wav = new File(savePcmPath);
if (file2wav.exists()) {
file2wav.delete();
}
fos2wav = new FileOutputStream(file2wav);// 建立一个可存取字节的文件
} catch (Exception e) {
e.printStackTrace();
}
while (isWriting || write_data.size() > 0) {
byte[] buffer = null;
synchronized (write_data) {
if(write_data.size() > 0){
buffer = write_data.get(0);
write_data.remove(0);
}
}
try {
if(buffer != null){
fos2wav.write(buffer);
fos2wav.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
fos2wav.close();
Pcm2Wav p2w = new Pcm2Wav();//将pcm格式转换成wav 其实就尼玛加了一个44字节的头信息
p2w.convertAudioFiles(savePcmPath, saveWavPath);
} catch (Throwable t) {
}
}
- 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
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
}
}
“`
ok! 至此音频的录制功能到此结束!
额,忘了,还有一个pcm转wav的类。如下:
public void convertAudioFiles(String src, String target) throws Exception
{
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(target);
byte[] buf = new byte[1024 * 1000];
int size = fis.read(buf);
int PCMSize = 0;
while (size != -1)
{
PCMSize += size;
size = fis.read(buf);
}
fis.close();
WaveHeader header = new WaveHeader();
header.fileLength = PCMSize + (44 - 8);
header.FmtHdrLeth = 16;
header.BitsPerSample = 16;
header.Channels = 1;
header.FormatTag = 0x0001;
header.SamplesPerSec = 16000;
header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
header.DataHdrLeth = PCMSize;
byte[] h = header.getHeader();
assert h.length == 44;
//write header
fos.write(h, 0, h.length);
//write data stream
fis = new FileInputStream(src);
size = fis.read(buf);
while (size != -1)
{
fos.write(buf, 0, size);
size = fis.read(buf);
}
fis.close();
fos.close();
}
- 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
对应的44字节头部信息:
package com.jwzt.jwzt_procaibian.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class WaveHeader
{
public final char fileID[] = { ‘R’, ‘I’, ‘F’, ‘F’ };
public int fileLength;
public char wavTag[] = { 'W', 'A', 'V', 'E' };;
public char FmtHdrID[] = { 'f', 'm', 't', ' ' };
public int FmtHdrLeth;
public short FormatTag;
public short Channels;
public int SamplesPerSec;
public int AvgBytesPerSec;
public short BlockAlign;
public short BitsPerSample;
public char DataHdrID[] = { 'd', 'a', 't', 'a' };
public int DataHdrLeth;
public byte[] getHeader() throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
WriteChar(bos, fileID);
WriteInt(bos, fileLength);
WriteChar(bos, wavTag);
WriteChar(bos, FmtHdrID);
WriteInt(bos, FmtHdrLeth);
WriteShort(bos, FormatTag);
WriteShort(bos, Channels);
WriteInt(bos, SamplesPerSec);
WriteInt(bos, AvgBytesPerSec);
WriteShort(bos, BlockAlign);
WriteShort(bos, BitsPerSample);
WriteChar(bos, DataHdrID);
WriteInt(bos, DataHdrLeth);
bos.flush();
byte[] r = bos.toByteArray();
bos.close();
return r;
}
private void WriteShort(ByteArrayOutputStream bos, int s)
throws IOException
{
byte[] mybyte = new byte[2];
mybyte[1] = (byte) ((s << 16) >> 24);
mybyte[0] = (byte) ((s << 24) >> 24);
bos.write(mybyte);
}
private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException
{
byte[] buf = new byte[4];
buf[3] = (byte) (n >> 24);
buf[2] = (byte) ((n << 8) >> 24);
buf[1] = (byte) ((n << 16) >> 24);
buf[0] = (byte) ((n << 24) >> 24);
bos.write(buf);
}
private void WriteChar(ByteArrayOutputStream bos, char[] id)
{
for (int i = 0; i < id.length; i++)
{
char c = id[i];
bos.write(c);
}
}
- 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
}
好了,录制方面的东西就差不多了!
pcm转为wav格式其实就是多了44个头字节,合并的时候,我们需要更改头部标记文件长度的那个字节进行修改!然后再利用FFmpeg进行格式转化!当然,裁剪的话稍微有些麻烦!我们需要进行帧的计算,注释中也提到了,20ms一帧,采用的单声道,16000的采集参数,那每帧大概就是640!然后在裁剪的时候计算出音频总的帧数,进行计算裁剪!误差应该不超100ms!
这里就不给源码了,这个项目还在开发测试阶段,机型,屏幕什么的还没做适配,坑定还有很多bug需要修改!也希望,给在这方面开发的同学们,提供一点思路!很多东西也需要大家进行思考和学习!
有不对的地方也希望大家指正,共同进步!
下一篇再介绍裁剪部分的界面制作和功能实现!
Github地址(大家下载的时候顺便给个star也是对作者劳动成果的肯定,谢谢):
https://github.com/T-chuangxin/VideoMergeDemo