Skip to content

Commit

Permalink
add support for native audio track
Browse files Browse the repository at this point in the history
  • Loading branch information
havlenapetr committed Jul 15, 2010
1 parent 1df437c commit b5368b5
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 41 deletions.
5 changes: 5 additions & 0 deletions jni/ffmpeg/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ LOCAL_CFLAGS := -D__STDC_CONSTANT_MACROS
LOCAL_CFLAGS += -DBUILD_WITH_PLAYER
#endif

LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../include

LOCAL_SRC_FILES := \
android/onLoad.cpp \
android/com_media_ffmpeg_FFMpegAVFrame.cpp \
Expand All @@ -30,6 +33,8 @@ LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := liblog
endif

LOCAL_SHARED_LIBRARIES := libjniaudio

LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libpostproc libswscale

LOCAL_MODULE := libffmpeg_jni
Expand Down
47 changes: 32 additions & 15 deletions jni/ffmpeg/android/com_media_ffmpeg_android_FFMpegPlayerAndroid.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <unistd.h>
#include <android/log.h>
#include <android/bitmap.h>
#include <android/audiotrack.h>
#include "jniUtils.h"
#include "methods.h"

Expand Down Expand Up @@ -31,14 +32,12 @@ struct ffmpeg_audio_t {
int stream;
AVCodecContext *codec_ctx;
AVCodec *codec;
jbyteArray buffer_jni;
} ffmpeg_audio;

struct jni_fields_t {
jfieldID surface;
jfieldID avformatcontext;
jmethodID clb_onVideoFrame;
jmethodID clb_onAudioBuffer;
} jni_fields;

enum State {
Expand Down Expand Up @@ -232,14 +231,15 @@ static AVFrame *FFMpegPlayerAndroid_createFrame(JNIEnv *env, jobject bitmap) {
static void FFMpegPlayerAndroid_processAudio(JNIEnv *env, jobject obj, AVPacket *packet, int16_t *samples, int samples_length) {
// Try to decode the audio from the packet into the frame
int out_size = samples_length;
int len = -1;/*avcodec_decode_audio3(ffmpeg_audio.codec_ctx, samples,
&out_size, packet);*/
int len = avcodec_decode_audio3(ffmpeg_audio.codec_ctx, samples,
&out_size, packet);

//__android_log_print(ANDROID_LOG_INFO, TAG, "size: %i, len: %i, out_size: %i", packet->size, len, out_size);


/*
* call java callback for writing audio buffer to android audio track
*/
*
if(len > 0) {
if(ffmpeg_audio.buffer_jni == NULL) {
__android_log_print(ANDROID_LOG_INFO, TAG, "creating jni audio buffer");
Expand All @@ -249,14 +249,15 @@ static void FFMpegPlayerAndroid_processAudio(JNIEnv *env, jobject obj, AVPacket
env->CallVoidMethod(obj, jni_fields.clb_onAudioBuffer, ffmpeg_audio.buffer_jni);
//env->DeleteLocalRef(arr);
}
*/
}

static void FFMpegPlayerAndroid_play(JNIEnv *env, jobject obj, jobject bitmap) {
static void FFMpegPlayerAndroid_play(JNIEnv *env, jobject obj, jobject bitmap, jobject audioTrack) {
AVPacket packet;
int result = -1;
int frameFinished;
int audio_sample_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;

// Allocate an AVFrame structure
AVFrame *pFrameRGB = FFMpegPlayerAndroid_createFrame(env, bitmap);
if (pFrameRGB == NULL) {
Expand All @@ -267,6 +268,12 @@ static void FFMpegPlayerAndroid_play(JNIEnv *env, jobject obj, jobject bitmap) {
}

int16_t *samples = (int16_t *) av_malloc(audio_sample_size);
if(AndroidAudioTrack_register(env, audioTrack) != ANDROID_AUDIOTRACK_RESULT_SUCCESS) {
jniThrowException(env,
"java/io/IOException",
"Couldn't start audio track");
return;
}

status = STATE_PLAYING;
while (status != STATE_STOPING) {
Expand Down Expand Up @@ -295,21 +302,35 @@ static void FFMpegPlayerAndroid_play(JNIEnv *env, jobject obj, jobject bitmap) {
env->CallVoidMethod(obj, jni_fields.clb_onVideoFrame);
}
} else if (packet.stream_index == ffmpeg_audio.stream) {
/*
int sample_size = FFMAX(packet.size * sizeof(*samples), audio_sample_size);
//__android_log_print(ANDROID_LOG_INFO, TAG, "orig. %i should be %i", audio_sample_size, sample_size);
if(audio_sample_size < sample_size) {
__android_log_print(ANDROID_LOG_INFO, TAG, "resizing audio buffer from %i to %i", audio_sample_size, sample_size);
av_free(samples);
audio_sample_size = sample_size;
samples = (int16_t *) av_malloc(sample_size);
}
FFMpegPlayerAndroid_processAudio(env, obj, &packet, samples, audio_sample_size);
*/
int out_size = audio_sample_size;
int len = avcodec_decode_audio3(ffmpeg_audio.codec_ctx, samples, &out_size, &packet);
if((result = AndroidAudioTrack_write(samples, out_size)) <= 0) {
jniThrowException(env,
"java/io/IOException",
"Couldn't write bytes to audio track");
return;
}
}

// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}


if(AndroidAudioTrack_unregister() != ANDROID_AUDIOTRACK_RESULT_SUCCESS) {
jniThrowException(env,
"java/io/IOException",
"Couldn't stop audio track");
}

av_free( samples );

// Free the RGB image
Expand Down Expand Up @@ -392,7 +413,7 @@ static JNINativeMethod methods[] = {
{ "nativeInit", "(Lcom/media/ffmpeg/FFMpegAVFormatContext;)Lcom/media/ffmpeg/FFMpegAVCodecContext;", (void*) FFMpegPlayerAndroid_init},
{ "nativeSetInputFile", "(Ljava/lang/String;)Lcom/media/ffmpeg/FFMpegAVFormatContext;", (void*) FFMpegPlayerAndroid_setInputFile },
{ "nativePause", "(Z)Z", (void*) FFMpegPlayerAndroid_pause},
{ "nativePlay", "(Landroid/graphics/Bitmap;)V", (void*) FFMpegPlayerAndroid_play },
{ "nativePlay", "(Landroid/graphics/Bitmap;Landroid/media/AudioTrack;)V", (void*) FFMpegPlayerAndroid_play },
{ "nativeStop", "()V", (void*) FFMpegPlayerAndroid_stop },
{ "nativeSetSurface", "(Landroid/view/Surface;)V", (void*) FFMpegPlayerAndroid_setSurface },
{ "nativeRelease", "()V", (void*) FFMpegPlayerAndroid_release },
Expand All @@ -413,10 +434,6 @@ int register_android_media_FFMpegPlayerAndroid(JNIEnv *env) {
if (jni_fields.clb_onVideoFrame == NULL) {
return JNI_ERR;
}
jni_fields.clb_onAudioBuffer = env->GetMethodID(FFMpegPlayerAndroid_getClass(env), "onAudioBuffer", "([B)V");
if (jni_fields.clb_onAudioBuffer == NULL) {
return JNI_ERR;
}
jni_fields.avformatcontext = env->GetFieldID(AVFormatContext_getClass(env), "pointer", "I");
if (jni_fields.avformatcontext == NULL) {
return JNI_ERR;
Expand Down
42 changes: 42 additions & 0 deletions jni/include/android/audiotrack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef ANDROID_AUDIOTRACK_WRAPPER_H
#define ANDROID_AUDIOTRACK_WRAPPER_H

#include <stdint.h>
#include <jni.h>

#define ANDROID_AUDIOTRACK_RESULT_SUCCESS 0
#define ANDROID_AUDIOTRACK_RESULT_BAD_PARAMETER -1
#define ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION -2
#define ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED -3

#ifdef __cplusplus
extern "C" {
#endif

int AndroidAudioTrack_register(JNIEnv* env, jobject jaudioTrack);

int AndroidAudioTrack_unregister();

int AndroidAudioTrack_write(void *buffer, int buffer_size);

#ifdef __cplusplus
}
#endif

#endif
79 changes: 79 additions & 0 deletions jni/include/android/bitmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef ANDROID_BITMAP_H
#define ANDROID_BITMAP_H

#include <stdint.h>
#include <jni.h>

#ifdef __cplusplus
extern "C" {
#endif

#define ANDROID_BITMAP_RESUT_SUCCESS 0
#define ANDROID_BITMAP_RESULT_BAD_PARAMETER -1
#define ANDROID_BITMAP_RESULT_JNI_EXCEPTION -2
#define ANDROID_BITMAP_RESULT_ALLOCATION_FAILED -3

enum AndroidBitmapFormat {
ANDROID_BITMAP_FORMAT_NONE = 0,
ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
ANDROID_BITMAP_FORMAT_RGB_565 = 4,
ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
ANDROID_BITMAP_FORMAT_A_8 = 8,
};

typedef struct {
uint32_t width;
uint32_t height;
uint32_t stride;
int32_t format;
uint32_t flags; // 0 for now
} AndroidBitmapInfo;

/**
* Given a java bitmap object, fill out the AndroidBitmap struct for it.
* If the call fails, the info parameter will be ignored
*/
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info);

/**
* Given a java bitmap object, attempt to lock the pixel address.
* Locking will ensure that the memory for the pixels will not move
* until the unlockPixels call, and ensure that, if the pixels had been
* previously purged, they will have been restored.
*
* If this call succeeds, it must be balanced by a call to
* AndroidBitmap_unlockPixels, after which time the address of the pixels should
* no longer be used.
*
* If this succeeds, *addrPtr will be set to the pixel address. If the call
* fails, addrPtr will be ignored.
*/
int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

/**
* Call this to balanace a successful call to AndroidBitmap_lockPixels
*/
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);

#ifdef __cplusplus
}
#endif

#endif
Binary file added libs/armeabi/libjniaudio.so
Binary file not shown.
1 change: 1 addition & 0 deletions src/com/media/ffmpeg/FFMpeg.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class FFMpeg {
private boolean mConverting;

static {
System.loadLibrary("jniaudio");
System.loadLibrary(LIB_NAME);
}

Expand Down
34 changes: 8 additions & 26 deletions src/com/media/ffmpeg/android/FFMpegPlayerAndroid.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ private void initVideoView(Context context) {
mFitToScreen = true;
mVideoWidth = 0;
mVideoHeight = 0;
/*mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
44100,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
FFMpegAVCodecTag.AVCODEC_MAX_AUDIO_FRAME_SIZE,
AudioTrack.MODE_STATIC);*/
getHolder().addCallback(mSHCallback);
}

Expand Down Expand Up @@ -115,6 +109,12 @@ private void startVideo() {
// we hasn't run player thread so we are launching
if(mRenderThread == null) {
mBitmap = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.RGB_565);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
44100,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
FFMpegAVCodecTag.AVCODEC_MAX_AUDIO_FRAME_SIZE,
AudioTrack.MODE_STREAM);
attachMediaController();

mRenderThread = new Thread() {
Expand All @@ -125,7 +125,7 @@ public void run() {
}

try {
nativePlay(mBitmap);
nativePlay(mBitmap, mAudioTrack);
} catch (IOException e) {
Log.e(TAG, "Error while playing: " + e.getMessage());
mPlaying = false;
Expand Down Expand Up @@ -217,7 +217,6 @@ private void release() {
}

if(mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
Expand Down Expand Up @@ -280,23 +279,6 @@ private void onVideoFrame() {
}
}

private void onAudioBuffer(byte[] buffer) {
//Log.d(TAG, "buffer length " + buffer.length);

/*
Log.d(TAG, "***************************************");
for(int i=0;i<buffer.length; i++) {
Log.d(TAG, "buffer: " + buffer[i]);
}
Log.d(TAG, "***************************************");
*/
if(mAudioTrack != null) {
mAudioTrack.write(buffer, 0, buffer.length);
mAudioTrack.flush();
mAudioTrack.play();
}
}

private void doDraw(Canvas c) {
if(mFitToScreen) {
float scale_x = (float) mSurfaceWidth/ (float) mVideoWidth;
Expand Down Expand Up @@ -447,7 +429,7 @@ public int getBufferPercentage() {
* @param bitmap to which player will draw pixels
* @throws IOException
*/
private native void nativePlay(Bitmap bitmap)throws IOException;
private native void nativePlay(Bitmap bitmap, AudioTrack audioTrack) throws IOException;

/**
* stops playing movie
Expand Down

0 comments on commit b5368b5

Please sign in to comment.