() {
81 | public VideoInfo createFromParcel(Parcel source) {
82 | return new VideoInfo(source);
83 | }
84 |
85 | public VideoInfo[] newArray(int size) {
86 | return new VideoInfo[size];
87 | }
88 | };
89 |
90 |
91 | public long getDuration() {
92 | return duration;
93 | }
94 |
95 | public void setDuration(long duration) {
96 | this.duration = duration;
97 | }
98 |
99 | public int getFrameRate() {
100 | return frameRate;
101 | }
102 |
103 | public void setFrameRate(int frameRate) {
104 | this.frameRate = frameRate;
105 | }
106 |
107 | public int getVideoRotation() {
108 | return videoRotation;
109 | }
110 |
111 | public void setVideoRotation(int videoRotation) {
112 | this.videoRotation = videoRotation;
113 | }
114 |
115 | @Override
116 | public VideoInfo clone() {
117 | VideoInfo videoInfo = new VideoInfo();
118 | videoInfo.videoRotation = videoRotation;
119 | videoInfo.frameRate = frameRate;
120 | videoInfo.duration = duration;
121 | videoInfo.videoHeight = videoHeight;
122 | videoInfo.videoWidth = videoWidth;
123 | videoInfo.videoPath = videoPath;
124 | return videoInfo;
125 | }
126 |
127 | public static void fillVideoInfo(String videoPath, VideoInfo videoInfo) {
128 | if (TextUtils.isEmpty(videoPath) || videoInfo == null) {
129 | return;
130 | }
131 | videoInfo.setVideoPath(videoPath);
132 | MediaExtractor extractor = new MediaExtractor();
133 | try {
134 | extractor.setDataSource(videoPath);
135 | int selectTrack = SVideoUtil.selectTrack(extractor, false);
136 | MediaFormat trackFormat = extractor.getTrackFormat(selectTrack);
137 | videoInfo.setFrameRate(trackFormat.containsKey(MediaFormat.KEY_FRAME_RATE) ?
138 | trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE) : 0);
139 | videoInfo.setDuration(trackFormat.containsKey(MediaFormat.KEY_DURATION) ?
140 | trackFormat.getLong(MediaFormat.KEY_DURATION) / 1000 : 0);
141 | videoInfo.setVideoWidth(trackFormat.getInteger(MediaFormat.KEY_WIDTH));
142 | videoInfo.setVideoHeight(trackFormat.getInteger(MediaFormat.KEY_HEIGHT));
143 | videoInfo.setVideoRotation(trackFormat.containsKey(MediaFormat.KEY_ROTATION) ?
144 | trackFormat.getInteger(MediaFormat.KEY_ROTATION) : 0);
145 | } catch (Exception e) {
146 | RL.e(e);
147 | } finally {
148 | extractor.release();
149 | }
150 | }
151 |
152 | @Override
153 | public String toString() {
154 | return "VideoInfo{" +
155 | "videoPath='" + videoPath + '\'' +
156 | ", videoWidth=" + videoWidth +
157 | ", videoHeight=" + videoHeight +
158 | ", frameRate=" + frameRate +
159 | ", duration=" + duration +
160 | ", videoRotation=" + videoRotation +
161 | '}';
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/OnRecordProgressListener.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | /**
4 | * Created by huangwei on 2016/1/8.
5 | */
6 | public interface OnRecordProgressListener {
7 | void onRecording(long dutationMs);
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoEncoderApi16.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.annotation.TargetApi;
5 | import android.media.MediaCodec;
6 | import android.media.MediaCodecInfo;
7 | import android.os.Build;
8 | import us.pinguo.svideo.recorder.RecordFailException;
9 | import us.pinguo.svideo.utils.RL;
10 |
11 | import java.io.File;
12 | import java.io.FileOutputStream;
13 | import java.io.IOException;
14 | import java.nio.ByteBuffer;
15 | import java.nio.channels.FileChannel;
16 |
17 | /**
18 | * 使用MediaCodec录制YUV数据,每次使用都要重新new一个
19 | */
20 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
21 | public class VideoEncoderApi16 extends VideoEncoderFromBuffer {
22 |
23 | private FileChannel mFileChannel;
24 | @SuppressLint("NewApi")
25 | public VideoEncoderApi16(int width, int height, int bitRate, int frameRate, int iFrameInterval, String path) {
26 | super(width, height, bitRate, frameRate, iFrameInterval, null);
27 |
28 | File file = new File(path);
29 | try {
30 | file.createNewFile();
31 | mFileChannel = new FileOutputStream(file).getChannel();
32 | } catch (IOException e) {
33 | throw new RecordFailException(e);
34 | }
35 | }
36 |
37 | public void encodeFrame(byte[] input, long fpsTimeUs) {
38 | RL.i("encodeFrame()");
39 | convertColorFormat(input);
40 |
41 | ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
42 | ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
43 | int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);
44 | if (VERBOSE) {
45 | RL.i("inputBufferIndex-->" + inputBufferIndex);
46 | }
47 | if (inputBufferIndex >= 0) {
48 | ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
49 | inputBuffer.clear();
50 | inputBuffer.put(mFrameData);
51 | mMediaCodec.queueInputBuffer(inputBufferIndex, 0,
52 | mFrameData.length, fpsTimeUs, 0);
53 | } else {
54 | // either all in use, or we timed out during initial setup
55 | if (VERBOSE) {
56 | RL.d("input buffer not available");
57 | }
58 | }
59 |
60 | int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
61 | RL.i("outputBufferIndex-->" + outputBufferIndex);
62 | do {
63 | if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
64 | // no output available yet
65 | if (VERBOSE) {
66 | RL.d("no output from encoder available");
67 | }
68 | } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
69 | // not expected for an encoder
70 | outputBuffers = mMediaCodec.getOutputBuffers();
71 | if (VERBOSE) {
72 | RL.d("encoder output buffers changed");
73 | }
74 | } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
75 | // not expected for an encoder
76 | } else if (outputBufferIndex < 0) {
77 | RL.w("unexpected result from encoder.dequeueOutputBuffer: " +
78 | outputBufferIndex);
79 | // let's ignore it
80 | } else {
81 | if (VERBOSE) {
82 | RL.d("perform encoding");
83 | }
84 | ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
85 | if (outputBuffer == null) {
86 | throw new RuntimeException("encoderOutputBuffer " + outputBufferIndex +
87 | " was null");
88 | }
89 |
90 | // if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
91 | // // The codec config data was pulled out and fed to the muxer when we got
92 | // // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
93 | // if (VERBOSE) {
94 | // L.d("ignoring BUFFER_FLAG_CODEC_CONFIG");
95 | // }
96 | // mBufferInfo.size = 0;
97 | // }
98 |
99 | if (mBufferInfo.size != 0) {
100 |
101 | // adjust the ByteBuffer values to match BufferInfo (not needed?)
102 | outputBuffer.position(mBufferInfo.offset);
103 | outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);
104 |
105 | if (mFileChannel != null) {
106 | try {
107 | mFileChannel.write(outputBuffer);
108 | } catch (IOException e1) {
109 | RL.e(e1);
110 | }
111 | }
112 | if (VERBOSE) {
113 | RL.d("sent " + mBufferInfo.size + " bytes to muxer");
114 | }
115 | }
116 |
117 | mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
118 | }
119 | outputBufferIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
120 | }
121 |
122 | while (outputBufferIndex >= 0);
123 |
124 | mRecordedFrames++;
125 | //计算录制时间
126 | if (mOnRecordProgressListener != null)
127 |
128 | {
129 | int recordedDuration = (int) ((1000f / mFrameRate) * mRecordedFrames);
130 | mOnRecordProgressListener.onRecording(recordedDuration);
131 | }
132 | }
133 |
134 | @SuppressLint("NewApi")
135 | public void close() {
136 | try {
137 | if (mMediaCodec != null && mCodecStarted) {
138 | mCodecStarted = false;
139 | mMediaCodec.stop();
140 | mMediaCodec.release();
141 | }
142 | } catch (Exception e) {
143 | RL.e(e);
144 | }
145 | if (mFileChannel != null) {
146 | try {
147 | mFileChannel.close();
148 | } catch (IOException e) {
149 | RL.e(e);
150 | }
151 | }
152 | }
153 |
154 | /**
155 | * Returns true if this is a color format that this test code understands
156 | * (i.e. we know how to read and generate frames in this format).
157 | */
158 | private static boolean isRecognizedFormat(int colorFormat) {
159 | switch (colorFormat) {
160 | // these are the formats we know how to handle for this test
161 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
162 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
163 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
164 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
165 | case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
166 | return true;
167 | default:
168 | return false;
169 | }
170 | }
171 |
172 | public void setOnRecordProgressListener(OnRecordProgressListener onRecordProgressListener) {
173 | this.mOnRecordProgressListener = onRecordProgressListener;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoEncoderApi21.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.annotation.TargetApi;
4 | import android.media.MediaCodec;
5 | import android.media.MediaFormat;
6 | import android.media.MediaMuxer;
7 | import android.os.Build;
8 | import us.pinguo.svideo.recorder.SMediaCodecRecorder;
9 | import us.pinguo.svideo.utils.RL;
10 |
11 | import java.nio.ByteBuffer;
12 |
13 | /**
14 | * 录的时候有问题,MP4文件录出来时长单位是微秒。。。
15 | */
16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
17 | public class VideoEncoderApi21 extends VideoEncoderFromBuffer {
18 |
19 | public VideoEncoderApi21(int width, int height, int bitRate, int frameRate, int iFrameInterval, String path, MediaMuxer mediaMuxer) {
20 | super(width, height, bitRate, frameRate, iFrameInterval, mediaMuxer);
21 | }
22 |
23 | public void encodeFrame(byte[] input, long fpsTimeUs) {
24 | RL.i("encodeFrame,fpsTimeUs:"+fpsTimeUs);
25 | //YUV数据格式转换
26 | convertColorFormat(input);
27 |
28 | //输入原始数据
29 | int inputBufferId = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);
30 | if (inputBufferId >= 0) {
31 | ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferId);
32 | inputBuffer.put(mFrameData);
33 | mMediaCodec.queueInputBuffer(inputBufferId, 0,
34 | mFrameData.length, fpsTimeUs, 0);
35 | } else {
36 | // either all in use, or we timed out during initial setup
37 | if (VERBOSE) {
38 | RL.d("input buffer not available");
39 | }
40 | }
41 | //拿取编码好的数据
42 | int outputBufferId = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
43 | do {
44 | if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
45 | // no output available yet
46 | if (VERBOSE) {
47 | RL.d("no output from encoder available");
48 | }
49 | } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
50 | // not expected for an encoder
51 |
52 | mNewFormat = mMediaCodec.getOutputFormat();
53 | RL.d("encoder output format changed: " + mNewFormat);
54 | /**放到下面start,方便同步*/
55 | // // now that we have the Magic Goodies, start the muxer
56 | // mTrackIndex = mMuxer.addTrack(newFormat);
57 | // mMuxer.start();
58 | // mMuxerStarted = true;
59 | } else if (outputBufferId < 0) {
60 | RL.w("unexpected result from encoder.dequeueOutputBuffer: " +
61 | outputBufferId);
62 | // let's ignore it
63 | } else {
64 | if (VERBOSE) {
65 | RL.d("perform encoding");
66 | }
67 | ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferId);
68 | if (outputBuffer == null) {
69 | throw new RuntimeException("encoderOutputBuffer " + outputBufferId +
70 | " was null");
71 | }
72 |
73 | if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
74 | // The codec config data was pulled out and fed to the muxer when we got
75 | // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
76 | if (VERBOSE) {
77 | RL.d("ignoring BUFFER_FLAG_CODEC_CONFIG");
78 | }
79 | mBufferInfo.size = 0;
80 | }
81 |
82 | if (mBufferInfo.size != 0) {
83 |
84 | if (!mMuxerStarted) {
85 | // throw new RuntimeException("muxer hasn't started");
86 | MediaFormat newFormat = mMediaCodec.getOutputFormat();
87 | mTrackIndex = mMuxer.addTrack(newFormat);
88 | SMediaCodecRecorder.sStartSemaphore.release(1);
89 | try {
90 | SMediaCodecRecorder.sStartSemaphore.acquire(2);
91 | if (!SMediaCodecRecorder.sMuxerStarted) {
92 | if (!SMediaCodecRecorder.sMuxerStarted) {
93 | SMediaCodecRecorder.sMuxerStarted = true;
94 | mMuxer.start();
95 | }
96 | }
97 | } catch (InterruptedException e1) {
98 | RL.e(e1);
99 | } finally {
100 | SMediaCodecRecorder.sStartSemaphore.release(2);
101 | }
102 | mMuxerStarted = true;
103 | }
104 |
105 | // adjust the ByteBuffer values to match BufferInfo (not needed?)
106 | outputBuffer.position(mBufferInfo.offset);
107 | outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);
108 |
109 | // synchronized (mMuxer) {
110 | mMuxer.writeSampleData(mTrackIndex, outputBuffer, mBufferInfo);
111 | // }
112 | if (VERBOSE) {
113 | RL.d("sent " + mBufferInfo.size + " bytes to muxer,timsUs:"+mBufferInfo.presentationTimeUs);
114 | }
115 | }
116 |
117 | mMediaCodec.releaseOutputBuffer(outputBufferId, false);
118 | }
119 | outputBufferId = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
120 | }
121 | while (outputBufferId >= 0);
122 | // RL.i("api21 平均耗时:" + totalTime / index + "ms");
123 | mRecordedFrames++;
124 | //计算录制时间
125 | if (mOnRecordProgressListener != null) {
126 | int recordedDuration = (int) ((1000f / mFrameRate) * mRecordedFrames);
127 | mOnRecordProgressListener.onRecording(recordedDuration);
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoMediaEncoderApi21Thread.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.media.MediaMuxer;
4 | import us.pinguo.svideo.utils.RL;
5 |
6 | import java.util.concurrent.CountDownLatch;
7 |
8 | /**
9 | * Created by huangwei on 2015/12/15.
10 | * 用YUV数据录制原始视频的类
11 | */
12 | public class VideoMediaEncoderApi21Thread extends VideoMediaEncoderThread implements Thread.UncaughtExceptionHandler {
13 | private VideoEncoderApi21Async mRecorder;
14 |
15 | private boolean mForceStop = false;
16 |
17 | // Runs in main thread
18 | public VideoMediaEncoderApi21Thread(int width, int height, int bitRate, int frameRate, int iFrameInterval,String path, MediaMuxer mediaMuxer, CountDownLatch countDownLatch) {
19 | super(width, height, bitRate, frameRate, iFrameInterval,path, mediaMuxer, countDownLatch);
20 | mRecorder.setVideoMediaEncoderThread(this);
21 | }
22 |
23 | @Override
24 | protected void initRecorder(int width, int height, int bitRate, int frameRate, int iFrameInterval,String path, MediaMuxer mediaMuxer) {
25 | mRecorder = new VideoEncoderApi21Async(width, height, bitRate, frameRate, iFrameInterval, mediaMuxer);
26 | }
27 |
28 | @Override
29 | public void run() {
30 | if (mRecorder == null) {
31 | mCountDonwLatch.countDown();
32 | return;
33 | }
34 | while (true) {
35 | SaveRequest r = null;
36 | try {
37 | r = mQueue.take();
38 | if (r.data == null) {
39 | break;
40 | }
41 | } catch (InterruptedException e) {
42 | RL.e(e);
43 | continue;
44 | }
45 | mRecorder.addImageData(r);
46 | synchronized (mDataObjectPool) {
47 | mDataObjectPool.add(r.data);
48 | }
49 | }
50 | if (!mForceStop) {
51 | mRecorder.waitFinish();
52 | }
53 | mDataObjectPool.clear();
54 | mQueue.clear();
55 | mRecorder.close();
56 | mIsSuccess = true;
57 | mCountDonwLatch.countDown();
58 | }
59 |
60 | @Deprecated
61 | @Override
62 | public void forceFinish() {
63 | super.forceFinish();
64 | mForceStop = true;
65 | }
66 |
67 | @Override
68 | public int getRecordedFrames() {
69 | return mRecorder.getRecordedFrames();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoMediaEncoderThreadApi16.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.media.MediaMuxer;
4 |
5 | import java.util.concurrent.CountDownLatch;
6 |
7 | /**
8 | * Created by huangwei on 2016/1/18.
9 | */
10 | public class VideoMediaEncoderThreadApi16 extends VideoMediaEncoderThread {
11 | public VideoMediaEncoderThreadApi16(int width, int height, int bitRate, int frameRate, int iFrameInterval, String path, MediaMuxer mediaMuxer, CountDownLatch countDownLatch) {
12 | super(width, height, bitRate, frameRate, iFrameInterval, path,mediaMuxer, countDownLatch);
13 | }
14 |
15 | @Override
16 | protected void initRecorder(int width, int height, int bitRate, int frameRate, int iFrameInterval,String path,MediaMuxer mediaMuxer) {
17 | mRecorder = new VideoEncoderApi16(
18 | width,
19 | height,
20 | bitRate,
21 | frameRate,
22 | iFrameInterval,
23 | path
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoSurfaceEncoderApi21.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.annotation.TargetApi;
4 | import android.media.MediaCodec;
5 | import android.media.MediaFormat;
6 | import android.media.MediaMuxer;
7 | import android.os.Build;
8 | import us.pinguo.svideo.recorder.SMediaCodecRecorder;
9 | import us.pinguo.svideo.utils.RL;
10 |
11 | import java.nio.ByteBuffer;
12 |
13 | /**
14 | * Created by huangwei on 2016/4/13.
15 | */
16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
17 | public class VideoSurfaceEncoderApi21 extends VideoSurfaceEncoder {
18 |
19 | private static final int MAX_LOOP_COUNT = 10;
20 |
21 | public VideoSurfaceEncoderApi21(int width, int height, int bitRate, int frameRate, int iFrameInterval, MediaMuxer mediaMuxer) {
22 | super(width, height, bitRate, frameRate, iFrameInterval, mediaMuxer);
23 | }
24 |
25 | @Override
26 | public void drainEncoder(boolean endOfStream, long timeUs) {
27 | if (VERBOSE) {
28 | RL.d("drainEncoder(" + endOfStream + ")");
29 | }
30 |
31 | if (endOfStream) {
32 | if (VERBOSE) {
33 | RL.d("sending EOS to encoder");
34 | }
35 | mMediaCodec.signalEndOfInputStream();
36 | }
37 | //拿取编码好的数据
38 | int loopCount = 0;
39 | while (true) {
40 | loopCount++;
41 | int outputBufferId = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
42 | if (outputBufferId >= 0) {
43 | ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferId);
44 | if (!mMuxerStarted) {
45 | // throw new RuntimeException("muxer hasn't started");
46 | MediaFormat bufferFormat = mMediaCodec.getOutputFormat();
47 | mTrackIndex = mMuxer.addTrack(bufferFormat);
48 | SMediaCodecRecorder.sStartSemaphore.release(1);
49 | try {
50 | SMediaCodecRecorder.sStartSemaphore.acquire(2);
51 | if (!SMediaCodecRecorder.sMuxerStarted) {
52 | if (!SMediaCodecRecorder.sMuxerStarted) {
53 | SMediaCodecRecorder.sMuxerStarted = true;
54 | mMuxer.start();
55 | }
56 | }
57 | } catch (InterruptedException e1) {
58 | RL.e(e1);
59 | } finally {
60 | SMediaCodecRecorder.sStartSemaphore.release(2);
61 | mMuxerStarted = true;
62 | }
63 | }
64 | if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
65 | return;
66 | }
67 | RL.i("mBufferInfo,flags:" + mBufferInfo.flags + " offset" + mBufferInfo.offset + " presentationTimeUs:" + mBufferInfo.presentationTimeUs
68 | + " size:" + mBufferInfo.size);
69 | if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM && mBufferInfo.presentationTimeUs < 0) {
70 | mBufferInfo.presentationTimeUs = 0;
71 | }
72 | mMuxer.writeSampleData(mTrackIndex, outputBuffer, mBufferInfo);
73 | mRecordedFrames++;
74 | if (VERBOSE) {
75 | RL.d("sent " + mBufferInfo.size + " bytes to muxer");
76 | }
77 | mMediaCodec.releaseOutputBuffer(outputBufferId, false);
78 | if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
79 | if (!endOfStream) {
80 | RL.w("reached end of stream unexpectedly");
81 | } else {
82 | if (VERBOSE) {
83 | RL.d("end of stream reached");
84 | }
85 | }
86 | break;
87 | }
88 | } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
89 | // Subsequent data will conform to new format.
90 | // Can ignore if using getOutputFormat(outputBufferId)
91 | MediaFormat outputFormat = mMediaCodec.getOutputFormat(); // option B
92 | RL.i("INFO_OUTPUT_FORMAT_CHANGED:" + outputFormat);
93 | } else if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
94 | // no output available yet
95 | if (!endOfStream) {
96 | break; // out of while
97 | } else {
98 | if (VERBOSE) {
99 | RL.d("no output available, spinning to await EOS");
100 | }
101 | }
102 | } else {
103 | RL.e("outputBufferId:" + outputBufferId + " break;");
104 | }
105 |
106 | if (loopCount > MAX_LOOP_COUNT) {
107 | RL.et("hwLog","loopCount:" + loopCount + ",outputBufferId:" + outputBufferId);
108 | }
109 | }
110 |
111 | //计算录制时间
112 | if (mOnRecordProgressListener != null) {
113 | int recordedDuration = (int) ((1000f / mFrameRate) * mRecordedFrames);
114 | mOnRecordProgressListener.onRecording(recordedDuration);
115 | }
116 |
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/encoder/VideoSurfaceEncoderAsyncApi21.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.encoder;
2 |
3 | import android.annotation.TargetApi;
4 | import android.media.MediaCodec;
5 | import android.media.MediaFormat;
6 | import android.media.MediaMuxer;
7 | import android.os.Build;
8 | import us.pinguo.svideo.recorder.SMediaCodecRecorder;
9 | import us.pinguo.svideo.utils.RL;
10 |
11 | import java.nio.ByteBuffer;
12 |
13 | /**
14 | * Created by huangwei on 2016/4/13.
15 | */
16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
17 | public class VideoSurfaceEncoderAsyncApi21 extends VideoSurfaceEncoder {
18 | private MediaCodec.Callback mCallback;
19 |
20 | public VideoSurfaceEncoderAsyncApi21(int width, int height, int bitRate, int frameRate, int iFrameInterval, MediaMuxer mediaMuxer) {
21 | super(width, height, bitRate, frameRate, iFrameInterval, mediaMuxer);
22 | mCallback = new MediaCodec.Callback() {
23 | @Override
24 | public void onInputBufferAvailable(MediaCodec codec, int index) {
25 | RL.i("onInputBufferAvailable");
26 | }
27 |
28 | @Override
29 | public void onOutputBufferAvailable(MediaCodec codec, int outputBufferId, MediaCodec.BufferInfo info) {
30 | if (info.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
31 | RL.i("BUFFER_FLAG_CODEC_CONFIG.return.");
32 | return;
33 | }
34 | long s = System.currentTimeMillis();
35 | ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferId);
36 | if (!mMuxerStarted) {
37 | // throw new RuntimeException("muxer hasn't started");
38 | MediaFormat bufferFormat = mMediaCodec.getOutputFormat();
39 | mTrackIndex = mMuxer.addTrack(bufferFormat);
40 | SMediaCodecRecorder.sStartSemaphore.release(1);
41 | try {
42 | SMediaCodecRecorder.sStartSemaphore.acquire(2);
43 | if (!SMediaCodecRecorder.sMuxerStarted) {
44 | if (!SMediaCodecRecorder.sMuxerStarted) {
45 | SMediaCodecRecorder.sMuxerStarted = true;
46 | mMuxer.start();
47 | }
48 | }
49 | } catch (InterruptedException e1) {
50 | RL.e(e1);
51 | } finally {
52 | SMediaCodecRecorder.sStartSemaphore.release(2);
53 | mMuxerStarted = true;
54 | }
55 | }
56 | RL.i("info,flags:" + info.flags + " offset" + info.offset + " presentationTimeUs:" + info.presentationTimeUs
57 | + " size:" + info.size);
58 | if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM && mBufferInfo.presentationTimeUs < 0) {
59 | mBufferInfo.presentationTimeUs = 0;
60 | }
61 | mMuxer.writeSampleData(mTrackIndex, outputBuffer, info);
62 | mRecordedFrames++;
63 | if (VERBOSE) {
64 | RL.d("sent " + info.size + " bytes to muxer");
65 | }
66 | mMediaCodec.releaseOutputBuffer(outputBufferId, false);
67 | //计算录制时间
68 | if (mOnRecordProgressListener != null) {
69 | int recordedDuration = (int) ((1000f / mFrameRate) * mRecordedFrames);
70 | mOnRecordProgressListener.onRecording(recordedDuration);
71 | }
72 | long e = System.currentTimeMillis();
73 | RL.i("耗时:" + (e - s) + "ms");
74 | }
75 |
76 | @Override
77 | public void onError(MediaCodec codec, MediaCodec.CodecException e) {
78 | RL.e(e);
79 | }
80 |
81 | @Override
82 | public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
83 | MediaFormat outputFormat = mMediaCodec.getOutputFormat();
84 | RL.i("INFO_OUTPUT_FORMAT_CHANGED:" + outputFormat);
85 | }
86 | };
87 | }
88 |
89 | @Override
90 | protected void afterMediaCodecCreated() {
91 | mMediaCodec.setCallback(mCallback);
92 | }
93 |
94 | @Override
95 | public void drainEncoder(boolean endOfStream,long timeUs) {
96 | if (endOfStream) {
97 | if (VERBOSE) {
98 | RL.d("sending EOS to encoder");
99 | }
100 | mMediaCodec.signalEndOfInputStream();
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/ICameraProxyForRecord.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | /**
4 | * Created by huangwei on 2016/1/22.
5 | */
6 | public interface ICameraProxyForRecord {
7 | void addSurfaceDataListener(PreviewSurfaceListener listener,SurfaceCreatedCallback callback);
8 |
9 | void removeSurfaceDataListener(PreviewSurfaceListener listener);
10 |
11 | void addPreviewDataCallback(PreviewDataCallback callback);
12 |
13 | void removePreviewDataCallback(PreviewDataCallback callback);
14 |
15 | /**
16 | * @return 视频需要旋转的角度
17 | */
18 | int getVideoRotation();
19 |
20 | int getPreviewWidth();
21 |
22 | int getPreviewHeight();
23 | }
24 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/IReporter.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Created by huangwei on 2016/1/22.
7 | */
8 | public interface IReporter {
9 | void reportError(Throwable t);
10 |
11 | void reportEvent_SOURCE_VIDEO_ENCODE_FPS(Context context, int duration);
12 |
13 | void reportEvent_SOURCE_VIDEO_ENCODE_FPS_API18(Context context, int duration);
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/ISVideoRecorder.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import us.pinguo.svideo.recorder.OnRecordFailListener;
4 |
5 | /**
6 | * Created by huangwei on 2016/1/21
7 | */
8 | public interface ISVideoRecorder extends PreviewDataCallback, OnRecordFailListener {
9 | void startRecord();
10 |
11 | void stopRecord();
12 |
13 | void pauseRecord();
14 |
15 | void resumeRecord();
16 |
17 | void cancelRecord();
18 |
19 | void addRecordListener(OnRecordListener onRecordListener);
20 |
21 | void removeRecordListener(OnRecordListener onRecordListener);
22 |
23 | boolean isRecordFailed();
24 |
25 | void setVideoEncodingBitRate(int bitRate);
26 |
27 | /**
28 | * @param frameRate {@link android.media.MediaFormat#KEY_FRAME_RATE}
29 | * @param iFrameInterval {@link android.media.MediaFormat#KEY_I_FRAME_INTERVAL}
30 | */
31 | void setVideoFrameRateAndInterval(int frameRate, int iFrameInterval);
32 |
33 | /**
34 | * 开始录新视频时会调用generator生成视频路径
35 | */
36 | void setVideoPathGenerator(IVideoPathGenerator generator);
37 | }
38 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/IVideoPathGenerator.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | /**
4 | * Created by huangwei on 2016/4/23.
5 | */
6 | public interface IVideoPathGenerator {
7 | String generate();
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/OnRecordListener.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import us.pinguo.svideo.bean.VideoInfo;
4 |
5 | /**
6 | * Created by huangwei on 2016/1/21.
7 | */
8 | public interface OnRecordListener {
9 | void onRecordSuccess(VideoInfo videoInfo);
10 |
11 | void onRecordStart();
12 |
13 | void onRecordFail(Throwable t);
14 |
15 | void onRecordStop();
16 |
17 | void onRecordPause();
18 |
19 | void onRecordResume();
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/OnSurfaceCreatedCallback.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import android.view.Surface;
4 |
5 | /**
6 | * Created by huangwei on 2016/4/8.
7 | */
8 | public interface OnSurfaceCreatedCallback {
9 | void onSurfaceCreate(Surface surface);
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/PreviewDataCallback.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | /**
4 | * Created by huangwei on 2016/1/21.
5 | */
6 | public interface PreviewDataCallback {
7 | /**
8 | * @param data
9 | * @param timeUs 单位us(例如:System.nanoTime()/1000)
10 | */
11 | void onPreviewData(byte[] data, long timeUs);
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/PreviewSurfaceListener.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | public interface PreviewSurfaceListener {
4 | void onFrameAvaibleSoon();
5 | }
6 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/SimpleRecordListener.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import us.pinguo.svideo.bean.VideoInfo;
4 |
5 | /**
6 | * Created by huangwei on 2018/8/16 0016.
7 | */
8 | public class SimpleRecordListener implements OnRecordListener {
9 | @Override
10 | public void onRecordSuccess(VideoInfo videoInfo) {
11 |
12 | }
13 |
14 | @Override
15 | public void onRecordStart() {
16 |
17 | }
18 |
19 | @Override
20 | public void onRecordFail(Throwable t) {
21 |
22 | }
23 |
24 | @Override
25 | public void onRecordStop() {
26 |
27 | }
28 |
29 | @Override
30 | public void onRecordPause() {
31 |
32 | }
33 |
34 | @Override
35 | public void onRecordResume() {
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/interfaces/SurfaceCreatedCallback.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.interfaces;
2 |
3 | import android.view.Surface;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by huangwei on 2016/4/8.
8 | */
9 | public interface SurfaceCreatedCallback {
10 | void setSurfaceCreateCallback(OnSurfaceCreatedCallback createCallback);
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/recorder/OnRecordFailListener.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.recorder;
2 |
3 | /**
4 | * Created by huangwei on 2015/12/26.
5 | */
6 | public interface OnRecordFailListener {
7 | void onVideoRecordFail(Throwable e, final boolean showToast);
8 |
9 | void onAudioRecordFail(Throwable e);
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/recorder/RecordCancelException.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.recorder;
2 |
3 | /**
4 | * Created by huangwei on 2018/8/22 0022.
5 | */
6 | public class RecordCancelException extends Exception {
7 | public RecordCancelException(String message) {
8 | super(message);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/recorder/RecordFailException.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.recorder;
2 |
3 | /**
4 | * Created by huangwei on 2015/12/26.
5 | */
6 | public class RecordFailException extends RuntimeException {
7 | public RecordFailException(String msg) {
8 | super(msg);
9 | }
10 |
11 | public RecordFailException(Exception e) {
12 | super(e);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/DateVideoNameGenerator.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 | import android.os.Environment;
4 | import us.pinguo.svideo.interfaces.IVideoPathGenerator;
5 |
6 | import java.io.File;
7 | import java.text.SimpleDateFormat;
8 |
9 | /**
10 | * Created by huangwei on 2016/4/23.
11 | */
12 | public class DateVideoNameGenerator implements IVideoPathGenerator {
13 | @Override
14 | public String generate() {
15 | File DCIM = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
16 | File dir = new File(DCIM, "Camera");
17 | if (!dir.exists()) {
18 | dir.mkdirs();
19 | }
20 | String name = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(System.currentTimeMillis())+".mp4";
21 | return new File(dir, name).getAbsolutePath();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/NSVUtil.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 | /**
4 | * Created by huangwei on 2018/8/15 0015.
5 | */
6 | public class NSVUtil {
7 | static {
8 | System.loadLibrary("NSVUtil");
9 | }
10 | public static native void NV12ToNV21(byte[] data,int width,int height,int len);
11 | public static native void NV12To420P(byte[] data,int width,int height,int len);
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/RL.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by huangwei on 2016/1/22.
7 | */
8 | public class RL {
9 | public static boolean sLogEnable = false;
10 | private static int sCount = 0;
11 |
12 | private RL() {
13 | }
14 |
15 | public static boolean isLogEnable() {
16 | return sLogEnable;
17 | }
18 |
19 | public static void setLogEnable(boolean enable) {
20 | if(++sCount > 1) {
21 | Log.e("L", "setLogEnable() could only be called once");
22 | } else {
23 | sLogEnable = enable;
24 | }
25 | }
26 |
27 | private static String createLog(TagInfo tagInfo, String log, Object... args) {
28 | StringBuilder buffer = new StringBuilder();
29 | buffer.append("[");
30 | buffer.append(tagInfo.fileName);
31 | buffer.append(".");
32 | buffer.append(tagInfo.methodName);
33 | buffer.append("():");
34 | buffer.append(tagInfo.lineNumber);
35 | buffer.append("]");
36 | buffer.append(formatString(log, args));
37 | return buffer.toString();
38 | }
39 |
40 | private static String createLogWithoutFileName(TagInfo tagInfo, String log, Object... args) {
41 | StringBuilder buffer = new StringBuilder();
42 | buffer.append("[");
43 | buffer.append(tagInfo.methodName);
44 | buffer.append("():");
45 | buffer.append(tagInfo.lineNumber);
46 | buffer.append("]");
47 | buffer.append(formatString(log, args));
48 | return buffer.toString();
49 | }
50 |
51 | private static TagInfo getMethodNames(StackTraceElement[] sElements) {
52 | TagInfo info = new TagInfo();
53 | if(sElements.length > 1) {
54 | info.fileName = sElements[1].getFileName();
55 | if(info.fileName.endsWith(".java")) {
56 | info.fileName = info.fileName.substring(0, info.fileName.length() - 5);
57 | }
58 |
59 | info.methodName = sElements[1].getMethodName();
60 | info.lineNumber = sElements[1].getLineNumber();
61 | }
62 |
63 | return info;
64 | }
65 |
66 | private static String formatString(String message, Object... args) {
67 | return args.length == 0?message:String.format(message, args);
68 | }
69 |
70 | public static void v(String message, Object... args) {
71 | if(sLogEnable) {
72 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
73 | String msg = createLogWithoutFileName(tagInfo, message, args);
74 | Log.v(tagInfo.fileName, msg);
75 | }
76 | }
77 |
78 | public static void v(Throwable ex) {
79 | if(sLogEnable) {
80 | TagInfo tagInfo = getMethodNames(ex.getStackTrace());
81 | Log.v(tagInfo.fileName, "", ex);
82 | }
83 | }
84 |
85 | public static void vt(String tag, String message, Object... args) {
86 | if(sLogEnable) {
87 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
88 | String msg = createLog(tagInfo, message, args);
89 | Log.v(tag, msg);
90 | }
91 | }
92 |
93 | public static void d(String message, Object... args) {
94 | if(sLogEnable) {
95 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
96 | String msg = createLogWithoutFileName(tagInfo, message, args);
97 | Log.d(tagInfo.fileName, msg);
98 | }
99 | }
100 |
101 | public static void d(Throwable ex) {
102 | if(sLogEnable) {
103 | TagInfo tagInfo = getMethodNames(ex.getStackTrace());
104 | Log.d(tagInfo.fileName, "", ex);
105 | }
106 | }
107 |
108 | public static void dt(String tag, String message, Object... args) {
109 | if(sLogEnable) {
110 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
111 | String msg = createLog(tagInfo, message, args);
112 | Log.d(tag, msg);
113 | }
114 | }
115 |
116 | public static void i(String message, Object... args) {
117 | if(sLogEnable) {
118 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
119 | String msg = createLogWithoutFileName(tagInfo, message, args);
120 | Log.i(tagInfo.fileName, msg);
121 | }
122 | }
123 |
124 | public static void i(Throwable ex) {
125 | if(sLogEnable) {
126 | TagInfo tagInfo = getMethodNames(ex.getStackTrace());
127 | Log.i(tagInfo.fileName, "", ex);
128 | }
129 | }
130 |
131 | public static void it(String tag, String message, Object... args) {
132 | if(sLogEnable) {
133 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
134 | String msg = createLog(tagInfo, message, args);
135 | Log.i(tag, msg);
136 | }
137 | }
138 |
139 | public static void w(String message, Object... args) {
140 | if(sLogEnable) {
141 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
142 | String msg = createLogWithoutFileName(tagInfo, message, args);
143 | Log.w(tagInfo.fileName, msg);
144 | }
145 | }
146 |
147 | public static void w(Throwable ex) {
148 | if(sLogEnable) {
149 | TagInfo tagInfo = getMethodNames(ex.getStackTrace());
150 | Log.v(tagInfo.fileName, "", ex);
151 | }
152 | }
153 |
154 | public static void wt(String tag, String message, Object... args) {
155 | if(sLogEnable) {
156 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
157 | String msg = createLog(tagInfo, message, args);
158 | Log.w(tag, msg);
159 | }
160 | }
161 |
162 | public static void e(String message, Object... args) {
163 | if(sLogEnable) {
164 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
165 | String msg = createLogWithoutFileName(tagInfo, message, args);
166 | Log.e(tagInfo.fileName, msg);
167 | }
168 | }
169 |
170 | public static void e(Throwable ex) {
171 | if(sLogEnable) {
172 | TagInfo tagInfo = getMethodNames(ex.getStackTrace());
173 | Log.e(tagInfo.fileName, "", ex);
174 | }
175 | }
176 |
177 | public static void et(String tag, String message, Object... args) {
178 | if(sLogEnable) {
179 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
180 | String msg = createLog(tagInfo, message, args);
181 | Log.e(tag, msg);
182 | }
183 | }
184 |
185 | public static void wtf(String message, Object... args) {
186 | if(sLogEnable) {
187 | TagInfo tagInfo = getMethodNames((new Throwable()).getStackTrace());
188 | String msg = createLogWithoutFileName(tagInfo, message, args);
189 | Log.wtf(tagInfo.fileName, msg);
190 | }
191 | }
192 |
193 | public static void printStackTrace(String tag) {
194 | if(sLogEnable) {
195 | String stackTrace = Log.getStackTraceString(new Throwable());
196 | Log.d(tag, stackTrace);
197 | }
198 | }
199 |
200 | static class TagInfo {
201 | String fileName;
202 | String methodName;
203 | int lineNumber;
204 |
205 | TagInfo() {
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/RecordSemaphore.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 | import java.util.concurrent.Semaphore;
4 |
5 | /**
6 | * Created by huangwei on 2016/1/26.
7 | * 该类主要是为了打日志
8 | */
9 | public class RecordSemaphore extends Semaphore {
10 |
11 | public RecordSemaphore(int permits) {
12 | super(permits);
13 | }
14 |
15 | @Override
16 | public void release(int permits) {
17 | super.release(permits);
18 | RL.i("release " + permits + " availablePermits:" + availablePermits());
19 | }
20 |
21 | @Override
22 | public void release() {
23 | super.release();
24 | RL.i("release " + 1 + " availablePermits:" + availablePermits());
25 | }
26 |
27 | @Override
28 | public boolean tryAcquire(int permits) {
29 | boolean rtn = super.tryAcquire(permits);
30 | RL.i("tryAcquire " + permits + " availablePermits:" + availablePermits());
31 | return rtn;
32 | }
33 |
34 | @Override
35 | public void acquire(int permits) throws InterruptedException {
36 | super.acquire(permits);
37 | RL.i("acquire " + permits + " availablePermits:" + availablePermits());
38 | }
39 |
40 | @Override
41 | public void acquire() throws InterruptedException {
42 | super.acquire();
43 | RL.i("acquire availablePermits:" + availablePermits());
44 | }
45 | }
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/SegVideoNameGenerator.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 | import android.content.Context;
4 | import us.pinguo.svideo.interfaces.IVideoPathGenerator;
5 |
6 | import java.io.File;
7 | import java.text.SimpleDateFormat;
8 |
9 | /**
10 | * Created by huangwei on 2016/4/23.
11 | */
12 | public class SegVideoNameGenerator implements IVideoPathGenerator {
13 | private Context mContext;
14 | public SegVideoNameGenerator(Context context) {
15 | mContext = context.getApplicationContext();
16 | }
17 |
18 | @Override
19 | public String generate() {
20 | File dir = new File(mContext.getCacheDir(), "SegVideos");
21 | if (!dir.exists()) {
22 | dir.mkdirs();
23 | }
24 | String name = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(System.currentTimeMillis()) + ".mp4";
25 | return new File(dir, name).getAbsolutePath();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/TimeOutThread.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils;
2 |
3 |
4 | import java.util.concurrent.CountDownLatch;
5 |
6 | /**
7 | * Created by huangwei on 2015/12/26.
8 | */
9 | public class TimeOutThread extends Thread {
10 |
11 | protected CountDownLatch mCountDonwLatch;
12 |
13 | public TimeOutThread(CountDownLatch countDownLatch) {
14 | mCountDonwLatch = countDownLatch;
15 | }
16 |
17 | public TimeOutThread(String threadName, CountDownLatch countDownLatch) {
18 | super(threadName);
19 | mCountDonwLatch = countDownLatch;
20 | }
21 |
22 | public TimeOutThread(Runnable runnable, String threadName, CountDownLatch countDownLatch) {
23 | super(runnable, threadName);
24 | mCountDonwLatch = countDownLatch;
25 | }
26 |
27 | @Override
28 | public void run() {
29 | super.run();
30 | mCountDonwLatch.countDown();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/Drawable2d.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | import java.nio.FloatBuffer;
20 |
21 | /**
22 | * Base class for stuff we like to draw.
23 | */
24 | public class Drawable2d {
25 | private static final int SIZEOF_FLOAT = 4;
26 |
27 | /**
28 | * Simple equilateral triangle (1.0 per side). Centered on (0,0).
29 | */
30 | private static final float TRIANGLE_COORDS[] = {
31 | 0.0f, 0.577350269f, // 0 top
32 | -0.5f, -0.288675135f, // 1 bottom left
33 | 0.5f, -0.288675135f // 2 bottom right
34 | };
35 | private static final float TRIANGLE_TEX_COORDS[] = {
36 | 0.5f, 0.0f, // 0 top center
37 | 0.0f, 1.0f, // 1 bottom left
38 | 1.0f, 1.0f, // 2 bottom right
39 | };
40 | private static final FloatBuffer TRIANGLE_BUF =
41 | GlUtil.createFloatBuffer(TRIANGLE_COORDS);
42 | private static final FloatBuffer TRIANGLE_TEX_BUF =
43 | GlUtil.createFloatBuffer(TRIANGLE_TEX_COORDS);
44 |
45 | /**
46 | * Simple square, specified as a triangle strip. The square is centered on (0,0) and has
47 | * a size of 1x1.
48 | *
49 | * Triangles are 0-1-2 and 2-1-3 (counter-clockwise winding).
50 | */
51 | private static final float RECTANGLE_COORDS[] = {
52 | -0.5f, -0.5f, // 0 bottom left
53 | 0.5f, -0.5f, // 1 bottom right
54 | -0.5f, 0.5f, // 2 top left
55 | 0.5f, 0.5f, // 3 top right
56 | };
57 | private static final float RECTANGLE_TEX_COORDS[] = {
58 | 0.0f, 1.0f, // 0 bottom left
59 | 1.0f, 1.0f, // 1 bottom right
60 | 0.0f, 0.0f, // 2 top left
61 | 1.0f, 0.0f // 3 top right
62 | };
63 | private static final FloatBuffer RECTANGLE_BUF =
64 | GlUtil.createFloatBuffer(RECTANGLE_COORDS);
65 | private static final FloatBuffer RECTANGLE_TEX_BUF =
66 | GlUtil.createFloatBuffer(RECTANGLE_TEX_COORDS);
67 |
68 | /**
69 | * A "full" square, extending from -1 to +1 in both dimensions. When the model/view/projection
70 | * matrix is identity, this will exactly cover the viewport.
71 | *
72 | * The texture coordinates are Y-inverted relative to RECTANGLE. (This seems to work out
73 | * right with external textures from SurfaceTexture.)
74 | */
75 | private static final float FULL_RECTANGLE_COORDS[] = {
76 | -1.0f, -1.0f, // 0 bottom left
77 | 1.0f, -1.0f, // 1 bottom right
78 | -1.0f, 1.0f, // 2 top left
79 | 1.0f, 1.0f, // 3 top right
80 | };
81 | private static final float FULL_RECTANGLE_TEX_COORDS[] = {
82 | 0.0f, 0.0f, // 0 bottom left
83 | 1.0f, 0.0f, // 1 bottom right
84 | 0.0f, 1.0f, // 2 top left
85 | 1.0f, 1.0f // 3 top right
86 | };
87 | private static final FloatBuffer FULL_RECTANGLE_BUF =
88 | GlUtil.createFloatBuffer(FULL_RECTANGLE_COORDS);
89 | private static final FloatBuffer FULL_RECTANGLE_TEX_BUF =
90 | GlUtil.createFloatBuffer(FULL_RECTANGLE_TEX_COORDS);
91 |
92 |
93 | private FloatBuffer mVertexArray;
94 | private FloatBuffer mTexCoordArray;
95 | private int mVertexCount;
96 | private int mCoordsPerVertex;
97 | private int mVertexStride;
98 | private int mTexCoordStride;
99 | private Prefab mPrefab;
100 |
101 | /**
102 | * Enum values for constructor.
103 | */
104 | public enum Prefab {
105 | TRIANGLE, RECTANGLE, FULL_RECTANGLE
106 | }
107 |
108 | /**
109 | * Prepares a drawable from a "pre-fabricated" shape definition.
110 | *
111 | * Does no EGL/GL operations, so this can be done at any time.
112 | */
113 | public Drawable2d(Prefab shape) {
114 | switch (shape) {
115 | case TRIANGLE:
116 | mVertexArray = TRIANGLE_BUF;
117 | mTexCoordArray = TRIANGLE_TEX_BUF;
118 | mCoordsPerVertex = 2;
119 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
120 | mVertexCount = TRIANGLE_COORDS.length / mCoordsPerVertex;
121 | break;
122 | case RECTANGLE:
123 | mVertexArray = RECTANGLE_BUF;
124 | mTexCoordArray = RECTANGLE_TEX_BUF;
125 | mCoordsPerVertex = 2;
126 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
127 | mVertexCount = RECTANGLE_COORDS.length / mCoordsPerVertex;
128 | break;
129 | case FULL_RECTANGLE:
130 | mVertexArray = FULL_RECTANGLE_BUF;
131 | mTexCoordArray = FULL_RECTANGLE_TEX_BUF;
132 | mCoordsPerVertex = 2;
133 | mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
134 | mVertexCount = FULL_RECTANGLE_COORDS.length / mCoordsPerVertex;
135 | break;
136 | default:
137 | throw new RuntimeException("Unknown shape " + shape);
138 | }
139 | mTexCoordStride = 2 * SIZEOF_FLOAT;
140 | mPrefab = shape;
141 | }
142 |
143 | /**
144 | * Returns the array of vertices.
145 | *
146 | * To avoid allocations, this returns internal state. The caller must not modify it.
147 | */
148 | public FloatBuffer getVertexArray() {
149 | return mVertexArray;
150 | }
151 |
152 | /**
153 | * Returns the array of texture coordinates.
154 | *
155 | * To avoid allocations, this returns internal state. The caller must not modify it.
156 | */
157 | public FloatBuffer getTexCoordArray() {
158 | return mTexCoordArray;
159 | }
160 |
161 | /**
162 | * Returns the number of vertices stored in the vertex array.
163 | */
164 | public int getVertexCount() {
165 | return mVertexCount;
166 | }
167 |
168 | /**
169 | * Returns the width, in bytes, of the data for each vertex.
170 | */
171 | public int getVertexStride() {
172 | return mVertexStride;
173 | }
174 |
175 | /**
176 | * Returns the width, in bytes, of the data for each texture coordinate.
177 | */
178 | public int getTexCoordStride() {
179 | return mTexCoordStride;
180 | }
181 |
182 | /**
183 | * Returns the number of position coordinates per vertex. This will be 2 or 3.
184 | */
185 | public int getCoordsPerVertex() {
186 | return mCoordsPerVertex;
187 | }
188 |
189 | @Override
190 | public String toString() {
191 | if (mPrefab != null) {
192 | return "[Drawable2d: " + mPrefab + "]";
193 | } else {
194 | return "[Drawable2d: ...]";
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/EglRecordEnv.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo.utils.gles;
2 |
3 | import android.view.Surface;
4 |
5 | import javax.microedition.khronos.egl.EGLContext;
6 | import javax.microedition.khronos.egl.EGLSurface;
7 |
8 | /**
9 | * Created by huangwei on 2016/4/8.
10 | */
11 | public class EglRecordEnv {
12 | private WindowSurface mInputWindowSurface;
13 | private EglCore mEglCore;
14 |
15 | public EglRecordEnv(Surface surface, EGLContext sharedContext, boolean releaseSurface) {
16 | this(surface, sharedContext, releaseSurface, EglCore.FLAG_RECORDABLE);
17 | }
18 |
19 | public EglRecordEnv(Surface surface, EGLContext sharedContext, boolean releaseSurface, int flag) {
20 | mEglCore = new EglCore(sharedContext, flag);
21 | mInputWindowSurface = new WindowSurface(mEglCore, surface, releaseSurface);
22 | }
23 |
24 | public void makeCurrent() {
25 | mInputWindowSurface.makeCurrent();
26 | }
27 |
28 | public void swapBuffers() {
29 | // mInputWindowSurface.setPresentationTime(System.nanoTime());
30 | mInputWindowSurface.swapBuffers();
31 | }
32 |
33 | public void release(boolean releaseWindowSurface) {
34 | if (releaseWindowSurface) {
35 | mInputWindowSurface.release();
36 | }
37 | mEglCore.releaseNotMakeCurrent();
38 | }
39 |
40 | public void releaseAndMakeCurrent(boolean releaseWindowSurface) {
41 | if (releaseWindowSurface) {
42 | mInputWindowSurface.release();
43 | }
44 | mEglCore.release();
45 | }
46 |
47 | public EGLSurface getEGLSurface() {
48 | return mInputWindowSurface == null ? null : mInputWindowSurface.getEGLSurface();
49 | }
50 |
51 | public void makeCurrentReadFrom(EGLSurface mEGLSurface) {
52 | mInputWindowSurface.makeCurrentReadFrom(mEGLSurface);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/EglSurfaceBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | import android.graphics.Bitmap;
20 | import android.opengl.GLES20;
21 | import android.util.Log;
22 |
23 | import javax.microedition.khronos.egl.EGL10;
24 | import javax.microedition.khronos.egl.EGLSurface;
25 | import java.io.BufferedOutputStream;
26 | import java.io.File;
27 | import java.io.FileOutputStream;
28 | import java.io.IOException;
29 | import java.nio.ByteBuffer;
30 | import java.nio.ByteOrder;
31 |
32 | /**
33 | * Common base class for EGL surfaces.
34 | *
35 | * There can be multiple surfaces associated with a single context.
36 | */
37 | public class EglSurfaceBase {
38 | protected static final String TAG = GlUtil.TAG;
39 |
40 | // EglCore object we're associated with. It may be associated with multiple surfaces.
41 | protected EglCore mEglCore;
42 |
43 | private EGLSurface mEGLSurface = EGL10.EGL_NO_SURFACE;
44 | private int mWidth = -1;
45 | private int mHeight = -1;
46 |
47 | protected EglSurfaceBase(EglCore eglCore) {
48 | mEglCore = eglCore;
49 | }
50 |
51 | /**
52 | * Creates a window surface.
53 | *
54 | *
55 | * @param surface May be a Surface or SurfaceTexture.
56 | */
57 | public void createWindowSurface(Object surface) {
58 | if (mEGLSurface != EGL10.EGL_NO_SURFACE) {
59 | throw new IllegalStateException("surface already created");
60 | }
61 | mEGLSurface = mEglCore.createWindowSurface(surface);
62 |
63 | // Don't cache width/height here, because the size of the underlying surface can change
64 | // out from under us (see e.g. HardwareScalerActivity).
65 | //mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
66 | //mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
67 | }
68 |
69 | /**
70 | * Creates an off-screen surface.
71 | */
72 | public void createOffscreenSurface(int width, int height) {
73 | if (mEGLSurface != EGL10.EGL_NO_SURFACE) {
74 | throw new IllegalStateException("surface already created");
75 | }
76 | mEGLSurface = mEglCore.createOffscreenSurface(width, height);
77 | mWidth = width;
78 | mHeight = height;
79 | }
80 |
81 | /**
82 | * Returns the surface's width, in pixels.
83 | *
84 | * If this is called on a window surface, and the underlying surface is in the process
85 | * of changing size, we may not see the new size right away (e.g. in the "surfaceChanged"
86 | * callback). The size should match after the next buffer swap.
87 | */
88 | public int getWidth() {
89 | if (mWidth < 0) {
90 | return mEglCore.querySurface(mEGLSurface, EGL10.EGL_WIDTH);
91 | } else {
92 | return mWidth;
93 | }
94 | }
95 |
96 | /**
97 | * Returns the surface's height, in pixels.
98 | */
99 | public int getHeight() {
100 | if (mHeight < 0) {
101 | return mEglCore.querySurface(mEGLSurface, EGL10.EGL_HEIGHT);
102 | } else {
103 | return mHeight;
104 | }
105 | }
106 |
107 | /**
108 | * Release the EGL surface.
109 | */
110 | public void releaseEglSurface() {
111 | mEglCore.releaseSurface(mEGLSurface);
112 | mEGLSurface = EGL10.EGL_NO_SURFACE;
113 | mWidth = mHeight = -1;
114 | }
115 |
116 | /**
117 | * Makes our EGL context and surface current.
118 | */
119 | public void makeCurrent() {
120 | mEglCore.makeCurrent(mEGLSurface);
121 | }
122 |
123 | /**
124 | * Makes our EGL context and surface current for drawing, using the supplied surface
125 | * for reading.
126 | */
127 | public void makeCurrentReadFrom(EglSurfaceBase readSurface) {
128 | mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface);
129 | }
130 |
131 | /**
132 | * Calls eglSwapBuffers. Use this to "publish" the current frame.
133 | *
134 | * @return false on failure
135 | */
136 | public boolean swapBuffers() {
137 | boolean result = mEglCore.swapBuffers(mEGLSurface);
138 | if (!result) {
139 | Log.d(TAG, "WARNING: swapBuffers() failed");
140 | }
141 | return result;
142 | }
143 |
144 | /**
145 | * Sends the presentation time stamp to EGL.
146 | *
147 | * @param nsecs Timestamp, in nanoseconds.
148 | */
149 | public void setPresentationTime(long nsecs) {
150 | mEglCore.setPresentationTime(mEGLSurface, nsecs);
151 | }
152 |
153 | /**
154 | * Saves the EGL surface to a file.
155 | *
156 | * Expects that this object's EGL surface is current.
157 | */
158 | public void saveFrame(File file) throws IOException {
159 | if (!mEglCore.isCurrent(mEGLSurface)) {
160 | throw new RuntimeException("Expected EGL context/surface is not current");
161 | }
162 |
163 | // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA
164 | // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap
165 | // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the
166 | // Bitmap "copy pixels" method wants the same format GL provides.
167 | //
168 | // Ideally we'd have some way to re-use the ByteBuffer, especially if we're calling
169 | // here often.
170 | //
171 | // Making this even more interesting is the upside-down nature of GL, which means
172 | // our output will look upside down relative to what appears on screen if the
173 | // typical GL conventions are used.
174 |
175 | String filename = file.toString();
176 |
177 | int width = getWidth();
178 | int height = getHeight();
179 | ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
180 | buf.order(ByteOrder.LITTLE_ENDIAN);
181 | GLES20.glReadPixels(0, 0, width, height,
182 | GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
183 | GlUtil.checkGlError("glReadPixels");
184 | buf.rewind();
185 |
186 | BufferedOutputStream bos = null;
187 | try {
188 | bos = new BufferedOutputStream(new FileOutputStream(filename));
189 | Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
190 | bmp.copyPixelsFromBuffer(buf);
191 | bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
192 | bmp.recycle();
193 | } finally {
194 | if (bos != null) {
195 | bos.close();
196 | }
197 | }
198 | Log.d(TAG, "Saved " + width + "x" + height + " frame as '" + filename + "'");
199 | }
200 |
201 | public void makeCurrentReadFrom(EGLSurface eglSurface) {
202 | mEglCore.makeCurrent(mEGLSurface, eglSurface);
203 | }
204 |
205 |
206 | public EGLSurface getEGLSurface() {
207 | return mEGLSurface;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/FlatShadedProgram.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | import android.opengl.GLES20;
20 | import android.util.Log;
21 |
22 | import java.nio.FloatBuffer;
23 |
24 | /**
25 | * GL program and supporting functions for flat-shaded rendering.
26 | */
27 | public class FlatShadedProgram {
28 | private static final String TAG = GlUtil.TAG;
29 |
30 | private static final String VERTEX_SHADER =
31 | "uniform mat4 uMVPMatrix;" +
32 | "attribute vec4 aPosition;" +
33 | "void main() {" +
34 | " gl_Position = uMVPMatrix * aPosition;" +
35 | "}";
36 |
37 | private static final String FRAGMENT_SHADER =
38 | "precision mediump float;" +
39 | "uniform vec4 uColor;" +
40 | "void main() {" +
41 | " gl_FragColor = uColor;" +
42 | "}";
43 |
44 | // Handles to the GL program and various components of it.
45 | private int mProgramHandle = -1;
46 | private int muColorLoc = -1;
47 | private int muMVPMatrixLoc = -1;
48 | private int maPositionLoc = -1;
49 |
50 |
51 | /**
52 | * Prepares the program in the current EGL context.
53 | */
54 | public FlatShadedProgram() {
55 | mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
56 | if (mProgramHandle == 0) {
57 | throw new RuntimeException("Unable to create program");
58 | }
59 | Log.d(TAG, "Created program " + mProgramHandle);
60 |
61 | // get locations of attributes and uniforms
62 |
63 | maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
64 | GlUtil.checkLocation(maPositionLoc, "aPosition");
65 | muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
66 | GlUtil.checkLocation(muMVPMatrixLoc, "uMVPMatrix");
67 | muColorLoc = GLES20.glGetUniformLocation(mProgramHandle, "uColor");
68 | GlUtil.checkLocation(muColorLoc, "uColor");
69 | }
70 |
71 | /**
72 | * Releases the program.
73 | */
74 | public void release() {
75 | GLES20.glDeleteProgram(mProgramHandle);
76 | mProgramHandle = -1;
77 | }
78 |
79 | /**
80 | * Issues the draw call. Does the full setup on every call.
81 | *
82 | * @param mvpMatrix The 4x4 projection matrix.
83 | * @param color A 4-element color vector.
84 | * @param vertexBuffer Buffer with vertex data.
85 | * @param firstVertex Index of first vertex to use in vertexBuffer.
86 | * @param vertexCount Number of vertices in vertexBuffer.
87 | * @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2).
88 | * @param vertexStride Width, in bytes, of the data for each vertex (often vertexCount *
89 | * sizeof(float)).
90 | */
91 | public void draw(float[] mvpMatrix, float[] color, FloatBuffer vertexBuffer,
92 | int firstVertex, int vertexCount, int coordsPerVertex, int vertexStride) {
93 | GlUtil.checkGlError("draw start");
94 |
95 | // Select the program.
96 | GLES20.glUseProgram(mProgramHandle);
97 | GlUtil.checkGlError("glUseProgram");
98 |
99 | // Copy the model / view / projection matrix over.
100 | GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0);
101 | GlUtil.checkGlError("glUniformMatrix4fv");
102 |
103 | // Copy the color vector in.
104 | GLES20.glUniform4fv(muColorLoc, 1, color, 0);
105 | GlUtil.checkGlError("glUniform4fv ");
106 |
107 | // Enable the "aPosition" vertex attribute.
108 | GLES20.glEnableVertexAttribArray(maPositionLoc);
109 | GlUtil.checkGlError("glEnableVertexAttribArray");
110 |
111 | // Connect vertexBuffer to "aPosition".
112 | GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex,
113 | GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
114 | GlUtil.checkGlError("glVertexAttribPointer");
115 |
116 | // Draw the rect.
117 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount);
118 | GlUtil.checkGlError("glDrawArrays");
119 |
120 | // Done -- disable vertex array and program.
121 | GLES20.glDisableVertexAttribArray(maPositionLoc);
122 | GLES20.glUseProgram(0);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/FullFrameRect.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | /**
20 | * This class essentially represents a viewport-sized sprite that will be rendered with
21 | * a texture, usually from an external source like the camera or video decoder.
22 | */
23 | public class FullFrameRect {
24 | private final Drawable2d mRectDrawable = new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE);
25 | private Texture2dProgram mProgram;
26 |
27 | /**
28 | * Prepares the object.
29 | *
30 | * @param program The program to use. FullFrameRect takes ownership, and will release
31 | * the program when no longer needed.
32 | */
33 | public FullFrameRect(Texture2dProgram program) {
34 | mProgram = program;
35 | }
36 |
37 | /**
38 | * Releases resources.
39 | *
40 | * This must be called with the appropriate EGL context current (i.e. the one that was
41 | * current when the constructor was called). If we're about to destroy the EGL context,
42 | * there's no value in having the caller make it current just to do this cleanup, so you
43 | * can pass a flag that will tell this function to skip any EGL-context-specific cleanup.
44 | */
45 | public void release(boolean doEglCleanup) {
46 | if (mProgram != null) {
47 | if (doEglCleanup) {
48 | mProgram.release();
49 | }
50 | mProgram = null;
51 | }
52 | }
53 |
54 | /**
55 | * Returns the program currently in use.
56 | */
57 | public Texture2dProgram getProgram() {
58 | return mProgram;
59 | }
60 |
61 | /**
62 | * Changes the program. The previous program will be released.
63 | *
64 | * The appropriate EGL context must be current.
65 | */
66 | public void changeProgram(Texture2dProgram program) {
67 | mProgram.release();
68 | mProgram = program;
69 | }
70 |
71 | /**
72 | * Creates a texture object suitable for use with drawFrame().
73 | */
74 | public int createTextureObject() {
75 | return mProgram.createTextureObject();
76 | }
77 |
78 | /**
79 | * Draws a viewport-filling rect, texturing it with the specified texture object.
80 | */
81 | public void drawFrame(int textureId, float[] texMatrix) {
82 | // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport.
83 | mProgram.draw(GlUtil.IDENTITY_MATRIX, mRectDrawable.getVertexArray(), 0,
84 | mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(),
85 | mRectDrawable.getVertexStride(),
86 | texMatrix, mRectDrawable.getTexCoordArray(), textureId,
87 | mRectDrawable.getTexCoordStride());
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/OffscreenSurface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | /**
20 | * Off-screen EGL surface (pbuffer).
21 | *
22 | * It's good practice to explicitly release() the surface, preferably from a "finally" block.
23 | */
24 | public class OffscreenSurface extends EglSurfaceBase {
25 | /**
26 | * Creates an off-screen surface with the specified width and height.
27 | */
28 | public OffscreenSurface(EglCore eglCore, int width, int height) {
29 | super(eglCore);
30 | createOffscreenSurface(width, height);
31 | }
32 |
33 | /**
34 | * Releases any resources associated with the surface.
35 | */
36 | public void release() {
37 | releaseEglSurface();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/Sprite2d.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | import android.opengl.Matrix;
20 |
21 | /**
22 | * Base class for a 2d object. Includes position, scale, rotation, and flat-shaded color.
23 | */
24 | public class Sprite2d {
25 | private static final String TAG = GlUtil.TAG;
26 |
27 | private Drawable2d mDrawable;
28 | private float mColor[];
29 | private int mTextureId;
30 | private float mAngle;
31 | private float mScaleX, mScaleY;
32 | private float mPosX, mPosY;
33 |
34 | private float[] mModelViewMatrix;
35 | private boolean mMatrixReady;
36 |
37 | private float[] mScratchMatrix = new float[16];
38 |
39 | public Sprite2d(Drawable2d drawable) {
40 | mDrawable = drawable;
41 | mColor = new float[4];
42 | mColor[3] = 1.0f;
43 | mTextureId = -1;
44 |
45 | mModelViewMatrix = new float[16];
46 | mMatrixReady = false;
47 | }
48 |
49 | /**
50 | * Re-computes mModelViewMatrix, based on the current values for rotation, scale, and
51 | * translation.
52 | */
53 | private void recomputeMatrix() {
54 | float[] modelView = mModelViewMatrix;
55 |
56 | Matrix.setIdentityM(modelView, 0);
57 | Matrix.translateM(modelView, 0, mPosX, mPosY, 0.0f);
58 | if (mAngle != 0.0f) {
59 | Matrix.rotateM(modelView, 0, mAngle, 0.0f, 0.0f, 1.0f);
60 | }
61 | Matrix.scaleM(modelView, 0, mScaleX, mScaleY, 1.0f);
62 | mMatrixReady = true;
63 | }
64 |
65 | /**
66 | * Returns the sprite scale along the X axis.
67 | */
68 | public float getScaleX() {
69 | return mScaleX;
70 | }
71 |
72 | /**
73 | * Returns the sprite scale along the Y axis.
74 | */
75 | public float getScaleY() {
76 | return mScaleY;
77 | }
78 |
79 | /**
80 | * Sets the sprite scale (size).
81 | */
82 | public void setScale(float scaleX, float scaleY) {
83 | mScaleX = scaleX;
84 | mScaleY = scaleY;
85 | mMatrixReady = false;
86 | }
87 |
88 | /**
89 | * Gets the sprite rotation angle, in degrees.
90 | */
91 | public float getRotation() {
92 | return mAngle;
93 | }
94 |
95 | /**
96 | * Sets the sprite rotation angle, in degrees. Sprite will rotate counter-clockwise.
97 | */
98 | public void setRotation(float angle) {
99 | // Normalize. We're not expecting it to be way off, so just iterate.
100 | while (angle >= 360.0f) {
101 | angle -= 360.0f;
102 | }
103 | while (angle <= -360.0f) {
104 | angle += 360.0f;
105 | }
106 | mAngle = angle;
107 | mMatrixReady = false;
108 | }
109 |
110 | /**
111 | * Returns the position on the X axis.
112 | */
113 | public float getPositionX() {
114 | return mPosX;
115 | }
116 |
117 | /**
118 | * Returns the position on the Y axis.
119 | */
120 | public float getPositionY() {
121 | return mPosY;
122 | }
123 |
124 | /**
125 | * Sets the sprite position.
126 | */
127 | public void setPosition(float posX, float posY) {
128 | mPosX = posX;
129 | mPosY = posY;
130 | mMatrixReady = false;
131 | }
132 |
133 | /**
134 | * Returns the model-view matrix.
135 | *
136 | * To avoid allocations, this returns internal state. The caller must not modify it.
137 | */
138 | public float[] getModelViewMatrix() {
139 | if (!mMatrixReady) {
140 | recomputeMatrix();
141 | }
142 | return mModelViewMatrix;
143 | }
144 |
145 | /**
146 | * Sets color to use for flat-shaded rendering. Has no effect on textured rendering.
147 | */
148 | public void setColor(float red, float green, float blue) {
149 | mColor[0] = red;
150 | mColor[1] = green;
151 | mColor[2] = blue;
152 | }
153 |
154 | /**
155 | * Sets texture to use for textured rendering. Has no effect on flat-shaded rendering.
156 | */
157 | public void setTexture(int textureId) {
158 | mTextureId = textureId;
159 | }
160 |
161 | /**
162 | * Returns the color.
163 | *
164 | * To avoid allocations, this returns internal state. The caller must not modify it.
165 | */
166 | public float[] getColor() {
167 | return mColor;
168 | }
169 |
170 | /**
171 | * Draws the rectangle with the supplied program and projection matrix.
172 | */
173 | public void draw(FlatShadedProgram program, float[] projectionMatrix) {
174 | // Compute model/view/projection matrix.
175 | Matrix.multiplyMM(mScratchMatrix, 0, projectionMatrix, 0, getModelViewMatrix(), 0);
176 |
177 | program.draw(mScratchMatrix, mColor, mDrawable.getVertexArray(), 0,
178 | mDrawable.getVertexCount(), mDrawable.getCoordsPerVertex(),
179 | mDrawable.getVertexStride());
180 | }
181 |
182 | /**
183 | * Draws the rectangle with the supplied program and projection matrix.
184 | */
185 | public void draw(Texture2dProgram program, float[] projectionMatrix) {
186 | // Compute model/view/projection matrix.
187 | Matrix.multiplyMM(mScratchMatrix, 0, projectionMatrix, 0, getModelViewMatrix(), 0);
188 |
189 | program.draw(mScratchMatrix, mDrawable.getVertexArray(), 0,
190 | mDrawable.getVertexCount(), mDrawable.getCoordsPerVertex(),
191 | mDrawable.getVertexStride(), GlUtil.IDENTITY_MATRIX, mDrawable.getTexCoordArray(),
192 | mTextureId, mDrawable.getTexCoordStride());
193 | }
194 |
195 | @Override
196 | public String toString() {
197 | return "[Sprite2d pos=" + mPosX + "," + mPosY +
198 | " scale=" + mScaleX + "," + mScaleY + " angle=" + mAngle +
199 | " color={" + mColor[0] + "," + mColor[1] + "," + mColor[2] +
200 | "} drawable=" + mDrawable + "]";
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/library/src/main/java/us/pinguo/svideo/utils/gles/WindowSurface.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package us.pinguo.svideo.utils.gles;
18 |
19 | import android.graphics.SurfaceTexture;
20 | import android.view.Surface;
21 |
22 | import javax.microedition.khronos.egl.EGLSurface;
23 |
24 | /**
25 | * Recordable EGL window surface.
26 | *
27 | * It's good practice to explicitly release() the surface, preferably from a "finally" block.
28 | */
29 | public class WindowSurface extends EglSurfaceBase {
30 | private Surface mSurface;
31 | private boolean mReleaseSurface;
32 |
33 | /**
34 | * Associates an EGL surface with the native window surface.
35 | *
36 | * Set releaseSurface to true if you want the Surface to be released when release() is
37 | * called. This is convenient, but can interfere with framework classes that expect to
38 | * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the
39 | * surfaceDestroyed() callback won't fire).
40 | */
41 | public WindowSurface(EglCore eglCore, Surface surface, boolean releaseSurface) {
42 | super(eglCore);
43 | createWindowSurface(surface);
44 | mSurface = surface;
45 | mReleaseSurface = releaseSurface;
46 | }
47 |
48 | /**
49 | * Associates an EGL surface with the SurfaceTexture.
50 | */
51 | public WindowSurface(EglCore eglCore, SurfaceTexture surfaceTexture) {
52 | super(eglCore);
53 | createWindowSurface(surfaceTexture);
54 | }
55 |
56 | /**
57 | * Releases any resources associated with the EGL surface (and, if configured to do so,
58 | * with the Surface as well).
59 | *
60 | * Does not require that the surface's EGL context be current.
61 | */
62 | public void release() {
63 | releaseEglSurface();
64 | if (mSurface != null) {
65 | if (mReleaseSurface) {
66 | mSurface.release();
67 | }
68 | mSurface = null;
69 | }
70 | }
71 |
72 | /**
73 | * Recreate the EGLSurface, using the new EglBase. The caller should have already
74 | * freed the old EGLSurface with releaseEglSurface().
75 | *
76 | * This is useful when we want to update the EGLSurface associated with a Surface.
77 | * For example, if we want to share with a different EGLContext, which can only
78 | * be done by tearing down and recreating the context. (That's handled by the caller;
79 | * this just creates a new EGLSurface for the Surface we were handed earlier.)
80 | *
81 | * If the previous EGLSurface isn't fully destroyed, e.g. it's still current on a
82 | * context somewhere, the create call will fail with complaints from the Surface
83 | * about already being connected.
84 | */
85 | public void recreate(EglCore newEglCore) {
86 | if (mSurface == null) {
87 | throw new RuntimeException("not yet implemented for SurfaceTexture");
88 | }
89 | mEglCore = newEglCore; // switch to new context
90 | createWindowSurface(mSurface); // create new surface
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/library/src/main/jniLibs/arm64-v8a/libNSVUtil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/library/src/main/jniLibs/arm64-v8a/libNSVUtil.so
--------------------------------------------------------------------------------
/library/src/main/jniLibs/armeabi-v7a/libNSVUtil.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/library/src/main/jniLibs/armeabi-v7a/libNSVUtil.so
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Svideo
3 |
4 |
--------------------------------------------------------------------------------
/library/src/test/java/us/pinguo/svideo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package us.pinguo.svideo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # 默认放在工程根目录下
3 | # array里面填写改工程下所有需要发布的模块,注意按依赖顺序写入,比如library1依赖library2,则library2在前,library1在后
4 | # ------配置区域------
5 | array=(
6 | library
7 | )
8 | myPath=./releaseNote/
9 |
10 |
11 | # ------执行区域------
12 | if [ ! -d "$myPath" ];then
13 | mkdir "$myPath"
14 | fi
15 |
16 | version=$(grep "publish_version=" gradle.properties)
17 | version=${version#*publish_version=}
18 |
19 | # check version
20 | dot_num=$(echo $version|grep -o '\.'|wc -l)
21 | if [ $dot_num -ne 2 ]
22 | then
23 | echo "Error!$version not allowed, only two separator is allowed(x.x.x or x.x.x-SNAPSHOT)"
24 | exit 1;
25 | fi
26 |
27 | output="$myPath$version.txt"
28 |
29 | count=${#array[@]}
30 | echo '' |tee -a ${output}
31 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a ${output}
32 | date |tee -a ${output}
33 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a ${output}
34 | for (( i = 0; i < count; i+=1 )); do
35 | gradle -q ${array[i]}:uploadArchives |tee -a ${output}
36 | gradle -q ${array[i]}:dependencies --configuration archives |tee -a ${output}
37 | done
38 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a ${output}
39 | echo 'END_OF_PUBLISH' |tee -a ${output}
40 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a ${output}
41 | echo '' |tee -a ${output}
42 | echo '' |tee -a ${output}
43 |
44 |
--------------------------------------------------------------------------------
/publish_init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ------------------ #
4 | # @author mr #
5 | # @version 1.0.1 #
6 | # ------------------ #
7 |
8 | # 判断内容是否存在,不存在则添加相应内容
9 | # s1=filename, s2=contentToCheckIfExist, s3=appendContent, s4=appendPosition
10 | appendIfNotExist() {
11 | touch $1
12 | if checkIfNotExist "$1" "$2"
13 | then
14 | echo 'do write file'
15 | if [ -n "$4" ]
16 | then
17 | sed -i '' "/$4/a\\
18 | $3\\
19 | " $1
20 | else
21 | echo >> $1
22 | echo $3 >> $1
23 | fi
24 | else
25 | echo 'do nothing'
26 | fi
27 | }
28 |
29 | # 判断内容是否存在,不存在则要求从终端输入
30 | # s1=filename, s2=contentToCheckIfExist
31 | checkIfNotExist() {
32 | touch $1
33 | if grep "$2" $1 >/dev/null
34 | then
35 | return 1
36 | else
37 | return 0
38 | fi
39 | }
40 |
41 | # 定义工具版本
42 | _publish_tool_version=1.0.1
43 |
44 | # 检查输入参数是否合法,至少需要一个library
45 | if [ $# -lt 1 ] ; then
46 | echo "USAGE: publish_init.sh LIBRARY_NAME...(传入需要初始化的发布library)"
47 | echo " e.g.: publish_init.sh library3 library2 library1(注意,被依赖的库顺序需要放在前面,如library1依赖library2,则library2在前)"
48 | exit 1;
49 | fi
50 |
51 | echo --START--
52 | # 提示输入,获取所有输入内容
53 | if checkIfNotExist "gradle.properties" "publish_group="
54 | then
55 | echo "输入发布域名(publish_group, eg: us.pinguo.tools):"
56 | read _group
57 | else
58 | echo 'publish_group exist'
59 | fi
60 |
61 | if checkIfNotExist "gradle.properties" "publish_version="
62 | then
63 | echo "输入发布版本号(publish_version, eg: 1.0.0-SNAPSHOT):"
64 | read _version
65 | else
66 | echo 'publish_version exist'
67 | fi
68 |
69 | if checkIfNotExist "local.properties" "MAVEN_USERNAME="
70 | then
71 | echo "输入maven用户名(MAVEN_USERNAME, eg: marui):"
72 | read _username
73 | else
74 | echo 'MAVEN_USERNAME exist'
75 | fi
76 |
77 | if checkIfNotExist "local.properties" "MAVEN_PASSWORD="
78 | then
79 | echo "输入maven密码(MAVEN_PASSWORD, eg: ******):"
80 | read -s _password
81 | else
82 | echo 'MAVEN_PASSWORD exist'
83 | fi
84 |
85 | # 添加plugin classpath
86 | echo '-----Start append classpath in build.gradle-----'
87 | appendIfNotExist "build.gradle" "classpath 'us.pinguo.tool:publish:" "classpath 'us.pinguo.tool:publish:$_publish_tool_version'" "dependencies {"
88 | # 更新plugin版本号
89 | sed -i '' "s/'us.pinguo.tool:publish:*.*.*'/'us.pinguo.tool:publish:$_publish_tool_version'/" build.gradle
90 | # 添加配置group
91 | echo '-----Start append group in gradle.properties-----'
92 | appendIfNotExist "gradle.properties" "publish_group=" "publish_group=$_group"
93 | # 添加配置version
94 | echo '-----Start append version in gradle.properties-----'
95 | appendIfNotExist "gradle.properties" "publish_version=" "publish_version=$_version"
96 | # 添加配置username
97 | echo '-----Start append username in local.properties-----'
98 | appendIfNotExist "local.properties" "MAVEN_USERNAME=" "MAVEN_USERNAME=$_username"
99 | # 添加配置password
100 | echo '-----Start append password in local.properties-----'
101 | appendIfNotExist "local.properties" "MAVEN_PASSWORD=" "MAVEN_PASSWORD=$_password"
102 |
103 | # 获取所有参数
104 | libraries=""
105 | for args in $@;do
106 | libraries+=$args" "
107 | appendIfNotExist "$args/build.gradle" "apply plugin: 'us.pinguo.tool.publish'" "apply plugin: 'us.pinguo.tool.publish'" "apply plugin: 'com.android.library'"
108 |
109 | if checkIfNotExist "$args/build.gradle" "publish_id" == 1
110 | then
111 | echo "输入$args 的发布ID(publish_id, eg: camerasdk-core):"
112 | read _id
113 | appendIfNotExist "$args/build.gradle" "publish_id" "publish { publish_id '$_id' }" "apply plugin: 'us.pinguo.tool.publish'"
114 | else
115 | echo "$args publish_id exist"
116 | fi
117 | done
118 |
119 | # 生成发布脚本
120 | echo -----生成发布执行脚本-----
121 |
122 | echo """#!/bin/bash
123 | # 默认放在工程根目录下
124 | # array里面填写改工程下所有需要发布的模块,注意按依赖顺序写入,比如library1依赖library2,则library2在前,library1在后
125 | # ------配置区域------
126 | array=(
127 | $libraries
128 | )
129 | myPath=./releaseNote/
130 |
131 |
132 | # ------执行区域------
133 | if [ ! -d \"\$myPath\" ];then
134 | mkdir \"\$myPath\"
135 | fi
136 |
137 | version=\$(grep \"publish_version=\" gradle.properties)
138 | version=\${version#*publish_version=}
139 |
140 | # check version
141 | dot_num=\$(echo \$version|grep -o '\.'|wc -l)
142 | if [ \$dot_num -ne 2 ]
143 | then
144 | echo \"Error!\$version not allowed, only two separator is allowed(x.x.x or x.x.x-SNAPSHOT)\"
145 | exit 1;
146 | fi
147 |
148 | output=\"\$myPath\$version.txt\"
149 |
150 | count=\${#array[@]}
151 | echo '' |tee -a \${output}
152 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a \${output}
153 | date |tee -a \${output}
154 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a \${output}
155 | for (( i = 0; i < count; i+=1 )); do
156 | ./gradlew -q \${array[i]}:uploadArchives |tee -a \${output}
157 | ./gradlew -q \${array[i]}:dependencies --configuration archives |tee -a \${output}
158 | done
159 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a \${output}
160 | echo 'END_OF_PUBLISH' |tee -a \${output}
161 | echo '------------------------------------------------------------------------------------------------------------------------' |tee -a \${output}
162 | echo '' |tee -a \${output}
163 | echo '' |tee -a \${output}
164 | """ > publish.sh
165 | chmod +x publish.sh
166 | echo -----生成发布脚本成功, 执行publish.sh可以发布组件-----
167 | echo --FINISH--
--------------------------------------------------------------------------------
/readme/ISVideoRecorder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/readme/ISVideoRecorder.png
--------------------------------------------------------------------------------
/readme/demo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/readme/demo1.png
--------------------------------------------------------------------------------
/readme/demo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/readme/demo2.png
--------------------------------------------------------------------------------
/releaseNote/1.5.3-SNAPSHOT.txt:
--------------------------------------------------------------------------------
1 |
2 | ------------------------------------------------------------------------------------------------------------------------
3 | 2017年12月25日 14:42:41
4 | ------------------------------------------------------------------------------------------------------------------------
5 | ------------------------------------------------------------------------------------------------------------------------
6 | END_OF_PUBLISH
7 | ------------------------------------------------------------------------------------------------------------------------
8 |
9 |
10 |
11 | ------------------------------------------------------------------------------------------------------------------------
12 | 2017年12月25日 14:44:54
13 | ------------------------------------------------------------------------------------------------------------------------
14 | ------------------------------------------------------------------------------------------------------------------------
15 | END_OF_PUBLISH
16 | ------------------------------------------------------------------------------------------------------------------------
17 |
18 |
19 |
--------------------------------------------------------------------------------
/releaseNote/1.5.3-SNAP_SHOT[D[D[D[[3~[3~[[3~[3~[3~[3~[3~[C[C[CSHOT.txt:
--------------------------------------------------------------------------------
1 |
2 | ------------------------------------------------------------------------------------------------------------------------
3 | 2017年12月25日 14:27:09
4 | ------------------------------------------------------------------------------------------------------------------------
5 | ------------------------------------------------------------------------------------------------------------------------
6 | END_OF_PUBLISH
7 | ------------------------------------------------------------------------------------------------------------------------
8 |
9 |
10 |
--------------------------------------------------------------------------------
/releaseNote/1.5.3.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yellowcath/SVideoRecorder/82c6f7f044ce5e46242a656ab1bbb74d1098cb1e/releaseNote/1.5.3.txt
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demo', ':library'
2 |
--------------------------------------------------------------------------------