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; } }