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

这里面要同步声音、快进、快退、暂停等,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);

基于Lae的ffmpeg播放器相关推荐

  1. 基于ijkplayer封装ffmpeg播放器的录屏实现

    关于ijkplayer我自己提出的issue的https://github.com/Bilibili/ijkplayer/issues/1166的一个小总结,还先不要期望我有完整代码给出,如果只想知道 ...

  2. 仿迅雷播放器教程 -- 基于VLC的MFC播放器 (6)

    代码下载: http://download.csdn.net/detail/qq316293804/6409417 昨天的教程里写着预计MFC播放器会隔得久一点,但是今晚仔细看了下VLC的常用代码,发 ...

  3. FFmpeg音频播放器(8)-创建FFmpeg播放器

    原文地址::https://www.jianshu.com/p/73b0a0a9bb0d 相关文章 1.FFmpeg音频解码播放----https://www.jianshu.com/p/76562a ...

  4. ffmpeg播放器实现详解 - 音频同步控制

    ffplay是ffmpeg源码中一个自带的开源播放器实例,同时支持本地视频文件的播放以及在线流媒体播放,功能非常强大. FFplay: FFplay is a very simple and port ...

  5. 基于python的音频播放器_基于python实现音乐播放器代码实例

    基于python实现音乐播放器代码实例,一首,函数,按钮,布局,音乐 基于python实现音乐播放器代码实例 易采站长站,站长之家为您整理了基于python实现音乐播放器代码实例的相关内容. 核心播放 ...

  6. 基于单片机的音乐播放器设计

     word完整版可点击如下下载>>>>>>>> 基于单片机的音乐播放器设计-硬件开发文档类资源-CSDN下载内容包括详细设计文档word版,附带开题报告 ...

  7. 《Android FFmpeg 播放器开发梳理》第一章 播放器初始化与解复用流程

    <Android FFmpeg 播放器开发梳理>: 第零章 基础公共类的封装 播放器初始化与解复用流程 这一章,我们来讲解播放器解复用(从文件中读取数据包)的流程.在讲解播放器的读数据包流 ...

  8. iOS播放器之基于VLCKit的自定义播放器

    VLC是一款了不起的播放器,很喜欢,功能很强大,目前据我所知能播放RMVB.MKV.mp4.FLV等等格式的视频,分享一个基于VLCKit的自定义播放器 源码地址:https://github.com ...

  9. 音频频谱显示-基于fmod设计音乐播放器并动态显示频谱图(二)

    音频频谱显示-显示音频文件静态频谱图(一) https://blog.csdn.net/xiaolong1126626497/article/details/126971535 音频频谱显示-基于fm ...

  10. 基于Qt的音乐播放器(二)切换歌曲,调节音量,调节语速,暂停

    2020博客之星年度总评选进行中:请为74号的狗子投上宝贵的一票! 我的投票地址:点击为我投票 文章目录 1.切换歌曲 2.调节音量 3.调节语速 4.播放/暂停 5.我们来看下效果 6.关于上一篇提 ...

最新文章

  1. 基于【 centos7】一 || 安装ELK
  2. gdcm::DummyValueGenerator的测试程序
  3. 随题而学(二)多维数组转一维数组
  4. 蓝桥杯 第几个幸运数 set
  5. TrueCrypt 密码找回工具
  6. C++奥赛第四弹——阿克曼函数
  7. 【AD封装】TF(micro SD)卡座封装大全(带3D)
  8. Linux系统可视化界面与Shell界面切换
  9. 使用Tor以加密方式发送BCH
  10. 写书给我带来了什么?
  11. 已知特征多项式求所有对应的二阶矩阵
  12. 1分钟搞定 OneNote自己账号扩容到15G永久免费空间
  13. HigherHRNet代码复现问题集(assert isinstance(orig, torch.nn.Module))
  14. 上海小伙三次成功创业,资产达上十亿被称为“创业神童”
  15. 淘宝店铺将导航和店招修改成通栏
  16. TDengine 单节点Cluster not ready( 群集未就绪) 异常问题分析及解决方案
  17. mysql安装流程以及各类问题解决
  18. arduino蓝牙linux,Arduino Tian 开发板 Arduino yun升级 wifi 蓝牙 Linux限量
  19. 共享办公室,高效率创业
  20. 我是怎么提升写作能力的

热门文章

  1. 【考研】22上海大学计算机上岸记录
  2. AMiner 会议论文推荐第十四期
  3. 三角函数与代数恒等式(1)
  4. sd卡数据恢复源码android,SD卡受损数据恢复图文详解
  5. 上市商业银行手机银行App月活增长及应用对标分析
  6. 计算机没有进程管理器,win10系统任务管理器中没有进程的处理办法
  7. ACO 蚁群算法(算法流程,TSP例子解析)
  8. matlab在点内加入权值,matlab权值矩阵
  9. 个人开发作品分享:iTab新标签页
  10. 远程桌面计算机没有密码设置,如果对方电脑没有设置密码,如何使用远程桌面连接?...