基于ffmpeg-1.1的视频监控,输出到LCD上

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <signal.h>
#include <pthread.h>
#include "myhead.h"
#include "lcd.h"


struct lcd_info_t *lcdinfo = NULL;
AVCodecContext  *pCodecCtx = NULL; // 保存了相应流的详细编码信息,比如视频的宽、高,编码类型等
AVFrame *pFrameRGB = NULL;


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* 初始化互斥锁 */
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; /* 初始化条件变量 */
int stop = 0;


void *lcd_routine(void *arg)
{
sleep(1);
while (!stop) {
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex); /* 等待 */
show16bpp(lcdinfo, 0, 16, pCodecCtx->width, pCodecCtx->height, (u16 *)pFrameRGB->data[0]);
pthread_mutex_unlock(&mutex);
}

return (void *)0;
}


void signal_handle(int sig)
{
if (SIGINT == sig) {
stop = 1;
}
}


/* ffmpeg中的AVFormat库可以帮助进行这一“分拆音视频流”的过程;而AVCodec则帮助解码视频。 */
int main (int argc, char **argv)
{
if (argc < 2) {
my_debug("Usage:%s file_name\n", argv[0]);
exit(1);
}

if (SIG_ERR == signal(SIGINT, signal_handle))
err_exit("signal");


/* LCD设备文件 */
char *fb_dev = "/dev/fb0";
lcdinfo = lcd_init(fb_dev);
if (NULL == lcdinfo)
err_exit("lcd initialize failure");

av_register_all();// 调用它用以注册所有支持的文件格式以及编解码器
avdevice_register_all(); // 初始化和注册所有设备

/* AVFormatContext保存需要读入的文件的格式信息,比如流的个数以及流数据等*/
AVFormatContext *pFormatCtx = NULL;// 必须为NULL或者由avformat_alloc_context分配得到

if (strstr(argv[1], "/dev/video")) {
AVInputFormat *pInputFmt = av_find_input_format("video4linux2");
if (NULL == pInputFmt)
err_exit("av_find_input_format");

if (avformat_open_input(&pFormatCtx, argv[1], pInputFmt, NULL) != 0)
err_exit("avformat_open_input");
} else if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
err_exit("avformat_open_input");

/*
** avformat_open_input函数只是读文件头,并不会填充流信息,因此我们需要接下来调用
** avformat_find_stream_info获取文件中的流信息,此函数会读取packet,并确定文件中所有的流信息,
** pFormatCtx->streams指向文件中的流,但此函数并不会改变文件指针,读取的packet会给后面的
** 解码进行处理.最后调用一个帮助函数av_dump_format,输出文件的信息,也就是我们在使用ffmpeg时
** 能看到的文件详细信息.第二个参数指定输出哪条流的信息,-1表示给ffmpeg自己选择.最后一个参数
** 用于指定dump的是不是输出文件,我们dump的是输入文件,因此一定要是0.
*/
if(avformat_find_stream_info(pFormatCtx, NULL ) < 0)
err_exit("avformat_find_stream_info");
av_dump_format(pFormatCtx, -1, argv[1], 0);


/* 现在 pFormatCtx->streams 中已经有所有流了,因此现在我们遍历它找到第一条视频流 */
int videoStream = -1, i;
for(i = 0; i < pFormatCtx->nb_streams; i++)
if( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
if(videoStream == -1)
err_exit("not find video stream");

/* 现在 pFormatCtx->streams 中已经有所有流了,因此现在我们遍历它找到第一条视频流 */
AVCodec *pCodec = NULL; // 真正的编解码器,其中有编解码需要调用的函数
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
err_exit("not find video decode");
if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 )
err_exit("not open video decode");

/* 接下来我们准备给即将解码的图片分配内存空间 */
/* AVFrame:用于保存数据帧的数据结构,这里的两个帧分别是保存颜色转换前后的两帧图像 */
/* pFrame用于存储解码后的数据,pFrameRGB用于存储转换后的数据 */
AVFrame *pFrame = NULL;
pFrame = avcodec_alloc_frame();
if(pFrame == NULL)
err_exit("avcodec_alloc_frame");
pFrameRGB = avcodec_alloc_frame();
if(pFrameRGB == NULL)
err_exit("avcodec_alloc_frame");


my_debug("width:%dheight:%d\n", pCodecCtx->width, pCodecCtx->height);
/*
** 调用 avpicture_get_size 根据 pCodecCtx 中原始图像的宽高计算 RGB24 格式
** 的图像需要占用的空间大小,这是为了之后给 pFrameRGB 分配空间:
*/
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB565LE, pCodecCtx->width, pCodecCtx->height);


/*
** 首先是用 av_malloc 分配上面计算大小的内存空间,然后调用
** avpicture_fill 将 pFrameRGB 跟 buffer 指向的内存关联起来 
*/
uint8_t *buffer = NULL;
buffer = av_malloc(numBytes);
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB565LE, pCodecCtx->width, pCodecCtx->height);
/* 一切准备好就可以开始从文件中读取视频帧并解码得到图像了
** av_read_frame 从文件中读取一个packet,对于视频来说一个packet里面包含一帧图像数据,音频可能包含多
** 个帧(当音频帧长度固定时),读到这一帧后,如果是视频帧,则使用 avcodec_decode_video2 对packet中的帧
** 进行解码,有时候解码器并不能从一个packet中解码得到一帧图像数据(比如在需要其他参考帧的情况下),因
** 此会设置 frameFinished,如果已经得到下一帧图像则设置 frameFinished 非零,否则为零.所以这里我们判
** 断 frameFinished 是否为零来确定 pFrame 中是否已经得到解码的图像.注意在每次处理完后需要调用
** av_free_packet 释放读取的packet.解码得到图像后,很有可能不是我们想要的 RGB24 格式,因此需要使用
** swscale 来做转换,调用 sws_getCachedContext 得到转换上下文,使用 sws_scale 将图形从解码后的格式转
** 换为 RGB24,最后将前50帧写人 ppm 文件.最后释放图像以及关闭文件
*/
i = 0;
int frameFinished;
AVPacket packet;// 解析文件时会将音/视频帧读入到packet中
lcd_printf(lcdinfo, 0, 0, RED, 0, 0, "制作:赵建辉  QQ:809205580");
pthread_t tid;
if (pthread_create(&tid, NULL, lcd_routine, NULL) != 0)
err_exit("pthread_create");


pthread_detach(tid);/* 设置线程分离属性 */

struct SwsContext *img_convert_ctx = NULL;
img_convert_ctx = sws_getCachedContext(img_convert_ctx, pCodecCtx->width,
pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_RGB565LE, SWS_BICUBIC,
NULL, NULL, NULL);
if(!img_convert_ctx)
err_exit("Cannot initialize sws conversion context\n");


while(!stop && av_read_frame(pFormatCtx, &packet) >= 0 ) {
if(packet.stream_index == videoStream) {
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if(frameFinished) {
pthread_mutex_lock(&mutex);
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, 
pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
pthread_cond_signal(&cond); /* 获取新的一帧图像,唤醒lcd显示线程 */
pthread_mutex_unlock(&mutex);
}
}
av_free_packet(&packet);
}


if (buffer)
av_free(buffer);
if (pFrameRGB)
av_free(pFrameRGB);
if (pFrame)
av_free(pFrame);
if (pFormatCtx)
avformat_close_input(&pFormatCtx);
if (img_convert_ctx)
sws_freeContext(img_convert_ctx);
if (lcdinfo) {
if (lcd_release(lcdinfo) < 0)
err_exit("lcd_release");
}

avcodec_close(pCodecCtx);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);


return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告:本课程项目仅供学习参考,请不要直接商用,概不负责任何法律责任。 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。 我将带领大家一起来学习使用FFmpeg开发视频监控项目,并动手操练。具体内容包括: 一、视频监控的架构和流程二、FFmpeg4.3+SDL2+Qt5开发环境的搭建三、FFmpeg的SDK编程回顾总结并操练四、SDL2.0的编程回顾总结并操练五、颜色空间转换RGB和YUV的原理与实战六、Qt5+FFmpeg本地摄像头采集预览实战七、代码封装:摄像头h264/5编码并存储八、Qt5+FFmpeg单路网络摄像头采集预览九、Qt5+FFmpeg单路网络摄像头采集预览录制会看十、onvif与GB/T-28181的简介  音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢?   因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。 所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。   梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。  目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。  

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗-永无止境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值