Skip to content

Commit

Permalink
* Upgrade RecordActivity sample with a continuous record loop, tha…
Browse files Browse the repository at this point in the history
…nks to Federico Sendra and Juan Manuel Sobral
  • Loading branch information
saudet committed Sep 20, 2014
1 parent 7898e91 commit 1095cb7
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Upgrade `RecordActivity` sample with a continuous record loop, thanks to Federico Sendra and Juan Manuel Sobral
* Make `FrameGrabber.createDefault()` throw an exception on unsupported input, instead of returning a cryptic `null` ([issue #30](https://github.com/bytedeco/javacv/issues/30))
* Add `videoCodec`, `videoBitrate`, `audioCodec`, and `audioBitrate` properties to `FrameGrabber`
* Work around `avcodec` and `avdevice` not loading properly for `FFmpegFrameGrabber` and `FFmpegFrameRecorder` ([issue #24](https://github.com/bytedeco/javacv/issues/24))
Expand Down
99 changes: 90 additions & 9 deletions samples/RecordActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ public class RecordActivity extends Activity implements OnClickListener {
private int screenWidth, screenHeight;
private Button btnRecorderControl;

/** The number of seconds in the continuous record loop (or 0 to disable loop). */
final int RECORD_LENGTH = 10;
IplImage[] images;
long[] timestamps;
ShortBuffer[] samples;
int imagesIndex, samplesIndex;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -147,7 +154,6 @@ public void onCreate(Bundle savedInstanceState) {
mWakeLock.acquire();

initLayout();
initRecorder();
}


Expand Down Expand Up @@ -244,7 +250,15 @@ private void initRecorder() {

Log.w(LOG_TAG,"init recorder");

if (yuvIplimage == null) {
if (RECORD_LENGTH > 0) {
imagesIndex = 0;
images = new IplImage[RECORD_LENGTH * frameRate];
timestamps = new long[images.length];
for (int i = 0; i < images.length; i++) {
images[i] = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 2);
timestamps[i] = -1;
}
} else if (yuvIplimage == null) {
yuvIplimage = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_8U, 2);
Log.i(LOG_TAG, "create yuvIplimage");
}
Expand All @@ -265,6 +279,8 @@ private void initRecorder() {

public void startRecording() {

initRecorder();

try {
recorder.start();
startTime = System.currentTimeMillis();
Expand All @@ -288,6 +304,49 @@ public void stopRecording() {
audioThread = null;

if (recorder != null && recording) {
if (RECORD_LENGTH > 0) {
Log.v(LOG_TAG,"Writing frames");
try {
int firstIndex = imagesIndex % samples.length;
int lastIndex = (imagesIndex - 1) % images.length;
if (imagesIndex <= images.length) {
firstIndex = 0;
lastIndex = imagesIndex - 1;
}
if ((startTime = timestamps[lastIndex] - RECORD_LENGTH * 1000000L) < 0) {
startTime = 0;
}
if (lastIndex < firstIndex) {
lastIndex += images.length;
}
for (int i = firstIndex; i <= lastIndex; i++) {
long t = timestamps[i % timestamps.length] - startTime;
if (t >= 0) {
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(images[i % images.length]);
}
}

firstIndex = samplesIndex % samples.length;
lastIndex = (samplesIndex - 1) % samples.length;
if (samplesIndex <= samples.length) {
firstIndex = 0;
lastIndex = samplesIndex - 1;
}
if (lastIndex < firstIndex) {
lastIndex += samples.length;
}
for (int i = firstIndex; i <= lastIndex; i++) {
recorder.record(samples[i % samples.length]);
}
} catch (FFmpegFrameRecorder.Exception e) {
Log.v(LOG_TAG,e.getMessage());
e.printStackTrace();
}
}

recording = false;
Log.v(LOG_TAG,"Finishing recording, calling stop and release on recorder");
try {
Expand Down Expand Up @@ -329,30 +388,43 @@ public void run() {

// Audio
int bufferSize;
short[] audioData;
ShortBuffer audioData;
int bufferReadResult;

bufferSize = AudioRecord.getMinBufferSize(sampleAudioRateInHz,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioRateInHz,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

audioData = new short[bufferSize];
if (RECORD_LENGTH > 0) {
samplesIndex = 0;
samples = new ShortBuffer[RECORD_LENGTH * sampleAudioRateInHz * 2 / bufferSize + 1];
for (int i = 0; i < samples.length; i++) {
samples[i] = ShortBuffer.allocate(bufferSize);
}
} else {
audioData = ShortBuffer.allocate(bufferSize);
}

Log.d(LOG_TAG, "audioRecord.startRecording()");
audioRecord.startRecording();

/* ffmpeg_audio encoding loop */
while (runAudioThread) {
if (RECORD_LENGTH > 0) {
audioData = samples[samplesIndex++ % samples.length];
audioData.position(0).limit(0);
}
//Log.v(LOG_TAG,"recording? " + recording);
bufferReadResult = audioRecord.read(audioData, 0, audioData.length);
bufferReadResult = audioRecord.read(audioData.array(), 0, audioData.capacity());
audioData.limit(bufferReadResult);
if (bufferReadResult > 0) {
Log.v(LOG_TAG,"bufferReadResult: " + bufferReadResult);
// If "recording" isn't true when start this thread, it never get's set according to this if statement...!!!
// Why? Good question...
if (recording) {
try {
recorder.record(ShortBuffer.wrap(audioData, 0, bufferReadResult));
if (RECORD_LENGTH <= 0) try {
recorder.record(audioData);
//Log.v(LOG_TAG,"recording " + 1024*i + " to " + 1024*i+1024);
} catch (FFmpegFrameRecorder.Exception e) {
Log.v(LOG_TAG,e.getMessage());
Expand Down Expand Up @@ -440,12 +512,21 @@ public void stopPreview() {

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvIplimage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
if (yuvIplimage != null && recording) {
yuvIplimage.getByteBuffer().put(data);

Log.v(LOG_TAG,"Writing Frame");
try {
if (RECORD_LENGTH <= 0) try {
Log.v(LOG_TAG,"Writing Frame");
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameGrabber.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ public void releaseUnsafe() throws Exception {
buffer_rgb = null;
}
if (picture_rgb != null) {
avcodec_free_frame(picture_rgb);
av_frame_free(picture_rgb);
picture_rgb = null;
}

// Free the native format picture frame
if (picture != null) {
avcodec_free_frame(picture);
av_frame_free(picture);
picture = null;
}

Expand All @@ -155,7 +155,7 @@ public void releaseUnsafe() throws Exception {

// Free the audio samples frame
if (samples_frame != null) {
avcodec_free_frame(samples_frame);
av_frame_free(samples_frame);
samples_frame = null;
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,19 @@ public void releaseUnsafe() throws Exception {
picture_buf = null;
}
if (picture != null) {
avcodec_free_frame(picture);
av_frame_free(picture);
picture = null;
}
if (tmp_picture != null) {
avcodec_free_frame(tmp_picture);
av_frame_free(tmp_picture);
tmp_picture = null;
}
if (video_outbuf != null) {
av_free(video_outbuf);
video_outbuf = null;
}
if (frame != null) {
avcodec_free_frame(frame);
av_frame_free(frame);
frame = null;
}
if (samples_out != null) {
Expand Down

0 comments on commit 1095cb7

Please sign in to comment.