当前位置: 首页 > news >正文

商业网站是什么常德外贸网站优化推广

商业网站是什么,常德外贸网站优化推广,aitt网站建设中,深圳市住房建设局官方网站NDK前期基础知识终于学完了#xff0c;现在开始进入项目实战学习#xff0c;通过FFmpeg实现一个简单的音视频播放器。本文主要内容如下#xff1a;阻塞式队列SafeQueue。音视频BaseChannel基础通道。音视频压缩包加入队列。视频解码与播放。ANativeWindow渲染用到的ffmpeg、…NDK前期基础知识终于学完了现在开始进入项目实战学习通过FFmpeg实现一个简单的音视频播放器。本文主要内容如下阻塞式队列SafeQueue。音视频BaseChannel基础通道。音视频压缩包加入队列。视频解码与播放。ANativeWindow渲染用到的ffmpeg、rtmp等库资源https://wwgl.lanzout.com/iN21C0qiiija音视频播放流程1.准备工作完成音视频解封装后通过音视频媒体上下文AVFormatContext获取到具体的音视频压缩包AVPacket2.将音视频压缩包AVPacket解压得到音视频原始包AVFrame可播放的文件包3.拿到音视频原始包AVFrame进行播放。代码逻辑1.获取压缩包AVPacket、获取原始包AVFrame、播放是个生产消费重复并发进行的过程可以通过队列queue来完成。2.创建两个队列queue压缩包AVPacket队列和原始包AVFrame队列3.创建循环获取压缩包AVPacket并push压缩包到AVPacket队列4.创建循环去AVPacket队列中获取压缩包AVPacket解压得到原始包AVFrame并push原始包到AVFrame队列5.创建循环去AVFrame队列中获取原始包AVFrame进行播放6.音频和视频都有相同的解压、原始包、播放动作故创建分别创建音频和视频队列并封装到音频AudioChannel通道和视频VideoChannel通道中去处理音频AudioChannel通道和视频VideoChannel通道重复部分封装BaseChannel通道去。一、阻塞式队列SafeQueue封装线程安全队列SafeQueue通过pthread_mutex_t互斥锁和pthread_cond_t条件变量来实现数据入队出队等待和唤醒工作。#ifndef NDKPLAYER_SAFEQUEUE_H #define NDKPLAYER_SAFEQUEUE_H#include queue #include pthread.husing namespace std;/*** 线程安全队列* tparam T 泛型存放任意类型*/ templatetypename T class SafeQueue { private:typedef void (*ReleaseCallback)(T *); // 函数指针定义 做回调 用来释放T里面的内容的 private:queueT queue;pthread_mutex_t mutex; // 互斥锁 安全pthread_cond_t cond; // 等待 和 唤醒int work; // 标记队列是否工作ReleaseCallback releaseCallback; public:SafeQueue() {pthread_mutex_init(mutex, 0); // 初始化互斥锁pthread_cond_init(cond, 0); // 初始化条件变量}virtual ~SafeQueue() {pthread_mutex_destroy(mutex); // 释放互斥锁pthread_cond_destroy(cond); // 释放条件变量}/*** 入队 [ AVPacket * 压缩包] [ AVFrame * 原始包]*/void insertToQueue(T value) {pthread_mutex_lock(mutex); // 多线程的访问先锁住if (work) {// 工作状态入队queue.push(value);// 当插入数据包 进队列后发出通知唤醒pthread_cond_signal(cond);} else {//非工作状态释放valueif (releaseCallback) {releaseCallback(value);}}pthread_mutex_unlock(mutex); // 多线程的访问要解锁}/*** 出队 [ AVPacket * 压缩包] [ AVFrame * 原始包]*/int getQueueAndDel(T value) {int result 0;pthread_mutex_lock(mutex); // 多线程的访问先锁住while (work queue.empty()) {// 如果是工作 并且 队列里面没有数据就阻塞在这里pthread_cond_wait(cond, mutex);}if (!queue.empty()) {// 取出队列的数据包 给外界并删除队列数据包value queue.front();// 删除队列中的数据queue.pop();// 成功 return trueresult 1;}pthread_mutex_unlock(mutex); // 多线程的访问要解锁return result;}/*** 设置工作状态设置队列是否工作* param work*/void setWork(int work) {pthread_mutex_lock(mutex); // 多线程的访问先锁住this-work work;// 每次设置状态后就去唤醒pthread_cond_signal(cond);pthread_mutex_unlock(mutex); // 多线程的访问要解锁}int empty() {return queue.empty();}int size() {return queue.size();}/*** 清空队列中所有的数据循环一个一个的删除*/void clear() {pthread_mutex_lock(mutex); // 多线程的访问先锁住unsigned int size queue.size();for (int i 0; i size; i) {//循环释放队列中的数据T value queue.front();if (releaseCallback) {releaseCallback(value); // 让外界去释放堆区空间}queue.pop(); // 删除队列中的数据让队列为0}pthread_mutex_unlock(mutex); // 多线程的访问要解锁}/*** 设置此函数指针的回调让外界去释放* param releaseCallback*/void setReleaseCallback(ReleaseCallback releaseCallback) {this-releaseCallback releaseCallback;} };#endif //NDKPLAYER_SAFEQUEUE_H二、音视频BaseChannel基础通道BaseChannel封装压缩包和原始包队列#ifndef NDKPLAYER_BASECHANNEL_H #define NDKPLAYER_BASECHANNEL_Hextern C { #include ffmpeg/include/libavcodec/avcodec.h };#include SafeQueue.h #include android/log.h// log宏 #define TAG NDK #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)class BaseChannel {public:int stream_index; // 音频 或 视频 的下标SafeQueueAVPacket * packets; // 压缩的 数据包SafeQueueAVFrame * frames; // 原始的 数据包bool isPlaying; // 音频 和 视频 都会有的标记 是否播放AVCodecContext *codecContext 0; // 音频 视频 都需要的 解码器上下文BaseChannel(int streamIndex, AVCodecContext *codecContext): stream_index(streamIndex), codecContext(codecContext) {packets.setReleaseCallback(releaseAVPacket);frames.setReleaseCallback(releaseAVFrame);}// 父类析构一定要加virtualvirtual ~BaseChannel() {// 清空队列packets.clear();frames.clear();}/*** 释放 队列中 所有的 AVPacket ** typedef void (*ReleaseCallback)(T *);*/static void releaseAVPacket(AVPacket **pPacket) {if (pPacket) {// 释放队列里面的 T AVPacketav_packet_free(pPacket);*pPacket 0;}}/*** 释放 队列中 所有的 AVFrame ** typedef void (*ReleaseCallback)(T *);*/static void releaseAVFrame(AVFrame **pFrame) {if (pFrame) {// 释放队列里面的 T AVFrameav_frame_free(pFrame);*pFrame 0;}} };#endif //NDKPLAYER_BASECHANNEL_H三、音视频压缩包加入队列创建子线程把音频和视频 压缩包 加入队列里面去/*** 函数指针* 此函数和NdkPlayer这个对象没有关系你没法拿NdkPlayer的私有成员* return*/ void *task_start(void *ndk_player) {NdkPlayer *ndk_player_ static_castNdkPlayer *(ndk_player);ndk_player_-start_();return 0; // 必须返回否则报错 }void NdkPlayer::start() {// 开始播放isPlaying 1;// 音视频通道开始if (audio_channel) {audio_channel-start();}if (video_channel) {video_channel-start();}// 创建子线程把音频和视频 压缩包 加入队列里面去pthread_create(pid_start, 0, task_start, this); }/*** 循环获取压缩包AVPacket并push压缩包到队列*/ void NdkPlayer::start_() {LOGI(NdkPlayer::start_());while (isPlaying) {// AVPacket 可能是音频 也可能是视频压缩包AVPacket *packet av_packet_alloc();int result av_read_frame(format_context, packet);// return 0 if OKif (!result) {// 把压缩包AVPacket 分别加入音频 和 视频队列if (audio_channel audio_channel-stream_index packet-stream_index) {// 音频audio_channel-packets.insertToQueue(packet);} else if (video_channel video_channel-stream_index packet-stream_index) {// 视频video_channel-packets.insertToQueue(packet);}} else if (result AVERROR_EOF) {// end of file 读到文件末尾了 AVERROR_EOF// 表示读完了要考虑释放播放完成并不代表播放完毕isPlaying 0;LOGI(NdkPlayer::start_() end);} else {// av_read_frame 出现了错误结束当前循环break;}} // end whileisPlaying 0;audio_channel-stop();video_channel-stop(); }四、视频解码与播放第一个线程 视频取出队列的压缩包 进行编码 编码后的原始包 再push队列中去第二线线程视频从队列取出原始包播放#include VideoChannel.hVideoChannel::VideoChannel(int streamIndex, AVCodecContext *codecContext): BaseChannel(streamIndex, codecContext) {}VideoChannel::~VideoChannel() {}void VideoChannel::stop() {}/*** 函数指针 解码* param video_channel* return*/ void *task_video_decode(void *video_channel) {VideoChannel *video_channel_ static_castVideoChannel *(video_channel);video_channel_-video_decode();return 0; }/*** 函数指针 播放* param video_channel* return*/ void *task_video_play(void *video_channel) {VideoChannel *video_channel_ static_castVideoChannel *(video_channel);video_channel_-video_play();return 0; }void VideoChannel::start() {LOGI(VideoChannel::start());isPlaying 1;// 队列开始工作了packets.setWork(1);frames.setWork(1);// 第一个线程 视频取出队列的压缩包 进行编码 编码后的原始包 再push队列中去pthread_create(pid_video_decode, 0, task_video_decode, this);// 第二线线程视频从队列取出原始包播放pthread_create(pid_video_play, 0, task_video_play, this); }/*** 第一个线程 视频取出队列的压缩包 进行编码 编码后的原始包 再push队列中去*/ void VideoChannel::video_decode() {LOGI(VideoChannel::video_decode());AVPacket *pkt 0;while (isPlaying) {// 获取AVPacket * 压缩包int result packets.getQueueAndDel(pkt);if (!isPlaying) {// 获取压缩包是耗时操作获取完如果关闭了播放跳出循环break;}if (!result) {// 获取失败可能是压缩包数据还没有加入队列继续获取continue;}// 1.发送pkt压缩包给缓冲区return 0 on successresult avcodec_send_packet(codecContext, pkt);// FFmpeg源码缓存一份pkt释放即可releaseAVPacket(pkt);if (result) {// avcodec_send_packet 出现了错误break;}AVFrame *frame av_frame_alloc();// 2.从缓冲区拿出来原始包,return 0: successresult avcodec_receive_frame(codecContext, frame);if (result AVERROR(EAGAIN)) {// B帧 B帧参考前面成功 B帧参考后面失败 可能是P帧没有出来再拿一次就行了continue;} else if (result ! 0) {// avcodec_receive_frame 出现了错误break;}// 拿到了原始包并将原始包push到队列frames.insertToQueue(frame);}// 解码获取原始包后释放压缩包releaseAVPacket(pkt); }/*** 第二线线程视频从队列取出原始包播放*/ void VideoChannel::video_play() {LOGI(VideoChannel::video_play());AVFrame *frame 0;uint8_t *dst_data[4]; // RGBA 播放文件int dst_linesize[4]; // RGBA//给 dst_data 申请内存 width * height * 4 xxxxav_image_alloc(dst_data, dst_linesize,codecContext-width, codecContext-height, AV_PIX_FMT_RGBA, 1);// SWS_BILINEAR 适中算法SwsContext *sws_ctx sws_getContext(// 下面是输入环节codecContext-width,codecContext-height,codecContext-pix_fmt, // 自动获取 xxx.mp4 的像素格式 AV_PIX_FMT_YUV420P // 写死的// 下面是输出环节codecContext-width,codecContext-height,AV_PIX_FMT_RGBA,SWS_BILINEAR, NULL, NULL, NULL);while (isPlaying) {int result frames.getQueueAndDel(frame);if (!isPlaying) {break; // 如果关闭了播放跳出循环releaseAVFrame(frame);}if (!result) { // ret 0continue; // 哪怕是没有成功也要继续假设你生产太慢(原始包加入队列)我消费就等一下你}// 格式转换 yuv --- rgbasws_scale(sws_ctx,// 下面是输入环节 YUV的数据frame-data, frame-linesize,0, codecContext-height,// 下面是输出环节 成果RGBA数据 dst_datadst_data,dst_linesize);/*** ANatvieWindows 渲染工作* SurfaceView ----- ANatvieWindows* 这里拿不到Surface只能函数指针renderCallback将RGBA数据 dst_data 回调给 native-lib.cpp显示* 函数指针renderCallback* 参数1RGBA数据 dst_data 数组被传递会退化成指针默认就是取第1元素* 参数2视频宽* 参数3视频高* 参数4数据长度*/this-renderCallback(dst_data[0], codecContext-width, codecContext-height,dst_linesize[0]);// 释放原始包因为已经被渲染完了没用了releaseAVFrame(frame);}releaseAVFrame(frame);isPlaying 0;av_free(dst_data[0]);// free(sws_ctx); FFmpeg必须使用人家的函数释放直接崩溃sws_freeContext(sws_ctx); }void VideoChannel::setRenderCallback(RenderCallback renderCallback) {this-renderCallback renderCallback; }五、ANativeWindow渲染1初始化surfaceViewprivate SurfaceView surfaceView; surfaceView findViewById(R.id.surfaceView); mNdkPlayer new NdkPlayer(dataSource); mNdkPlayer.setSurfaceHolder(surfaceView);2绑定surfaceHolderpublic class NdkPlayer implements SurfaceHolder.Callback {private SurfaceHolder surfaceHolder;public void setSurfaceHolder(SurfaceView surfaceView) {if (surfaceHolder ! null) {// 清除上一次数据surfaceHolder.removeCallback(this);}this.surfaceHolder surfaceView.getHolder();// 添加监听surfaceHolder.addCallback(this);}Overridepublic void surfaceCreated(NonNull SurfaceHolder holder) {}Overridepublic void surfaceChanged(NonNull SurfaceHolder holder, int format, int width, int height) {setSurfaceNative(holder.getSurface());}Overridepublic void surfaceDestroyed(NonNull SurfaceHolder holder) {}/*** native函数区域*/private native void setSurfaceNative(Surface surface); }3关联Native层ANativeWindowANativeWindow *window 0; /*** 实例化播放window 关联 surfaceView*/ extern C JNIEXPORT void JNICALL Java_com_ndk_player_NdkPlayer_setSurfaceNative(JNIEnv *env, jobject thiz, jobject surface) {// 线程安全锁住pthread_mutex_lock(mutex);// 先释放之前的显示窗口if (window) {ANativeWindow_release(window);window 0;}// 创建新的窗口用于视频显示window ANativeWindow_fromSurface(env, surface);pthread_mutex_unlock(mutex); }4VideoChannel将解析完的RGBA数据可播放数据回调给 native-lib.cpp进行渲染显示。/*** 定义函数指针 实现渲染工作this-renderCallback()回调到这里来*/ void renderCallback(uint8_t *dst_data, int width, int height, int dst_linesize) {LOGI(native-lib::renderCallback playing);pthread_mutex_lock(mutex);// 播放窗口为空释放锁小概率出现if (!window) {pthread_mutex_unlock(mutex);return;}// 设置窗口的大小各个属性ANativeWindow_setBuffersGeometry(window, width, height, WINDOW_FORMAT_RGBA_8888);// 定义缓冲区 bufferANativeWindow_Buffer window_buffer;// 如果在渲染的时候是被锁住的那就无法渲染需要释放防止出现死锁if (ANativeWindow_lock(window, window_buffer, 0)) {ANativeWindow_release(window);window 0;pthread_mutex_unlock(mutex); // 解锁怕出现死锁return;}// 开始渲染把rgba数据 --- 字节对齐 渲染填充window_buffer画面就出来了uint8_t *dst_data_ static_castuint8_t *(window_buffer.bits);// ANativeWindow_Buffer 64字节对齐的数据长度int dst_linesize_ window_buffer.stride * 4;for (int i 0; i window_buffer.height; i) {/*** 参数1接收播放数据容器* 参数2RGBA播放数据* 参数364字节对齐的数据长度*/memcpy(dst_data_ i * dst_linesize_, dst_data i * dst_linesize, dst_linesize_);}// 解锁并且刷新 window_buffer的数据显示画面ANativeWindow_unlockAndPost(window);pthread_mutex_unlock(mutex); }音视频--视频解码与播放渲染功能完成接下来。。。
http://www.w-s-a.com/news/452815/

相关文章:

  • 网站国内空间价格销售技巧
  • 广安建设企业网站qq互联网站备案号
  • 京东网站建设的要求vs2010做的网站
  • wordpress 新闻杂志主题佛山企业网站排名优化
  • 选服务好的网站建设金华市开发区人才网
  • 广州建站商城南阳高质量建设大城市网站
  • 网站建设合同封面模板做代炼的网站
  • 外贸网站建站要多少钱南昌优化排名推广
  • 做公司网站的尺寸一般是多大企业管理网站
  • 苏州网站设计公司兴田德润i简介做签证宾馆订单用啥网站
  • 网站页面设计工具做网站租空间
  • 做智能网站系统百度提交入口
  • 网站建设代理商电话网站规划和建设方案
  • 双桥区网站制作seo 首页
  • 电子商务网站建设前期准备wordpress域名指向二级目录
  • 汕头建站网站模板淮北做网站电话
  • 手机做logo用什么网站服务器安全防护
  • 课程分销的网站怎么做北京企业网站建设方案
  • 吴兴区建设局网站湖北企业网站建设
  • 网页与网站的区别是什么2023年8月份新冠
  • 唐山网站建设外包公司安卓手机怎么搭建网页
  • 国内做网站最大的公司计量检测网站平台建设方案
  • 重庆沛宣网站建设网页制作初学者
  • php网站漂浮广告代码网络营销跟网站推广有啥区别
  • wordpress调用图片优化型网站建设的基本要求
  • 郑州模板网站建设策划公司做网站怎么赚钱滑县电
  • 东昌府聊城网站优化秦皇岛市妇幼保健院
  • 做网站能赚钱吗网页升级访问通知天天更新
  • 做网站使用什么软件的免费招聘网
  • 宁波网站建设公司推荐哪家淄博网站制作公司服务