diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c60c937..155a497 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,7 +25,7 @@
-
+
\ No newline at end of file
diff --git a/default.properties b/default.properties
new file mode 100755
index 0000000..9d6f70d
--- /dev/null
+++ b/default.properties
@@ -0,0 +1,13 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-4
+# Indicates whether an apk should be generated for each density.
+split.density=false
diff --git a/jni/include/android/surface.h b/jni/include/android/surface.h
index f45c81e..dd017fc 100644
--- a/jni/include/android/surface.h
+++ b/jni/include/android/surface.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2011 Petr Havlena havlenapetr@gmail.com
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,13 +21,13 @@
#include
#include
-#define ANDROID_SURFACE_RESULT_SUCCESS 0
-#define ANDROID_SURFACE_RESULT_NOT_VALID -1
-#define ANDROID_SURFACE_RESULT_COULDNT_LOCK -2
-#define ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST -3
-#define ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE -4
-#define ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT -5
-#define ANDROID_SURFACE_RESULT_JNI_EXCEPTION -6
+#define ANDROID_SURFACE_RESULT_SUCCESS 0
+#define ANDROID_SURFACE_RESULT_NOT_VALID -1
+#define ANDROID_SURFACE_RESULT_COULDNT_LOCK -2
+#define ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST -3
+#define ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE -4
+#define ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT -5
+#define ANDROID_SURFACE_RESULT_JNI_EXCEPTION -6
#ifdef __cplusplus
extern "C" {
@@ -36,7 +37,7 @@ int AndroidSurface_register(JNIEnv* env, jobject jsurface);
int AndroidSurface_getPixels(int width, int height, void** pixels);
-int AndroidSurface_updateSurface();
+int AndroidSurface_updateSurface(bool autoscale = true);
int AndroidSurface_unregister();
diff --git a/jni/jni/com_media_ffmpeg_FFMpegPlayer.cpp b/jni/jni/com_media_ffmpeg_FFMpegPlayer.cpp
index 2210a0b..5e2b20f 100644
--- a/jni/jni/com_media_ffmpeg_FFMpegPlayer.cpp
+++ b/jni/jni/com_media_ffmpeg_FFMpegPlayer.cpp
@@ -86,9 +86,9 @@ static MediaPlayer* setMediaPlayer(JNIEnv* env, jobject thiz, MediaPlayer* playe
{
MediaPlayer* old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
if (old != NULL) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "freeing old mediaplayer object");
- free(old);
- }
+ __android_log_print(ANDROID_LOG_INFO, TAG, "freeing old mediaplayer object");
+ free(old);
+ }
env->SetIntField(thiz, fields.context, (int)player);
return old;
}
@@ -100,12 +100,12 @@ static MediaPlayer* setMediaPlayer(JNIEnv* env, jobject thiz, MediaPlayer* playe
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
- /*
+ /*
if (opStatus != (status_t) OK) {
sp mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
- */
+ */
} else { // Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -172,8 +172,8 @@ com_media_ffmpeg_FFMpegPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
- process_media_player_call( env, thiz, mp->setVideoSurface(env, jsurface),
- "java/io/IOException", "Set video surface failed.");
+ process_media_player_call( env, thiz, mp->setVideoSurface(env, jsurface),
+ "java/io/IOException", "Set video surface failed.");
}
static void
@@ -220,6 +220,28 @@ com_media_ffmpeg_FFMpegPlayer_pause(JNIEnv *env, jobject thiz)
process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
}
+static void
+com_media_ffmpeg_FFMpegPlayer_setResolution(JNIEnv *env, jobject thiz, jint width, jint height)
+{
+ MediaPlayer* mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+ process_media_player_call( env, thiz, mp->setResolution(width, height), NULL, NULL );
+}
+
+static void
+com_media_ffmpeg_FFMpegPlayer_setAutoscale(JNIEnv *env, jobject thiz, jboolean value)
+{
+ MediaPlayer* mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+ process_media_player_call( env, thiz, mp->setAutoscale(value), NULL, NULL );
+}
+
static jboolean
com_media_ffmpeg_FFMpegPlayer_isPlaying(JNIEnv *env, jobject thiz)
{
@@ -406,6 +428,8 @@ static JNINativeMethod gMethods[] = {
{"prepare", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_prepare},
{"_start", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_start},
{"_stop", "()V", (void *)com_media_ffmpeg_FFMpegPlayer_stop},
+ {"_setResolution", "(II)V", (void *)com_media_ffmpeg_FFMpegPlayer_setResolution},
+ {"_setAutoscale", "(Z)V", (void *)com_media_ffmpeg_FFMpegPlayer_setAutoscale},
{"getVideoWidth", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},
{"seekTo", "(I)V", (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},
diff --git a/jni/libmediaplayer/decoder.cpp b/jni/libmediaplayer/decoder.cpp
index 83df6a3..3c8a85a 100644
--- a/jni/libmediaplayer/decoder.cpp
+++ b/jni/libmediaplayer/decoder.cpp
@@ -5,28 +5,28 @@
IDecoder::IDecoder(AVStream* stream)
{
- mQueue = new PacketQueue();
- mStream = stream;
+ mQueue = new PacketQueue();
+ mStream = stream;
}
IDecoder::~IDecoder()
{
- if(mRunning)
+ if(mRunning)
{
stop();
}
- free(mQueue);
- avcodec_close(mStream->codec);
+ free(mQueue);
+ avcodec_close(mStream->codec);
}
void IDecoder::enqueue(AVPacket* packet)
{
- mQueue->put(packet);
+ mQueue->put(packet);
}
int IDecoder::packets()
{
- return mQueue->size();
+ return mQueue->size();
}
void IDecoder::stop()
@@ -34,20 +34,20 @@ void IDecoder::stop()
mQueue->abort();
__android_log_print(ANDROID_LOG_INFO, TAG, "waiting on end of decoder thread");
int ret = -1;
- if((ret = wait()) != 0) {
+ if((ret = join()) != 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "Couldn't cancel IDecoder: %i", ret);
return;
}
}
-void IDecoder::handleRun(void* ptr)
+void IDecoder::run()
{
- if(!prepare())
+ if(!prepare())
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "Couldn't prepare decoder");
+ __android_log_print(ANDROID_LOG_INFO, TAG, "Couldn't prepare decoder");
return;
}
- decode(ptr);
+ decode();
}
bool IDecoder::prepare()
@@ -57,10 +57,10 @@ bool IDecoder::prepare()
bool IDecoder::process(AVPacket *packet)
{
- return false;
+ return false;
}
-bool IDecoder::decode(void* ptr)
+bool IDecoder::decode()
{
return false;
}
diff --git a/jni/libmediaplayer/decoder.h b/jni/libmediaplayer/decoder.h
index 5916fc4..22ab40c 100644
--- a/jni/libmediaplayer/decoder.h
+++ b/jni/libmediaplayer/decoder.h
@@ -14,21 +14,22 @@ extern "C" {
class IDecoder : public Thread
{
public:
- IDecoder(AVStream* stream);
- ~IDecoder();
+ IDecoder(AVStream* stream);
+ ~IDecoder();
- void stop();
- void enqueue(AVPacket* packet);
- int packets();
+ void stop();
+ void enqueue(AVPacket* packet);
+ int packets();
protected:
- PacketQueue* mQueue;
- AVStream* mStream;
+ PacketQueue* mQueue;
+ AVStream* mStream;
- virtual bool prepare();
- virtual bool decode(void* ptr);
- virtual bool process(AVPacket *packet);
- void handleRun(void* ptr);
+ virtual bool prepare();
+ virtual bool decode();
+ virtual bool process(AVPacket *packet);
+
+ virtual void run();
};
#endif //FFMPEG_DECODER_H
diff --git a/jni/libmediaplayer/decoder_audio.cpp b/jni/libmediaplayer/decoder_audio.cpp
index 3ec469a..6954cf8 100644
--- a/jni/libmediaplayer/decoder_audio.cpp
+++ b/jni/libmediaplayer/decoder_audio.cpp
@@ -3,12 +3,14 @@
#define TAG "FFMpegAudioDecoder"
-DecoderAudio::DecoderAudio(AVStream* stream) : IDecoder(stream)
+DecoderAudio::DecoderAudio(AVStream* stream, DecoderAudioCallback* callback) : IDecoder(stream)
{
+ mCallback = callback;
}
DecoderAudio::~DecoderAudio()
{
+ delete mCallback;
}
bool DecoderAudio::prepare()
@@ -27,12 +29,12 @@ bool DecoderAudio::process(AVPacket *packet)
int len = avcodec_decode_audio3(mStream->codec, mSamples, &size, packet);
//call handler for posting buffer to os audio driver
- onDecode(mSamples, size);
+ mCallback->onDecode(mSamples, size);
return true;
}
-bool DecoderAudio::decode(void* ptr)
+bool DecoderAudio::decode()
{
AVPacket pPacket;
diff --git a/jni/libmediaplayer/decoder_audio.h b/jni/libmediaplayer/decoder_audio.h
index ace3652..1efd3f6 100644
--- a/jni/libmediaplayer/decoder_audio.h
+++ b/jni/libmediaplayer/decoder_audio.h
@@ -3,24 +3,27 @@
#include "decoder.h"
-typedef void (*AudioDecodingHandler) (int16_t*,int);
+class DecoderAudioCallback
+{
+public:
+ virtual void onDecode(int16_t* buffer, int buffer_size);
+};
class DecoderAudio : public IDecoder
{
public:
- DecoderAudio(AVStream* stream);
-
+ DecoderAudio(AVStream* stream, DecoderAudioCallback* callback);
~DecoderAudio();
- AudioDecodingHandler onDecode;
private:
- int16_t* mSamples;
- int mSamplesSize;
+ int16_t* mSamples;
+ int mSamplesSize;
+ DecoderAudioCallback* mCallback;
- bool prepare();
- bool decode(void* ptr);
- bool process(AVPacket *packet);
+ bool prepare();
+ bool decode();
+ bool process(AVPacket *packet);
};
#endif //FFMPEG_DECODER_AUDIO_H
diff --git a/jni/libmediaplayer/decoder_video.cpp b/jni/libmediaplayer/decoder_video.cpp
index 1bf3eb6..f76f675 100644
--- a/jni/libmediaplayer/decoder_video.cpp
+++ b/jni/libmediaplayer/decoder_video.cpp
@@ -5,23 +5,25 @@
static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE;
-DecoderVideo::DecoderVideo(AVStream* stream) : IDecoder(stream)
+DecoderVideo::DecoderVideo(AVStream* stream, DecoderVideoCallback* callback) : IDecoder(stream)
{
- mStream->codec->get_buffer = getBuffer;
- mStream->codec->release_buffer = releaseBuffer;
+ mCallback = callback;
+ mStream->codec->get_buffer = getBuffer;
+ mStream->codec->release_buffer = releaseBuffer;
}
DecoderVideo::~DecoderVideo()
{
+ delete mCallback;
}
bool DecoderVideo::prepare()
{
- mFrame = avcodec_alloc_frame();
- if (mFrame == NULL) {
- return false;
- }
- return true;
+ mFrame = avcodec_alloc_frame();
+ if (mFrame == NULL) {
+ return false;
+ }
+ return true;
}
double DecoderVideo::synchronize(AVFrame *src_frame, double pts) {
@@ -67,19 +69,17 @@ bool DecoderVideo::process(AVPacket *packet)
if (completed) {
pts = synchronize(mFrame, pts);
-
- onDecode(mFrame, pts);
-
+ mCallback->onDecode(mFrame, pts);
return true;
}
return false;
}
-bool DecoderVideo::decode(void* ptr)
+bool DecoderVideo::decode()
{
- AVPacket pPacket;
+ AVPacket pPacket;
- __android_log_print(ANDROID_LOG_INFO, TAG, "decoding video");
+ __android_log_print(ANDROID_LOG_INFO, TAG, "decoding video");
while(mRunning)
{
diff --git a/jni/libmediaplayer/decoder_video.h b/jni/libmediaplayer/decoder_video.h
index c7d742c..aec15a6 100644
--- a/jni/libmediaplayer/decoder_video.h
+++ b/jni/libmediaplayer/decoder_video.h
@@ -3,26 +3,30 @@
#include "decoder.h"
-typedef void (*VideoDecodingHandler) (AVFrame*,double);
+class DecoderVideoCallback
+{
+public:
+ virtual void onDecode(AVFrame* frame, double pts);
+};
class DecoderVideo : public IDecoder
{
public:
- DecoderVideo(AVStream* stream);
+ DecoderVideo(AVStream* stream, DecoderVideoCallback* callback);
~DecoderVideo();
- VideoDecodingHandler onDecode;
-
private:
- AVFrame* mFrame;
- double mVideoClock;
+ AVFrame* mFrame;
+ double mVideoClock;
+ DecoderVideoCallback* mCallback;
+
+ bool prepare();
+ double synchronize(AVFrame *src_frame, double pts);
+ bool decode();
+ bool process(AVPacket *packet);
- bool prepare();
- double synchronize(AVFrame *src_frame, double pts);
- bool decode(void* ptr);
- bool process(AVPacket *packet);
- static int getBuffer(struct AVCodecContext *c, AVFrame *pic);
- static void releaseBuffer(struct AVCodecContext *c, AVFrame *pic);
+ static int getBuffer(struct AVCodecContext *c, AVFrame *pic);
+ static void releaseBuffer(struct AVCodecContext *c, AVFrame *pic);
};
#endif //FFMPEG_DECODER_AUDIO_H
diff --git a/jni/libmediaplayer/mediaplayer.cpp b/jni/libmediaplayer/mediaplayer.cpp
index e171682..1e2d72d 100644
--- a/jni/libmediaplayer/mediaplayer.cpp
+++ b/jni/libmediaplayer/mediaplayer.cpp
@@ -2,9 +2,6 @@
* mediaplayer.cpp
*/
-//#define LOG_NDEBUG 0
-#define TAG "FFMpegMediaPlayer"
-
#include
#include
#include
@@ -22,15 +19,148 @@ extern "C" {
#include
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+
#include "mediaplayer.h"
#include "output.h"
-#define FPS_DEBUGGING false
+#define LOG_TAG "FFMpegMediaPlayer"
static MediaPlayer* sPlayer;
+class VideoCallback : public DecoderVideoCallback
+{
+public:
+ virtual void onDecode(AVFrame* frame, double pts);
+};
+
+void VideoCallback::onDecode(AVFrame* frame, double pts)
+{
+ // Convert the image from its native format to RGB
+ sws_scale(sPlayer->mConvertCtx,
+ frame->data,
+ frame->linesize,
+ 0,
+ sPlayer->mVideoHeight,
+ sPlayer->mFrame->data,
+ sPlayer->mFrame->linesize);
+
+ Output::VideoDriver_updateSurface(sPlayer->mAutoscale);
+}
+
+class AudioCallback : public DecoderAudioCallback
+{
+public:
+ virtual void onDecode(int16_t* buffer, int buffer_size);
+};
+
+void AudioCallback::onDecode(int16_t* buffer, int buffer_size)
+{
+ if(Output::AudioDriver_write(buffer, buffer_size) <= 0) {
+ LOGE("Couldn't write samples to audio track");
+ }
+}
+
+DecodeLoop::DecodeLoop(AVFormatContext* context, int audioStreamId, int videoStreamId, DecodeLoopCallback* callback)
+{
+ mCallback = callback;
+ mContext = context;
+ mAudioStreamId = audioStreamId;
+ mVideoStreamId = videoStreamId;
+ mEnding = false;
+}
+
+DecodeLoop::~DecodeLoop()
+{
+ LOGI("killing decode loop");
+
+ mEnding = true;
+
+ if(mDecoderAudio != NULL) {
+ mDecoderAudio->stop();
+ }
+ if(mDecoderVideo != NULL) {
+ mDecoderVideo->stop();
+ }
+
+ if(join() != 0) {
+ LOGE("Couldn't cancel player thread");
+ }
+
+ // Close the codec
+ delete mDecoderAudio;
+ delete mDecoderVideo;
+}
+
+void DecodeLoop::run()
+{
+ AVPacket pPacket;
+
+ AVStream* stream_audio = mContext->streams[mAudioStreamId];
+ mDecoderAudio = new DecoderAudio(stream_audio, new AudioCallback());
+ mDecoderAudio->start();
+
+ AVStream* stream_video = mContext->streams[mVideoStreamId];
+ mDecoderVideo = new DecoderVideo(stream_video, new VideoCallback());
+ mDecoderVideo->start();
+
+ LOGI("playing");
+
+ while (!mEnding)
+ {
+ if (mDecoderVideo->packets() > FFMPEG_PLAYER_MAX_QUEUE_SIZE &&
+ mDecoderAudio->packets() > FFMPEG_PLAYER_MAX_QUEUE_SIZE) {
+ usleep(200);
+ continue;
+ }
+
+ if(mPaused) {
+ usleep(200);
+ continue;
+ }
+
+ sPlayer->mLock.lock();
+
+ if(av_read_frame(mContext, &pPacket) < 0) {
+ mEnding = true;
+ continue;
+ }
+
+ // Is this a packet from the video stream?
+ if (pPacket.stream_index == mVideoStreamId) {
+ mDecoderVideo->enqueue(&pPacket);
+ }
+ else if (pPacket.stream_index == mAudioStreamId) {
+ mDecoderAudio->enqueue(&pPacket);
+ }
+ else {
+ // Free the packet that was allocated by av_read_frame
+ av_free_packet(&pPacket);
+ }
+
+ sPlayer->mLock.unlock();
+ }
+
+ //waits on end of video thread
+ LOGI("waiting on video thread");
+ int ret = -1;
+ if((ret = mDecoderVideo->join()) != 0) {
+ LOGE("Couldn't cancel video thread: %i", ret);
+ }
+
+ LOGI("waiting on audio thread");
+ if((ret = mDecoderAudio->join()) != 0) {
+ LOGE("Couldn't cancel audio thread: %i", ret);
+ }
+
+ mCallback->onCompleted();
+}
+
+
MediaPlayer::MediaPlayer()
{
+ mDecodingLoop = NULL;
mListener = NULL;
mCookie = NULL;
mDuration = -1;
@@ -41,7 +171,7 @@ MediaPlayer::MediaPlayer()
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false;
- pthread_mutex_init(&mLock, NULL);
+ mAutoscale = true;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
sPlayer = this;
@@ -56,7 +186,7 @@ MediaPlayer::~MediaPlayer()
status_t MediaPlayer::prepareAudio()
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "prepareAudio");
+ LOGI("prepareAudio");
mAudioStreamIndex = -1;
for (int i = 0; i < mMovieFile->nb_streams; i++) {
if (mMovieFile->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
@@ -100,7 +230,7 @@ status_t MediaPlayer::prepareAudio()
status_t MediaPlayer::prepareVideo()
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "prepareVideo");
+ LOGI("prepareVideo");
// Find the first video stream
mVideoStreamIndex = -1;
for (int i = 0; i < mMovieFile->nb_streams; i++) {
@@ -131,11 +261,11 @@ status_t MediaPlayer::prepareVideo()
mVideoHeight = codec_ctx->height;
mDuration = mMovieFile->duration;
- mConvertCtx = sws_getContext(stream->codec->width,
- stream->codec->height,
+ mConvertCtx = sws_getContext(mVideoWidth,
+ mVideoHeight,
stream->codec->pix_fmt,
- stream->codec->width,
- stream->codec->height,
+ mVideoWidth,
+ mVideoHeight,
PIX_FMT_RGB565,
SWS_POINT,
NULL,
@@ -146,29 +276,39 @@ status_t MediaPlayer::prepareVideo()
return INVALID_OPERATION;
}
- void* pixels;
- if (Output::VideoDriver_getPixels(stream->codec->width,
- stream->codec->height,
- &pixels) != ANDROID_SURFACE_RESULT_SUCCESS) {
- return INVALID_OPERATION;
- }
-
- mFrame = avcodec_alloc_frame();
- if (mFrame == NULL) {
+ if(!createVideoSurface(mVideoWidth, mVideoHeight)) {
return INVALID_OPERATION;
}
- // Assign appropriate parts of buffer to image planes in pFrameRGB
- // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
- // of AVPicture
- avpicture_fill((AVPicture *) mFrame,
- (uint8_t *) pixels,
- PIX_FMT_RGB565,
- stream->codec->width,
- stream->codec->height);
return NO_ERROR;
}
+bool MediaPlayer::createVideoSurface(int width, int height)
+{
+ void* pixels;
+ if (Output::VideoDriver_getPixels(width, height, &pixels)
+ != ANDROID_SURFACE_RESULT_SUCCESS)
+ {
+ return false;
+ }
+
+ mFrame = avcodec_alloc_frame();
+ if (mFrame == NULL) {
+ return false;
+ }
+
+ // Assign appropriate parts of buffer to image planes in pFrameRGB
+ // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
+ // of AVPicture
+ avpicture_fill((AVPicture *) mFrame,
+ (uint8_t *) pixels,
+ PIX_FMT_RGB565,
+ width,
+ height);
+
+ return true;
+}
+
status_t MediaPlayer::prepare()
{
status_t ret;
@@ -188,14 +328,14 @@ status_t MediaPlayer::prepare()
status_t MediaPlayer::setListener(MediaPlayerListener* listener)
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "setListener");
+ LOGI("setListener");
mListener = listener;
return NO_ERROR;
}
status_t MediaPlayer::setDataSource(const char *url)
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "setDataSource(%s)", url);
+ LOGI("setDataSource(%s)", url);
status_t err = BAD_VALUE;
// Open video file
if(av_open_input_file(&mMovieFile, url, NULL, 0, NULL) != 0) {
@@ -210,40 +350,38 @@ status_t MediaPlayer::setDataSource(const char *url)
}
status_t MediaPlayer::suspend() {
- __android_log_print(ANDROID_LOG_INFO, TAG, "suspend");
-
- mCurrentState = MEDIA_PLAYER_STOPPED;
- if(mDecoderAudio != NULL) {
- mDecoderAudio->stop();
- }
- if(mDecoderVideo != NULL) {
- mDecoderVideo->stop();
- }
-
- if(pthread_join(mPlayerThread, NULL) != 0) {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "Couldn't cancel player thread");
- }
+ LOGI("suspend");
+
+ mCurrentState = MEDIA_PLAYER_STOPPED;
- // Close the codec
- free(mDecoderAudio);
- free(mDecoderVideo);
+ delete mDecodingLoop;
+ mDecodingLoop = NULL;
- // Close the video file
- av_close_input_file(mMovieFile);
-
- //close OS drivers
- Output::AudioDriver_unregister();
- Output::VideoDriver_unregister();
+ // Close the video file
+ av_close_input_file(mMovieFile);
- __android_log_print(ANDROID_LOG_ERROR, TAG, "suspended");
+ //close OS drivers
+ Output::AudioDriver_unregister();
+ Output::VideoDriver_unregister();
+ LOGE("suspended");
return NO_ERROR;
}
+void MediaPlayer::onCompleted()
+{
+ if(mCurrentState == MEDIA_PLAYER_STATE_ERROR) {
+ LOGI("playing err");
+ }
+
+ mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
+ LOGI("end of playing");
+}
+
status_t MediaPlayer::resume() {
- //pthread_mutex_lock(&mLock);
- mCurrentState = MEDIA_PLAYER_STARTED;
- //pthread_mutex_unlock(&mLock);
+ Mutex::AutoLock _l(&mLock);
+
+ mCurrentState = MEDIA_PLAYER_STARTED;
return NO_ERROR;
}
@@ -265,166 +403,71 @@ bool MediaPlayer::shouldCancel(PacketQueue* queue)
&& queue->size() == 0));
}
-void MediaPlayer::decode(AVFrame* frame, double pts)
+status_t MediaPlayer::start()
{
- if(FPS_DEBUGGING) {
- timeval pTime;
- static int frames = 0;
- static double t1 = -1;
- static double t2 = -1;
-
- gettimeofday(&pTime, NULL);
- t2 = pTime.tv_sec + (pTime.tv_usec / 1000000.0);
- if (t1 == -1 || t2 > t1 + 1) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "Video fps:%i", frames);
- //sPlayer->notify(MEDIA_INFO_FRAMERATE_VIDEO, frames, -1);
- t1 = t2;
- frames = 0;
- }
- frames++;
- }
+ Mutex::AutoLock _l(&mLock);
+
+ if (mDecodingLoop != NULL || mCurrentState != MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
- // Convert the image from its native format to RGB
- sws_scale(sPlayer->mConvertCtx,
- frame->data,
- frame->linesize,
- 0,
- sPlayer->mVideoHeight,
- sPlayer->mFrame->data,
- sPlayer->mFrame->linesize);
+ mDecodingLoop = new DecodeLoop(mMovieFile, mAudioStreamIndex, mVideoStreamIndex, this);
+ mDecodingLoop->start();
- Output::VideoDriver_updateSurface();
+ return NO_ERROR;
}
-void MediaPlayer::decode(int16_t* buffer, int buffer_size)
+status_t MediaPlayer::stop()
{
- if(FPS_DEBUGGING) {
- timeval pTime;
- static int frames = 0;
- static double t1 = -1;
- static double t2 = -1;
-
- gettimeofday(&pTime, NULL);
- t2 = pTime.tv_sec + (pTime.tv_usec / 1000000.0);
- if (t1 == -1 || t2 > t1 + 1) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "Audio fps:%i", frames);
- //sPlayer->notify(MEDIA_INFO_FRAMERATE_AUDIO, frames, -1);
- t1 = t2;
- frames = 0;
- }
- frames++;
- }
+ Mutex::AutoLock _l(&mLock);
- if(Output::AudioDriver_write(buffer, buffer_size) <= 0) {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "Couldn't write samples to audio track");
- }
+ mCurrentState = MEDIA_PLAYER_STOPPED;
+ return NO_ERROR;
}
-void MediaPlayer::decodeMovie(void* ptr)
+status_t MediaPlayer::pause()
{
- AVPacket pPacket;
-
- AVStream* stream_audio = mMovieFile->streams[mAudioStreamIndex];
- mDecoderAudio = new DecoderAudio(stream_audio);
- mDecoderAudio->onDecode = decode;
- mDecoderAudio->startAsync();
-
- AVStream* stream_video = mMovieFile->streams[mVideoStreamIndex];
- mDecoderVideo = new DecoderVideo(stream_video);
- mDecoderVideo->onDecode = decode;
- mDecoderVideo->startAsync();
-
- mCurrentState = MEDIA_PLAYER_STARTED;
- __android_log_print(ANDROID_LOG_INFO, TAG, "playing %ix%i", mVideoWidth, mVideoHeight);
- while (mCurrentState != MEDIA_PLAYER_DECODED && mCurrentState != MEDIA_PLAYER_STOPPED &&
- mCurrentState != MEDIA_PLAYER_STATE_ERROR)
- {
- if (mDecoderVideo->packets() > FFMPEG_PLAYER_MAX_QUEUE_SIZE &&
- mDecoderAudio->packets() > FFMPEG_PLAYER_MAX_QUEUE_SIZE) {
- usleep(200);
- continue;
- }
-
- if(av_read_frame(mMovieFile, &pPacket) < 0) {
- mCurrentState = MEDIA_PLAYER_DECODED;
- continue;
- }
-
- // Is this a packet from the video stream?
- if (pPacket.stream_index == mVideoStreamIndex) {
- mDecoderVideo->enqueue(&pPacket);
- }
- else if (pPacket.stream_index == mAudioStreamIndex) {
- mDecoderAudio->enqueue(&pPacket);
- }
- else {
- // Free the packet that was allocated by av_read_frame
- av_free_packet(&pPacket);
- }
- }
-
- //waits on end of video thread
- __android_log_print(ANDROID_LOG_ERROR, TAG, "waiting on video thread");
- int ret = -1;
- if((ret = mDecoderVideo->wait()) != 0) {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "Couldn't cancel video thread: %i", ret);
- }
-
- __android_log_print(ANDROID_LOG_ERROR, TAG, "waiting on audio thread");
- if((ret = mDecoderAudio->wait()) != 0) {
- __android_log_print(ANDROID_LOG_ERROR, TAG, "Couldn't cancel audio thread: %i", ret);
- }
-
- if(mCurrentState == MEDIA_PLAYER_STATE_ERROR) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "playing err");
- }
- mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
- __android_log_print(ANDROID_LOG_INFO, TAG, "end of playing");
-}
+ Mutex::AutoLock _l(&mLock);
-void* MediaPlayer::startPlayer(void* ptr)
-{
- __android_log_print(ANDROID_LOG_INFO, TAG, "starting main player thread");
- sPlayer->decodeMovie(ptr);
+ mDecodingLoop->pause();
+
+ return NO_ERROR;
}
-status_t MediaPlayer::start()
+bool MediaPlayer::isPlaying()
{
- if (mCurrentState != MEDIA_PLAYER_PREPARED) {
- return INVALID_OPERATION;
- }
- pthread_create(&mPlayerThread, NULL, startPlayer, NULL);
- return NO_ERROR;
+ Mutex::AutoLock _l(&mLock);
+ return mDecodingLoop->isPlaying();
}
-status_t MediaPlayer::stop()
+status_t MediaPlayer::setAutoscale(bool value)
{
- //pthread_mutex_lock(&mLock);
- mCurrentState = MEDIA_PLAYER_STOPPED;
- //pthread_mutex_unlock(&mLock);
+ if (mCurrentState < MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
+
+ mAutoscale = value;
+
return NO_ERROR;
}
-status_t MediaPlayer::pause()
+status_t MediaPlayer::setResolution(int width, int height)
{
- //pthread_mutex_lock(&mLock);
- mCurrentState = MEDIA_PLAYER_PAUSED;
- //pthread_mutex_unlock(&mLock);
- return NO_ERROR;
-}
+ if (mCurrentState < MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
-bool MediaPlayer::isPlaying()
-{
- return mCurrentState == MEDIA_PLAYER_STARTED ||
- mCurrentState == MEDIA_PLAYER_DECODED;
+ return NO_ERROR;
}
status_t MediaPlayer::getVideoWidth(int *w)
{
- if (mCurrentState < MEDIA_PLAYER_PREPARED) {
- return INVALID_OPERATION;
- }
- *w = mVideoWidth;
+ Mutex::AutoLock _l(&mLock);
+
+ if (mCurrentState < MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
+ *w = mVideoWidth;
return NO_ERROR;
}
@@ -439,36 +482,42 @@ status_t MediaPlayer::getVideoHeight(int *h)
status_t MediaPlayer::getCurrentPosition(int *msec)
{
- if (mCurrentState < MEDIA_PLAYER_PREPARED) {
- return INVALID_OPERATION;
- }
- *msec = 0/*av_gettime()*/;
- //__android_log_print(ANDROID_LOG_INFO, TAG, "position %i", *msec);
- return NO_ERROR;
+ Mutex::AutoLock _l(&mLock);
+
+ if (mCurrentState < MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
+ *msec = 0/*av_gettime()*/;
+ //LOGI("position %i", *msec);
+ return NO_ERROR;
}
status_t MediaPlayer::getDuration(int *msec)
{
- if (mCurrentState < MEDIA_PLAYER_PREPARED) {
- return INVALID_OPERATION;
- }
- *msec = mDuration / 1000;
+ Mutex::AutoLock _l(&mLock);
+
+ if (mCurrentState < MEDIA_PLAYER_PREPARED) {
+ return INVALID_OPERATION;
+ }
+ *msec = mDuration / 1000;
return NO_ERROR;
}
status_t MediaPlayer::seekTo(int msec)
{
+ Mutex::AutoLock _l(&mLock);
return INVALID_OPERATION;
}
status_t MediaPlayer::reset()
{
+ Mutex::AutoLock _l(&mLock);
return INVALID_OPERATION;
}
status_t MediaPlayer::setAudioStreamType(int type)
{
- return NO_ERROR;
+ return NO_ERROR;
}
void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl) {
@@ -478,7 +527,7 @@ void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl
* Something went really wrong and we will crash now.
*/
case AV_LOG_PANIC:
- __android_log_print(ANDROID_LOG_ERROR, TAG, "AV_LOG_PANIC: %s", fmt);
+ LOGE("AV_LOG_PANIC: %s", fmt);
//sPlayer->mCurrentState = MEDIA_PLAYER_STATE_ERROR;
break;
@@ -488,7 +537,7 @@ void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl
* on headers or an illegal combination of parameters is used.
*/
case AV_LOG_FATAL:
- __android_log_print(ANDROID_LOG_ERROR, TAG, "AV_LOG_FATAL: %s", fmt);
+ LOGE("AV_LOG_FATAL: %s", fmt);
//sPlayer->mCurrentState = MEDIA_PLAYER_STATE_ERROR;
break;
@@ -497,7 +546,7 @@ void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl
* However, not all future data is affected.
*/
case AV_LOG_ERROR:
- __android_log_print(ANDROID_LOG_ERROR, TAG, "AV_LOG_ERROR: %s", fmt);
+ LOGE("AV_LOG_ERROR: %s", fmt);
//sPlayer->mCurrentState = MEDIA_PLAYER_STATE_ERROR;
break;
@@ -506,15 +555,15 @@ void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl
* lead to problems. An example would be the use of '-vstrict -2'.
*/
case AV_LOG_WARNING:
- __android_log_print(ANDROID_LOG_ERROR, TAG, "AV_LOG_WARNING: %s", fmt);
+ LOGE("AV_LOG_WARNING: %s", fmt);
break;
case AV_LOG_INFO:
- __android_log_print(ANDROID_LOG_INFO, TAG, "%s", fmt);
+ LOGI("%s", fmt);
break;
case AV_LOG_DEBUG:
- __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s", fmt);
+ LOGI("%s", fmt);
break;
}
@@ -522,13 +571,13 @@ void MediaPlayer::ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl
void MediaPlayer::notify(int msg, int ext1, int ext2)
{
- //__android_log_print(ANDROID_LOG_INFO, TAG, "message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
+ //LOGI("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
bool locked = false;
if ((mListener != 0) && send) {
- //__android_log_print(ANDROID_LOG_INFO, TAG, "callback application");
+ //LOGI("callback application");
mListener->notify(msg, ext1, ext2);
- //__android_log_print(ANDROID_LOG_INFO, TAG, "back from callback");
+ //LOGI("back from callback");
}
}
diff --git a/jni/libmediaplayer/mediaplayer.h b/jni/libmediaplayer/mediaplayer.h
index 0da9881..6068b69 100644
--- a/jni/libmediaplayer/mediaplayer.h
+++ b/jni/libmediaplayer/mediaplayer.h
@@ -1,8 +1,6 @@
#ifndef FFMPEG_MEDIAPLAYER_H
#define FFMPEG_MEDIAPLAYER_H
-#include
-
#include
#include
@@ -106,6 +104,38 @@ enum media_player_states {
MEDIA_PLAYER_PLAYBACK_COMPLETE = 1 << 8
};
+class DecodeLoopCallback
+{
+private:
+ virtual void onCompleted();
+
+ friend class DecodeLoop;
+};
+
+class DecodeLoop : public Thread
+{
+private:
+ DecoderAudio* mDecoderAudio;
+ int mAudioStreamId;
+ DecoderVideo* mDecoderVideo;
+ int mVideoStreamId;
+ AVFormatContext* mContext;
+
+ DecodeLoopCallback* mCallback;
+ bool mEnding;
+ bool mPaused;
+
+public:
+ DecodeLoop(AVFormatContext* context, int audioStreamId, int videoStreamId, DecodeLoopCallback* callback);
+ ~DecodeLoop();
+
+ virtual void run();
+ void suspend();
+ void resume() { mPaused = false; };
+ void pause() { mPaused = true; };
+ bool isPlaying() { return !mPaused; };
+};
+
// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayerListener
@@ -114,76 +144,79 @@ class MediaPlayerListener
virtual void notify(int msg, int ext1, int ext2) = 0;
};
-class MediaPlayer
+class MediaPlayer : private DecodeLoopCallback
{
public:
MediaPlayer();
~MediaPlayer();
- status_t setDataSource(const char *url);
- status_t setVideoSurface(JNIEnv* env, jobject jsurface);
- status_t setListener(MediaPlayerListener *listener);
- status_t start();
- status_t stop();
- status_t pause();
- bool isPlaying();
- status_t getVideoWidth(int *w);
- status_t getVideoHeight(int *h);
- status_t seekTo(int msec);
- status_t getCurrentPosition(int *msec);
- status_t getDuration(int *msec);
- status_t reset();
- status_t setAudioStreamType(int type);
- status_t prepare();
- void notify(int msg, int ext1, int ext2);
+ status_t setAutoscale(bool value);
+ status_t setResolution(int width, int height);
+ status_t setDataSource(const char *url);
+ status_t setVideoSurface(JNIEnv* env, jobject jsurface);
+ status_t setListener(MediaPlayerListener *listener);
+ status_t start();
+ status_t stop();
+ status_t pause();
+ bool isPlaying();
+ status_t getVideoWidth(int *w);
+ status_t getVideoHeight(int *h);
+ status_t seekTo(int msec);
+ status_t getCurrentPosition(int *msec);
+ status_t getDuration(int *msec);
+ status_t reset();
+ status_t setAudioStreamType(int type);
+ status_t prepare();
+ void notify(int msg, int ext1, int ext2);
// static sp decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
// static sp decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
// static int snoop(short *data, int len, int kind);
// status_t invoke(const Parcel& request, Parcel *reply);
// status_t setMetadataFilter(const Parcel& filter);
// status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
- status_t suspend();
- status_t resume();
+ status_t suspend();
+ status_t resume();
private:
- status_t prepareAudio();
- status_t prepareVideo();
- bool shouldCancel(PacketQueue* queue);
- static void ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl);
- static void* startPlayer(void* ptr);
+ bool createVideoSurface(int width, int height);
+ status_t prepareAudio();
+ status_t prepareVideo();
+ bool shouldCancel(PacketQueue* queue);
+ static void ffmpegNotify(void* ptr, int level, const char* fmt, va_list vl);
+
+ double mTime;
- static void decode(AVFrame* frame, double pts);
- static void decode(int16_t* buffer, int buffer_size);
+ Mutex mLock;
- void decodeMovie(void* ptr);
-
- double mTime;
- pthread_mutex_t mLock;
- pthread_t mPlayerThread;
- PacketQueue* mVideoQueue;
- //Mutex mNotifyLock;
- //Condition mSignal;
- MediaPlayerListener* mListener;
- AVFormatContext* mMovieFile;
- int mAudioStreamIndex;
- int mVideoStreamIndex;
- DecoderAudio* mDecoderAudio;
- DecoderVideo* mDecoderVideo;
- AVFrame* mFrame;
- struct SwsContext* mConvertCtx;
-
- void* mCookie;
- media_player_states mCurrentState;
- int mDuration;
- int mCurrentPosition;
- int mSeekPosition;
- bool mPrepareSync;
- status_t mPrepareStatus;
- int mStreamType;
- bool mLoop;
- float mLeftVolume;
- float mRightVolume;
- int mVideoWidth;
- int mVideoHeight;
+ MediaPlayerListener* mListener;
+ AVFormatContext* mMovieFile;
+ int mAudioStreamIndex;
+ int mVideoStreamIndex;
+
+ DecodeLoop* mDecodingLoop;
+
+ void* mCookie;
+ media_player_states mCurrentState;
+ int mDuration;
+ int mCurrentPosition;
+ int mSeekPosition;
+ bool mPrepareSync;
+ status_t mPrepareStatus;
+ int mStreamType;
+ bool mLoop;
+ float mLeftVolume;
+ float mRightVolume;
+
+public:
+ bool mAutoscale;
+ struct SwsContext* mConvertCtx;
+ AVFrame* mFrame;
+ int mVideoWidth;
+ int mVideoHeight;
+
+protected:
+ virtual void onCompleted();
+
+ friend class DecodeLoop;
};
#endif // FFMPEG_MEDIAPLAYER_H
diff --git a/jni/libmediaplayer/output.cpp b/jni/libmediaplayer/output.cpp
index 3ec32c7..945996e 100644
--- a/jni/libmediaplayer/output.cpp
+++ b/jni/libmediaplayer/output.cpp
@@ -68,7 +68,7 @@ int Output::VideoDriver_getPixels(int width, int height, void** pixels)
return AndroidSurface_getPixels(width, height, pixels);
}
-int Output::VideoDriver_updateSurface()
+int Output::VideoDriver_updateSurface(bool autoscale)
{
- return AndroidSurface_updateSurface();
-}
\ No newline at end of file
+ return AndroidSurface_updateSurface(autoscale);
+}
diff --git a/jni/libmediaplayer/output.h b/jni/libmediaplayer/output.h
index 705b981..210a3e7 100644
--- a/jni/libmediaplayer/output.h
+++ b/jni/libmediaplayer/output.h
@@ -23,7 +23,7 @@ class Output
static int VideoDriver_register(JNIEnv* env, jobject jsurface);
static int VideoDriver_getPixels(int width, int height, void** pixels);
- static int VideoDriver_updateSurface();
+ static int VideoDriver_updateSurface(bool autoscale = true);
static int VideoDriver_unregister();
};
diff --git a/jni/libmediaplayer/thread.cpp b/jni/libmediaplayer/thread.cpp
index 4151bab..ff9825a 100644
--- a/jni/libmediaplayer/thread.cpp
+++ b/jni/libmediaplayer/thread.cpp
@@ -1,65 +1,81 @@
-#include
+/*
+ * Copyright (c) 2011 Petr Havlena havlenapetr@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include
+
#include "thread.h"
-#define TAG "FFMpegThread"
+#define THREAD_NAME_MAX 50
-Thread::Thread()
-{
- pthread_mutex_init(&mLock, NULL);
- pthread_cond_init(&mCondition, NULL);
-}
+#define LOG_TAG "Thread"
-Thread::~Thread()
-{
-}
+static int sThreadCounter = 0;
-void Thread::start()
+Thread::Thread() :
+ mName(NULL)
{
- handleRun(NULL);
+ init();
}
-void Thread::startAsync()
+Thread::Thread(const char* name) :
+ mName(name)
{
- pthread_create(&mThread, NULL, startThread, this);
+ init();
}
-int Thread::wait()
+Thread::~Thread()
{
- if(!mRunning)
- {
- return 0;
- }
- return pthread_join(mThread, NULL);
}
-void Thread::stop()
+void Thread::init()
{
+ pthread_mutex_init(&mLock, NULL);
+ pthread_cond_init(&mCondition, NULL);
+ sThreadCounter++;
}
-void* Thread::startThread(void* ptr)
+void Thread::start()
{
- __android_log_print(ANDROID_LOG_INFO, TAG, "starting thread");
- Thread* thread = (Thread *) ptr;
- thread->mRunning = true;
- thread->handleRun(ptr);
- thread->mRunning = false;
- __android_log_print(ANDROID_LOG_INFO, TAG, "thread ended");
+ pthread_create(&mThread, NULL, handleThreadStart, this);
}
-void Thread::waitOnNotify()
+int Thread::join()
{
- pthread_mutex_lock(&mLock);
- pthread_cond_wait(&mCondition, &mLock);
- pthread_mutex_unlock(&mLock);
+ if(!mRunning)
+ {
+ return 0;
+ }
+ return pthread_join(mThread, NULL);
}
-void Thread::notify()
+void* Thread::handleThreadStart(void* ptr)
{
- pthread_mutex_lock(&mLock);
- pthread_cond_signal(&mCondition);
- pthread_mutex_unlock(&mLock);
+ Thread* thread = (Thread *) ptr;
+
+ thread->mRunning = true;
+
+ thread->run();
+
+ thread->mRunning = false;
+
+ return 0;
}
-void Thread::handleRun(void* ptr)
+void Thread::run()
{
}
diff --git a/jni/libmediaplayer/thread.h b/jni/libmediaplayer/thread.h
index b06d5f1..1893fc6 100644
--- a/jni/libmediaplayer/thread.h
+++ b/jni/libmediaplayer/thread.h
@@ -1,33 +1,143 @@
-#ifndef FFMPEG_THREAD_H
-#define FFMPEG_THREAD_H
+/*
+ * Copyright (c) 2011 Petr Havlena havlenapetr@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Copyright (C) 2007 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 _ENGINE_Thread_H_
+#define _ENGINE_Thread_H_
#include
+#include
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class Mutex {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ Mutex();
+ Mutex(const char* name);
+ Mutex(int type, const char* name = NULL);
+ ~Mutex();
+
+ // lock or unlock the mutex
+ int lock();
+ void unlock();
+
+ // lock if possible; returns 0 on success, error otherwise
+ int tryLock();
+
+ // Manages the mutex automatically. It'll be locked when Autolock is
+ // constructed and released when Autolock goes out of scope.
+ class AutoLock {
+ public:
+ inline AutoLock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
+ inline AutoLock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~AutoLock() { mLock.unlock(); }
+ private:
+ Mutex& mLock;
+ };
+
+private:
+
+ // A mutex cannot be copied
+ Mutex(const Mutex&);
+ Mutex& operator = (const Mutex&);
+
+ pthread_mutex_t mMutex;
+};
+
+inline Mutex::Mutex() {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(const char* name) {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(int type, const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
+inline Mutex::~Mutex() {
+ pthread_mutex_destroy(&mMutex);
+}
+inline int Mutex::lock() {
+ return -pthread_mutex_lock(&mMutex);
+}
+inline void Mutex::unlock() {
+ pthread_mutex_unlock(&mMutex);
+}
+inline int Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+}
class Thread
{
public:
- Thread();
- ~Thread();
-
- void start();
- void startAsync();
- int wait();
+ Thread();
+ Thread(const char* name);
+ ~Thread();
- void waitOnNotify();
- void notify();
- virtual void stop();
+ void start();
+ int join();
+
+ bool isRunning() { return mRunning; };
protected:
- bool mRunning;
+ bool mRunning;
+
+ virtual void run();
- virtual void handleRun(void* ptr);
-
private:
- pthread_t mThread;
- pthread_mutex_t mLock;
- pthread_cond_t mCondition;
+ pthread_t mThread;
+ pthread_mutex_t mLock;
+ pthread_cond_t mCondition;
+ const char* mName;
- static void* startThread(void* ptr);
+ void init();
+ static void* handleThreadStart(void* ptr);
};
-#endif //FFMPEG_DECODER_H
+#endif // Thread_H
diff --git a/jni/prebuilt/libjnivideo.so b/jni/prebuilt/libjnivideo.so
index 4d29d64..48750d7 100755
Binary files a/jni/prebuilt/libjnivideo.so and b/jni/prebuilt/libjnivideo.so differ
diff --git a/src/com/media/ffmpeg/FFMpegPlayer.java b/src/com/media/ffmpeg/FFMpegPlayer.java
index b9492ff..798f6e5 100644
--- a/src/com/media/ffmpeg/FFMpegPlayer.java
+++ b/src/com/media/ffmpeg/FFMpegPlayer.java
@@ -166,6 +166,14 @@ public void pause() throws IllegalStateException {
_pause();
}
+ public void setResolution(int width, int height) throws IllegalStateException {
+ _setResolution(width, height);
+ }
+
+ public void setAutoscale(boolean value)throws IllegalStateException {
+ _setAutoscale(value);
+ }
+
/**
* Set the low-level power management behavior for this MediaPlayer. This
* can be used when the MediaPlayer is not playing through a SurfaceHolder
@@ -271,7 +279,11 @@ private void updateSurfaceScreenOn() {
private native void _stop() throws IllegalStateException;
private native void _pause() throws IllegalStateException;
-
+
+ private native void _setResolution(int width, int height) throws IllegalStateException;
+
+ private native void _setAutoscale(boolean value) throws IllegalStateException;
+
/**
* Returns the width of the video.
*
diff --git a/src/com/media/ffmpeg/android/FFMpegMovieViewAndroid.java b/src/com/media/ffmpeg/android/FFMpegMovieViewAndroid.java
index 831235e..9fcf147 100644
--- a/src/com/media/ffmpeg/android/FFMpegMovieViewAndroid.java
+++ b/src/com/media/ffmpeg/android/FFMpegMovieViewAndroid.java
@@ -53,7 +53,11 @@ private void attachMediaController() {
public void setVideoPath(String filePath) throws IllegalArgumentException, IllegalStateException, IOException {
mPlayer.setDataSource(filePath);
}
-
+
+ public void setAutoscale(boolean value) {
+ mPlayer.setAutoscale(value);
+ }
+
/**
* initzialize player
*/
@@ -68,7 +72,8 @@ private void openVideo(SurfaceHolder surfHolder) {
}
}
- private void startVideo() {
+ private void startVideo(int format, int w, int h) {
+ //Log.d(TAG, "screen format: " + format + ", res: " + w + "x" + h);
attachMediaController();
mPlayer.start();
}
@@ -90,7 +95,7 @@ public boolean onTouchEvent(android.view.MotionEvent event) {
SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- startVideo();
+ startVideo(format, w, h);
}
public void surfaceCreated(SurfaceHolder holder) {
@@ -105,7 +110,7 @@ public void surfaceDestroyed(SurfaceHolder holder) {
}
};
- MediaPlayerControl mMediaPlayerControl = new MediaPlayerControl() {
+ private MediaPlayerControl mMediaPlayerControl = new MediaPlayerControl() {
public void start() {
mPlayer.resume();
diff --git a/src/cz/havlena/ffmpeg/ui/FFMpegPlayerActivity.java b/src/cz/havlena/ffmpeg/ui/FFMpegPlayerActivity.java
index b740021..4b90a6f 100644
--- a/src/cz/havlena/ffmpeg/ui/FFMpegPlayerActivity.java
+++ b/src/cz/havlena/ffmpeg/ui/FFMpegPlayerActivity.java
@@ -10,14 +10,19 @@
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
public class FFMpegPlayerActivity extends Activity {
private static final String TAG = "FFMpegPlayerActivity";
//private static final String LICENSE = "This software uses libraries from the FFmpeg project under the LGPLv2.1";
private FFMpegMovieViewAndroid mMovieView;
+ private boolean mAutoscale;
//private WakeLock mWakeLock;
+ private static final int MENU_AUTOSCALE = 1;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -53,5 +58,25 @@ protected void onCreate(Bundle savedInstanceState) {
finish();
}
}
+
+ mAutoscale = true;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_AUTOSCALE, 1, "Autoscale");
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch(item.getItemId())
+ {
+ case MENU_AUTOSCALE:
+ mAutoscale = !mAutoscale;
+ mMovieView.setAutoscale(mAutoscale);
+ return true;
+ }
+ return false;
}
}