基于Lae的ffmpeg播放器

来源:互联网 发布:office mac破解版下载 编辑:程序博客网 时间:2024/05/21 17:07

播放器其实就是播放图片,原理很简单。

这里面要同步声音、快进、快退、暂停等,fplay.c代码量很大,4000多行代码,想弄明白所有东西还是蛮费时间的。

下面我们转换个思路,只修改ffplay.c界面和播放声音的东西就OK.

 

剩下就是界面,界面用Lae工具很简单,想怎样就有怎样!!!


该播放器依赖于Lae,ffmpeg.

ffmpeg视频播放器源代码

https://github.com/ouloba/MyVideo.git


Lae使用教程 

极速模仿Yahoo News视频教程

http://www.tudou.com/programs/view/AaqZ81jIt-k


主要步骤

1、拷贝3个文件

ffplay.c,把名字修改成ffplay.cpp

cmduntil.c,把名字修改成cmduntil.cpp

把里面头文件加extern "C"包起来

extern "C" {
#include "libavutil/avstring.h"
#include "libavutil/eval.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/avassert.h"
#include "libavutil/time.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/avutil.h"
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"


#if CONFIG_AVFILTER
# include "libavfilter/avfilter.h"
# include "libavfilter/buffersink.h"
# include "libavfilter/buffersrc.h"
#endif


//把SDL屏蔽
//#include <SDL.h>
//#include <SDL_thread.h>
#include "cmdutils.h"
#include "al.h"
#include "alc.h"
}


#define SDL_MIX_MAXVOLUME 50


添加Lae相关头文件

#include "LXZLock.h"
#include "LXZWindowAPI.h"


//代码太多,无需考虑里面流程,只要把ffplay.c里的界面相关给换成Lae就行



extern "C"{
static void SDL_CondSignal(Linux_Win_Event* event){
if (event){
event->SetEvent();
}
}


static Linux_Win_Event* SDL_CreateCond(const char* name=""){
Linux_Win_Event* event = new Linux_Win_Event();
event->Initialize(name);
return event;
}


static void SDL_DestroyCond(Linux_Win_Event* event){
if (event){
delete event;
}
}


static const char* SDL_GetError(){
return "alloc fail";
}


static void SDL_DestroyMutex(Linux_Win_Lock* event){
if (event){
delete event;
}
}


static Linux_Win_Lock* SDL_CreateMutex(){
Linux_Win_Lock* event = new Linux_Win_Lock();
return event;
}




static void SDL_LockMutex(Linux_Win_Lock* mutex){
if (mutex){
mutex->Linux_Win_Locked();
}
}


static void SDL_UnlockMutex(Linux_Win_Lock* mutex){
if (mutex){
mutex->Linux_Win_UnLocked();
}
}


static void SDL_CondWait1(Linux_Win_Event* cond, uint32_t t=-1){
if (cond){
cond->Wait(t);
}
}


static void SDL_CondWait2(Linux_Win_Event* cond, Linux_Win_Lock* mutex, uint32_t t = -1){
SDL_UnlockMutex(mutex);
SDL_CondWait1(cond,t);
SDL_LockMutex(mutex);
}


static void SDL_CondWaitTimeout(Linux_Win_Event* cond, Linux_Win_Lock* mutex, uint32_t t){
SDL_UnlockMutex(mutex);
SDL_CondWait1(cond, t);
SDL_LockMutex(mutex);
}


struct _WrapThread {
void* data;
int (*fn)(void*);


};


static void fnThread(void* data){
_WrapThread* pt = (_WrapThread*)data;
pt->fn(pt->data);
}


static Linux_Win_Event* SDL_CreateThread(int(*fn)(void*), void* data){
_WrapThread* pt = new _WrapThread;
pt->data = data;
pt->fn = fn;
Linux_Win_Event* event = SDL_CreateCond();
LXZAPI_NewThread(fnThread, pt);
return event;
}


static void SDL_WaitThread(Linux_Win_Event* cond, int* ret){
if (cond){
cond->Wait(-1);
if (ret){
*ret = 0;
}
}
}
}


typedef Linux_Win_Lock SDL_mutex;


用open al 播放声音


添加初始和退出接口
static ALCdevice *device = NULL;
void InitOPENAL()
{
//
int i4Ret = 0;
ALenum error;
ALCcontext *context = NULL;


device = alcOpenDevice(NULL);
//assert(NULL != device);
if (NULL != device)
{
error = alcGetError(device);
context = alcCreateContext(device, 0);
// assert(NULL != context);
if (NULL != context)
{
alcMakeContextCurrent(context);
}
else {
error = alGetError();
printf("Error Create Context! %x\n", error);
}
}
else
{
error = alGetError();
printf("Error Open Device! %x\n", error);
}


}


static void ExitOPENAL()
{
ALCcontext *context = alcGetCurrentContext();
device = alcGetContextsDevice(context);
alcDestroyContext(context);
alcCloseDevice(device);
device = NULL;


//
/* if (!alutExit ())
{
ALenum  error = alGetError();
printf("Error Create Context! %x\n", error);
assert(false);
}*/
}


播放流接口

这是一个标准流播放流程,只要添加sdl_audio_callback读取pcm数接口就ok


static void play_sound(void* opaque){
VideoState* is = (VideoState*)opaque;
Sleep(100);

alGenBuffers(NUMBUFFERS, uiBuffers);


uint8_t* decoded_buf = new uint8_t[MAX_AUDIO_FRAME_SIZE];
uint32_t decoded_pos = 0;


alGenSources(1, &source);
ALenum err = alGetError();
if (source == (ALuint)-1 && err != AL_NO_ERROR){
ALenum err = alGetError();
return;
}


alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(source, AL_ROLLOFF_FACTOR, 0);
alSourcei(source, AL_BUFFER, 0);


int count = 0;
/* Fill and queue the buffers */
for (int j = 0; j < NUMBUFFERS; j++)
{
ALint size, numchans, numbits;


/* Make sure we get some data to give to the buffer */
count = sdl_audio_callback(current_is, decoded_buf, MAX_AUDIO_FRAME_SIZE);//getAVAudioData(decoded_buf, MAX_AUDIO_FRAME_SIZE, 100000);
if (count <= 0)
break;


/* Buffer the data with OpenAL and queue the buffer onto the
* source */
alBufferData(uiBuffers[j], format, decoded_buf, count, dwSampleRate);
alSourceQueueBuffers(source, 1, &uiBuffers[j]);


/* For each successful buffer queued, increment the filetime */
alGetBufferi(uiBuffers[j], AL_SIZE, &size);
alGetBufferi(uiBuffers[j], AL_CHANNELS, &numchans);
alGetBufferi(uiBuffers[j], AL_BITS, &numbits);
//filetime += size / numchans * 8 / numbits;
}


alSourcePlay(source);
if (alGetError() != AL_NO_ERROR){
return;
}


static LXZuint32 updateTime = 0;
while (!do_quit&&!is->abort_request){


/* Check if any buffers on the source are finished playing */
ALint processed = 0;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
if (processed == 0)
{
/* All buffers are full. Check if the source is still playing.
* If not, restart it, otherwise, print the time and rest */
ALint state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if (alGetError() != AL_NO_ERROR)
{
printf_msg("\nError checking source state...\n");
break;
}
if (state != AL_PLAYING)
{
ALint iQueuedBuffers = 0;
alGetSourcei(source, AL_BUFFERS_QUEUED, &iQueuedBuffers);
if (iQueuedBuffers)
{
alSourcePlay(source);
}
else
{
// Finished playing
break;
}
}
else
{
/* int64_t curtime = 0;
if (basetime >= 0)
{
ALint offset = 0;
alGetSourcei(source, AL_SAMPLE_OFFSET, &offset);
curtime = basetime + offset;
}
fprintf(stderr, "\rTime: %ld:%05.02f", (long)(curtime / dwSampleRate / 60),
(float)(curtime % (dwSampleRate * 60)) / (float)dwSampleRate);*/
Sleep(1);
}
continue;
}


for (int t = 0; t<processed; t++){
/* Read the next chunk of data and refill the oldest buffer */
count = sdl_audio_callback(current_is, decoded_buf, MAX_AUDIO_FRAME_SIZE);//getAVAudioData(decoded_buf, MAX_AUDIO_FRAME_SIZE, 1000);
if (count > 0)
{
ALuint buf = 0;
alSourceUnqueueBuffers(source, 1, &buf);
if (buf != 0)
{
ALint size, numchans, numbits;


/* For each successfully unqueued buffer, increment the
* base time. */
alGetBufferi(buf, AL_SIZE, &size);
alGetBufferi(buf, AL_CHANNELS, &numchans);
alGetBufferi(buf, AL_BITS, &numbits);
// basetime += size / numchans * 8 / numbits;


alBufferData(buf, format, decoded_buf, count, dwSampleRate);
alSourceQueueBuffers(source, 1, &buf);


alGetBufferi(buf, AL_SIZE, &size);
alGetBufferi(buf, AL_CHANNELS, &numchans);
alGetBufferi(buf, AL_BITS, &numbits);
// filetime += size / numchans * 8 / numbits;
}
if (alGetError() != AL_NO_ERROR)
{
printf_msg(" !!! Error buffering data !!!\n");
break;
}
}
}


updateTime = LXZAPI_timeGetSystemTime() + 5;
}
}

用单独线程来播放声音
void ThreadPlayAudio(void* data){
play_sound(data);
}


/*解码声音流,并同步声音时钟*/

/* prepare a new audio buffer */
static int sdl_audio_callback(void *opaque, uint8_t *stream, int len)
{
VideoState *is = (VideoState*)opaque;
int audio_size = 0;
int len1 = 0;
int decoded = 0;
    audio_callback_time = av_gettime_relative();
while (len > 0){
audio_size = audio_decode_frame(is);
if (audio_size<0){
break;
}


if (len < audio_size){
len1 = len;
}
else{
len1 = audio_size;
}


memcpy(stream, is->audio_buf, len1);
len -= len1;
decoded += len1;
stream += len1;
}


if (!isnan(is->audio_clock)) {
set_clock_at(&is->audclk, is->audio_clock - (NUMBUFFERS*MAX_AUDIO_FRAME_SIZE - decoded) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0);
sync_clock_to_slave(&is->extclk, &is->audclk);
}


return decoded;   
}


解码出图片如何和Lae关联?

LXZAPI_AsyncCallback(CallbackUpdateTexture, "video.png", m_ctx, vp->bmp);


MyVideo.cpp, Lae通用文件.修改一点就ok

LXZSystem_RegisterAPI("Play", ccPlay);
LXZSystem_RegisterAPI("Pause", ccPause);
LXZSystem_RegisterAPI("seekpos", ccSeekpos);
LXZSystem_RegisterAPI("Resume", ccResume);


LXZSystem_RegisterAPI("skipfront", ccSkipNext);
LXZSystem_RegisterAPI("skipback", ccSkipBack);
LXZSystem_RegisterAPI("toggle_muted", ccMutex);
LXZSystem_RegisterAPI("volume", ccSetVolume);
LXZSystem_RegisterAPI("toggle_fullscreen", toggle_fullscreen);




0 0
原创粉丝点击