基于Lae的ffmpeg播放器
播放器其实就是播放图片,原理很简单。
这里面要同步声音、快进、快退、暂停等,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播放器相关推荐
- 基于ijkplayer封装ffmpeg播放器的录屏实现
关于ijkplayer我自己提出的issue的https://github.com/Bilibili/ijkplayer/issues/1166的一个小总结,还先不要期望我有完整代码给出,如果只想知道 ...
- 仿迅雷播放器教程 -- 基于VLC的MFC播放器 (6)
代码下载: http://download.csdn.net/detail/qq316293804/6409417 昨天的教程里写着预计MFC播放器会隔得久一点,但是今晚仔细看了下VLC的常用代码,发 ...
- FFmpeg音频播放器(8)-创建FFmpeg播放器
原文地址::https://www.jianshu.com/p/73b0a0a9bb0d 相关文章 1.FFmpeg音频解码播放----https://www.jianshu.com/p/76562a ...
- ffmpeg播放器实现详解 - 音频同步控制
ffplay是ffmpeg源码中一个自带的开源播放器实例,同时支持本地视频文件的播放以及在线流媒体播放,功能非常强大. FFplay: FFplay is a very simple and port ...
- 基于python的音频播放器_基于python实现音乐播放器代码实例
基于python实现音乐播放器代码实例,一首,函数,按钮,布局,音乐 基于python实现音乐播放器代码实例 易采站长站,站长之家为您整理了基于python实现音乐播放器代码实例的相关内容. 核心播放 ...
- 基于单片机的音乐播放器设计
word完整版可点击如下下载>>>>>>>> 基于单片机的音乐播放器设计-硬件开发文档类资源-CSDN下载内容包括详细设计文档word版,附带开题报告 ...
- 《Android FFmpeg 播放器开发梳理》第一章 播放器初始化与解复用流程
<Android FFmpeg 播放器开发梳理>: 第零章 基础公共类的封装 播放器初始化与解复用流程 这一章,我们来讲解播放器解复用(从文件中读取数据包)的流程.在讲解播放器的读数据包流 ...
- iOS播放器之基于VLCKit的自定义播放器
VLC是一款了不起的播放器,很喜欢,功能很强大,目前据我所知能播放RMVB.MKV.mp4.FLV等等格式的视频,分享一个基于VLCKit的自定义播放器 源码地址:https://github.com ...
- 音频频谱显示-基于fmod设计音乐播放器并动态显示频谱图(二)
音频频谱显示-显示音频文件静态频谱图(一) https://blog.csdn.net/xiaolong1126626497/article/details/126971535 音频频谱显示-基于fm ...
- 基于Qt的音乐播放器(二)切换歌曲,调节音量,调节语速,暂停
2020博客之星年度总评选进行中:请为74号的狗子投上宝贵的一票! 我的投票地址:点击为我投票 文章目录 1.切换歌曲 2.调节音量 3.调节语速 4.播放/暂停 5.我们来看下效果 6.关于上一篇提 ...
最新文章
- 基于【 centos7】一 || 安装ELK
- gdcm::DummyValueGenerator的测试程序
- 随题而学(二)多维数组转一维数组
- 蓝桥杯 第几个幸运数 set
- TrueCrypt 密码找回工具
- C++奥赛第四弹——阿克曼函数
- 【AD封装】TF(micro SD)卡座封装大全(带3D)
- Linux系统可视化界面与Shell界面切换
- 使用Tor以加密方式发送BCH
- 写书给我带来了什么?
- 已知特征多项式求所有对应的二阶矩阵
- 1分钟搞定 OneNote自己账号扩容到15G永久免费空间
- HigherHRNet代码复现问题集(assert isinstance(orig, torch.nn.Module))
- 上海小伙三次成功创业,资产达上十亿被称为“创业神童”
- 淘宝店铺将导航和店招修改成通栏
- TDengine 单节点Cluster not ready( 群集未就绪) 异常问题分析及解决方案
- mysql安装流程以及各类问题解决
- arduino蓝牙linux,Arduino Tian 开发板 Arduino yun升级 wifi 蓝牙 Linux限量
- 共享办公室,高效率创业
- 我是怎么提升写作能力的