ffmpeg播放器实战(播放渲染类)

简介: 播放渲染类

1.传入播放信息类

Render::Render(DataInfo*dataInfo,QObject*parent)
    : QObject{parent}
{
this->dataInfo=dataInfo;
}

2.开启渲染

voidRender::slotRender()
{
autovideoCodecCtx=dataInfo->getVideoCtx();
autoaudioCodecCtx=dataInfo->getAudioCtx();
intSDLFormat=dataInfo->getSDLFmt();
intaudioIndex=dataInfo->getAudioIndex();
floatspeed= (dataInfo->getPlaySpeed() ==PLAY_SLOW) ?0.5 : dataInfo->getPlaySpeed();
intisOpenAudio=-1;
if (audioIndex>=0) {
//音频//SDL_AudioSpec是Simple DirectMedia Layer(SDL)库中的一个结构体,用于指定音频设备的参数和配置。//它包含了音频流的格式、采样率、声道数等信息,以及回调函数等。//通过设置SDL_AudioSpec的各个字段,可以实现音频的播放、录制和处理等功能。SDL_AudioSpecaudioSpec;
audioSpec.freq=audioCodecCtx->sample_rate;            //采样率audioSpec.format=SDLFormat;                             //声音格式audioSpec.channels=audioCodecCtx->ch_layout.nb_channels;  //通道数audioSpec.silence=0;                                     //静音值audioSpec.samples=audioCodecCtx->frame_size;             //每帧大小audioSpec.callback=playAudioCallback;                     //回调函数//audioSpec.userdata 是一个用于存储音频特殊数据的字段。//它允许开发者在音频规格对象中附加自定义数据,以便在需要时进行访问和处理。//这个字段的具体用途和内容取决于使用该字段的应用程序或平台。//在处理音频时,开发者可以根据自己的需求将相关的信息存储在 userdata 中,以便后续处理过程中使用。audioSpec.userdata=dataInfo;
//SDL_OpenAudio是一个用于初始化音频子系统的SDL库函数。它用于设置音频设备的参数,并打开音频流以进行读写操作。通过调用此函数,可以配置音频格式、采样率、声道数和缓冲区大小等参数。isOpenAudio=SDL_OpenAudio(&audioSpec, nullptr);
if (speed==1)
//SDL_PauseAudio函数是一个IT类问题。这个函数用于暂停或恢复音频流的播放。如果参数为0,表示恢复播放;如果参数为1,表示暂停播放。SDL_PauseAudio(0);
    }
//视频////视频//SDL_CreateRenderer函数是SDL库中用于创建渲染器的函数。它的原型定义如下://SDL_Renderer* SDL_CreateRenderer(SDL_Window* window, int index, Uint32 flags)//参数解释://window:要创建渲染器的窗口指针。//index:指定渲染的驱动索引,一般传入-1表示使用第一个支持的驱动。//flags:渲染器的附加选项,可以是SDL_RendererFlags枚举类型的值,一般传入0即可。//函数返回一个SDL_Renderer指针,如果创建失败则返回NULL。//渲染器用于将图形数据绘制到窗口上,可以通过渲染器实现图形、纹理、字体等的渲染操作。创建渲染器后,可以使用SDL_RenderPresent函数将绘制结果更新到窗口上。//SDL_RENDERER_ACCELERATE是SDL库中的一个常量,用于指定渲染器的加速类型。它表示使用硬件加速来执行渲染操作,以提高性能和效率SDL_Renderer*SDLRend=SDL_CreateRenderer(dataInfo->getSDLWind(), -1, SDL_RENDERER_ACCELERATED);
if (!SDLRend) {
qDebug() <<"SDL_CreateRenderer fail";
    }
////SDL_CreateTexture函数是SDL库中的一个函数,用于创建一个纹理对象。纹理对象可以用来在窗口上绘制图像。//该函数的原型如下: SDL_Texture* SDL_CreateTexture(SDL_Renderer* renderer, Uint32 format, int access, int w, int h)//参数说明://renderer:渲染器对象,用于创建纹理的目标渲染器。//format:纹理的像素格式,可以参考SDL_PixelFormatEnum枚举类型。//access:纹理的访问方式,可以是SDL_TEXTUREACCESS_STATIC、SDL_TEXTUREACCESS_STREAMING或SDL_TEXTUREACCESS_TARGET。//w和h:纹理的宽度和高度。//函数返回值为创建成功的纹理对象指针,如果创建失败则返回NULL。//注意:在使用完纹理后,需要使用SDL_DestroyTexture函数来销毁纹理对象,以释放内存空间。//这是一个关于SDL库中的像素格式的问题。SDL_PIXELFORMAT_IYUV是SDL库中的一种像素格式,它用于表示色彩空间转换中的YUV图像数据。其中,Y表示亮度(luma),U和V表示色度(chroma)。IYUV格式在视频编解码和处理中经常使用。//SDL_TEXTUREACCESS_STREAMING是SDL(Simple DirectMedia Layer)库中的一个常量,用于指定纹理的访问方式。具体来说,当使用SDL创建一个纹理时,可以通过设置纹理的访问方式来决定如何访问纹理的像素数据。// SDL_TEXTUREACCESS_STREAMING表示纹理是可通过内存缓冲区进行动态更新的。这意味着可以通过锁定纹理并直接修改像素数据来实现每帧更新纹理的效果。通常,这种访问方式适用于需要频繁更新纹理数据的场景,如实时渲染、视频播放等。SDL_Texture*SDLText=SDL_CreateTexture(SDLRend, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, videoCodecCtx->width, videoCodecCtx->height);
if (!SDLText) {
qDebug() <<"SDL_CreateTexture fail";
    }
int64_tlastPtsTime=0;
//av_gettime() 是 FFmpeg 库中的一个函数,用于获取当前系统时间(以微秒为单位)。它通常用于计算时间戳和执行时间相关的操作。该函数的原型如下://int64_t av_gettime(void);//函数返回一个 int64_t 类型的整数,表示当前系统的时间。具体的时间单位要根据操作系统的不同而定。在 Windows 上,它通常以100-nanosecond为单位(即10^-7秒),而在其他系统上可能以微秒为单位(即10^-6秒)。//请注意,av_gettime() 函数是属于 FFmpeg 库而不是 C 标准库。如果你希望使用该函数,需要先引入 FFmpeg 的相关头文件,并链接对应的库。int64_tlastRenderTime=av_gettime();
int64_taudioClock=0;
//判断是否解码//判断视频数据是否为空//判断音频数据是否为空while (dataInfo->getDecodeState() ||!dataInfo->videoIsEmpty() ||!dataInfo->audioIsEmpty()) {
//播放状态为空则跳出循环if (dataInfo->getPlayState() ==PLAY_STOP) {
break;
        }
//视频和音频数据为空if (dataInfo->videoIsEmpty() &&dataInfo->audioIsEmpty()) {
//暂停0.005秒QThread::msleep(5);
//跳出本次循环continue;
        }
//如果视频为空if (dataInfo->videoIsEmpty()) {
//暂停0.01秒QThread::msleep(10);
//跳出本次循环continue;
        }
//获取第一个视频数据autovideoData=dataInfo->videoPop();
//如果视频渲染时间设置为-1if (videoData.ptsTime==-1)
continue;
//获取播放速度speed= (dataInfo->getPlaySpeed() ==PLAY_SLOW) ?0.5 : dataInfo->getPlaySpeed();
//如果跳转为真if (dataInfo->getSeekFlag()) {
dataInfo->setSeekFlag(false);
//设置跳转时间dataInfo->setAudioClock(0);
        } else {
//最后渲染的pts不等于0和视频pts不等于0if (lastPtsTime!=0&&videoData.ptsTime!=0) {
//视频打开//获得音频数据索引//播放速度为1if (isOpenAudio>=0&&dataInfo->getAudioIndex() >=0&&speed==1) {
//SDL_GetAudioStatus函数是SDL库中的一个函数,用于获取音频设备的当前状态。该函数返回一个枚举类型的值,表示音频设备的状态,可能的取值包括://SDL_AUDIO_STOPPED:音频设备当前停止状态。//SDL_AUDIO_PLAYING:音频设备当前正在播放音频。//SDL_AUDIO_PAUSED:音频设备当前处于暂停状态。//请注意,SDL_GetAudioStatus函数需要在SDL初始化后才能正常调用,并且需要先使用SDL_OpenAudio函数打开音频设备才能获取正确的状态。if (SDL_GetAudioStatus() ==SDL_AUDIO_PAUSED){
//暂停状态设置为播放状态SDL_PauseAudio(0);
                    }
audioClock=dataInfo->getAudioClock();
//如果视频播放pts<播放时间if (videoData.ptsTime<audioClock) {
//大于半秒if (audioClock-videoData.ptsTime>=500) {  //视频帧太慢,则丢弃qDebug() <<"丢帧:"<<videoData.ptsTime;
//更新进度emitsignalProgress(videoData.ptsTime);
//最后ptslastPtsTime=videoData.ptsTime;
//释放该帧av_frame_free(&videoData.frame);
continue;
                        }
                    } else {
while (dataInfo->getPlayState() ==PLAY_PLAY&&!dataInfo->audioIsEmpty()
&&videoData.ptsTime>dataInfo->getAudioClock() &&!dataInfo->getSeekFlag()) {
QThread::msleep(1);
                    }
                }
            } else {
//音频数据大于0if (dataInfo->getAudioIndex() >=0) {
if (SDL_GetAudioStatus() ==SDL_AUDIO_PLAYING)
//设置暂停SDL_PauseAudio(1);
//获得音频第一帧autoaudioData=dataInfo->audioFirst();
//音频渲染时间<视频渲染时间//音频渲染时间>=0while (audioData.ptsTime>=0&&audioData.ptsTime<videoData.ptsTime) {
//获得音频数据autotempData=dataInfo->audioPop();
//释放av_free(tempData.buff);
//获得音频第一个数据audioData=dataInfo->audioFirst();
//设置音频时间dataInfo->setAudioClock(audioData.ptsTime);
                    }
                }
int64_toffset= (videoData.ptsTime-lastPtsTime) *1000.0/speed;
int64_tnowOffset=av_gettime() -lastRenderTime;
if (offset<nowOffset) {
if (nowOffset-offset>=offset) {  //视频帧太慢,则丢弃qDebug() <<"丢帧:"<<videoData.ptsTime;
emitsignalProgress(videoData.ptsTime);
lastPtsTime=videoData.ptsTime;
lastRenderTime=av_gettime();
av_frame_free(&videoData.frame);
continue;
                    }
                    } else {
if (offset>nowOffset+1000)
QThread::usleep(offset-nowOffset-1000);
                    }
                }
            }
        }
//显示//SDL_UpdateYUVTexture是一个SDL函数,用于更新YUV格式的纹理数据。//通过传入纹理、矩形区域、YUV帧数据和行大小等参数,//函数会将YUV帧数据更新到纹理上,以便渲染器显示。SDL_UpdateYUVTexture(SDLText,
nullptr,
videoData.frame->data[0],
videoData.frame->linesize[0],
videoData.frame->data[1],
videoData.frame->linesize[1],
videoData.frame->data[2],
videoData.frame->linesize[2]);
//SDL_RenderClear(SDLRend);
//SDL_RenderClear函数是用来清空渲染器的函数,它会将渲染器上的所有内容清除,使得渲染器变为空白。//SDL_RenderCopy是SDL库中的一个函数,用于将纹理数据复制到渲染目标上//在使用SDL_RenderCopy函数时,需要传入渲染器(renderer)、纹理(texture)、源矩形(srcrect)和目标矩形(dstrect)等参数。该函数会将纹理的一部分或全部复制到目标矩形所指定的位置上。//例如,在上述引用的代码中,SDL_RenderCopy函数被用来渲染视频画面。根据旋转角度的不同,可以选择使用SDL_RenderCopy或SDL_RenderCopyEx函数来实现渲染。//总结起来,SDL_RenderCopy函数用于将纹理数据复制到渲染目标上,通过传入不同的参数可以实现不同的渲染效果SDL_RenderCopy(SDLRend, SDLText, nullptr, nullptr);
//SDL_RenderPresent函数通过调用SDL_Renderer的RenderPresent方法,将图像显示在屏幕上SDL_RenderPresent(SDLRend);
lastPtsTime=videoData.ptsTime;
lastRenderTime=av_gettime();
//设置播放时间dataInfo->setPlayTime(videoData.ptsTime);
emitsignalProgress(videoData.ptsTime);
//暂停while (dataInfo->getPlayState() ==PLAY_PAUSE) {
if (dataInfo->getAudioIndex() >=0)
if (SDL_GetAudioStatus() ==SDL_AUDIO_PLAYING)
SDL_PauseAudio(1);
QThread::msleep(10);
//防止暂停时失去焦点就没有显示SDL_UpdateYUVTexture(SDLText,
nullptr,
videoData.frame->data[0],
videoData.frame->linesize[0],
videoData.frame->data[1],
videoData.frame->linesize[1],
videoData.frame->data[2],
videoData.frame->linesize[2]);
SDL_RenderClear(SDLRend);
SDL_RenderCopy(SDLRend, SDLText, nullptr, nullptr);
SDL_RenderPresent(SDLRend);
        }
av_frame_free(&videoData.frame);
if (dataInfo->getAudioIndex() >=0) {
if (SDL_GetAudioStatus() ==SDL_AUDIO_PAUSED)
SDL_PauseAudio(0);
        }
    }
if (dataInfo->getAudioIndex() >=0)
SDL_CloseAudio();
//销毁SDLSDL_DestroyTexture(SDLText);
SDL_DestroyRenderer(SDLRend);
//暂停播放emitsignalPlayStop();
}
voidRender::playAudioCallback(void*udata, uchar*stream, intlen)
{
//SDL_memset 是 C/C++ 语言中的一个函数,用于将指定内存块的每个字节都设置为特定的值。//它的原型如下: void *SDL_memset(void *dst, int value, size_t len);//其中,参数 dst 是要设置的内存块的起始地址,value 是要设置的值,//len 是要设置的字节数。 例如,如果我们想将一个数组的每个元素都设置为0,//可以使用 SDL_memset 函数进行操作: int arr[10]; SDL_memset(arr, 0, sizeof(arr));//这样,arr 数组中的每个元素都会被设置为0。SDL_memset(stream, 0, len);
autodataInfo= (DataInfo*)udata;
intSDLFormat=dataInfo->getSDLFmt();
autodata=dataInfo->audioPop();
if (data.ptsTime>0) {
dataInfo->setAudioClock(data.ptsTime);
if (data.buffSize>0)
//SDL_MixAudioFormat是一个用于混合音频数据的函数,它可以在不同的音频格式之间进行混合。它是SDL(Simple DirectMedia Layer)库中提供的一个函数。 函数原型如下: void SDL_MixAudioFormat(Uint8* dst, const Uint8* src, SDL_AudioFormat format, Uint32 len, int volume); void SDL_MixAudioFormat(Uint8* dst, const Uint8* src, SDL_AudioFormat format, Uint32 len, int volume); 参数说明: dst:目标音频数据的指针,它将包含混合后的音频数据。 src:源音频数据的指针,它包含要混合的音频数据。 format:音频数据的格式,使用SDL_AudioFormat类型表示,例如SDL_AudioFormat.SDL_AUDIO_S16LSB表示16位带符号整数的小端存储格式。 len:要混合的音频数据的长度(以字节为单位)。 volume:音频混合的音量控制参数,范围是0到128。 该函数将通过叠加两个音频数据来实现混合,将源音频数据(src)与目标音频数据(dst)进行混合,并将结果存储在目标音频数据中。音量参数(volume)可以用来控制混合后的音量大小。 注意:在调用该函数之前,需要确保源音频数据和目标音频数据的格式是一致的,否则可能会导致不可预期的结果。SDL_MixAudioFormat(stream, data.buff, SDLFormat, data.buffSize, dataInfo->getPlayVolume());
av_free(data.buff);
    }
}
相关文章
|
6月前
|
XML 编解码 JSON
FFmpeg常用命令讲解及实战二(2)
FFmpeg常用命令讲解及实战二
82 0
|
17天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
78 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
29天前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
115 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
3月前
|
机器学习/深度学习 编解码 API
【机器学习】FFmpeg+Whisper:二阶段法视频理解(video-to-text)大模型实战
【机器学习】FFmpeg+Whisper:二阶段法视频理解(video-to-text)大模型实战
49 0
|
5月前
|
存储 编解码 Linux
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
62 1
|
6月前
FFmpeg开发笔记(十八)FFmpeg兼容各种音频格式的播放
《FFmpeg开发实战》一书中,第10章示例程序playaudio.c原本仅支持mp3和aac音频播放。为支持ogg、amr、wma等非固定帧率音频,需进行三处修改:1)当frame_size为0时,将输出采样数量设为512;2)遍历音频帧时,计算实际采样位数以确定播放数据大小;3)在SDL音频回调函数中,确保每次发送len字节数据。改进后的代码在chapter10/playaudio2.c,可编译运行播放ring.ogg测试,成功则显示日志并播放铃声。
112 1
FFmpeg开发笔记(十八)FFmpeg兼容各种音频格式的播放
|
6月前
|
Web App开发 编解码 vr&ar
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
使用FFmpeg从音视频处理到流媒体技术的探索和实战应用
278 2
|
6月前
|
编解码 计算机视觉 索引
使用ffmpeg MP4转 m3u8并播放 实测!!
使用ffmpeg MP4转 m3u8并播放 实测!!
306 1
|
6月前
|
存储 数据处理 API
ffmpeg过滤器filter理论与实战
ffmpeg过滤器filter理论与实战
98 0
|
6月前
|
编解码 vr&ar 内存技术
FFmpeg常用命令讲解及实战二(1)
FFmpeg常用命令讲解及实战二
56 0