FFmpeg获取网络摄像头数据解码

    对USB摄像头实时编码,在前面已经探讨过了。这次改变下思路,尝试去截取网络摄像头的H264码流,将其解码播放。

    这里的测试代码,是在海康摄像头的基础上进行的。


    解码的大致流程和以前的保持一致,只不过增加了部分函数。

    FFmpeg打开媒体文件并查看媒体文件的信息,有三个步骤:

    avformat_open_input;

    avformat_find_stream_info;

    av_dump_format;

    依次调用三个函数后,我们可以很清楚的知道码流的各种信息。


    完整的代码:

#include <stdio.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <windows.h>
#include "queue.h"

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")	
#pragma comment(lib ,"swscale.lib")

using namespace std;
using namespace cv;

DWORD WINAPI opencv_imshow(LPVOID lparam)
{
	result_link_type* result_link = (result_link_type*)lparam;
	struct result_node_datatype *result_node2 = NULL;
	while (1)
	{
		result_node2 = result_pop(result_link);
		if (result_node2 == NULL)
		{
			Sleep(1);
			continue;
		}
		imshow("frame", result_node2->result);
		waitKey(1);
	}
}

int main(int argc, const char * argv[])
{
	HANDLE thread1;
	result_link_type *result_link = new result_link_type;
	result_link->head = result_link->end = NULL;
	result_link->result_num = 0;
	thread1 = CreateThread(NULL, 0, opencv_imshow, (LPVOID)result_link, 0, NULL);

	int i;
	int videoStream;
	int frameFinished;
	int numBytes;
	int ret;
	int got_picture;
	long prepts = 0;
	bool first_time = true;

	AVCodec *pCodec;
	AVFrame *pFrame;
	AVFrame *pFrameRGB;
	AVPacket packet;
	AVCodecContext *pCodecCtx;
	AVFormatContext *pFormatCtx = NULL;//结构体AVFormatContext:包含码流参数较多

	static struct SwsContext *img_convert_ctx;

	uint8_t *buffer;
	Mat pCvMat;

	char filepath[] = "rtsp://admin:jdh123456@10.170.6.187/axis-media/media.amp?camera=2";//码流的获取路径

	av_register_all();//注册编解码器
	avformat_network_init();//加载socket库以及网络加密协议相关的库

	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)//打开多媒体数据并且获得信息
	{
		return -1;
	}

	if (avformat_find_stream_info(pFormatCtx, NULL) < 0)//读取视音频数据并且获得信息
	{
		return -1;
	}

	av_dump_format(pFormatCtx, 0, argv[1], false);//手工调试函数,看到pFormatCtx->streams的内容

	videoStream = -1;

	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;
			break;
		}
	}

	if (videoStream == -1)
	{
		return -1;
	}

	pCodecCtx = pFormatCtx->streams[videoStream]->codec;

	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);//查找解码器

	if (pCodec == NULL)
	{
		return -1;
	}

	if (avcodec_open2(pCodecCtx, pCodec, 0) < 0)//初始化AVCodecContext
	{
		return -1;
	}

	if (pCodecCtx->time_base.num > 1000 && pCodecCtx->time_base.den == 1)
	{
		pCodecCtx->time_base.den = 1000;
	}

	pFrame = av_frame_alloc();//分配内存
	pFrameRGB = av_frame_alloc();

	i = 0;
	while (1)
	{
		if (av_read_frame(pFormatCtx, &packet) >= 0)//读取码流中的音频若干帧或者视频一帧
		{
			if (packet.stream_index == videoStream)
			{
				ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);//开始解码
				if (ret < 0)
				{
					printf("Decode Error.(解码错误)\n");
					return ret;
				}
				if (got_picture)//解码成功,got_picture返回任意非零值
				{
					if (first_time)
					{
						//初始化SwsContext
						img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
						if (img_convert_ctx == NULL)
						{
							fprintf(stderr, "Cannot initialize the conversion context!\n");
							exit(1);
						}

						numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
						buffer = (uint8_t *)av_malloc(numBytes);
						avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); // allocator memory for BGR buffer  
						pCvMat.create(cv::Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);
						first_time = false;
					}

					//处理图像数据
					sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
					memcpy(pCvMat.data, buffer, numBytes);
					struct result_node_datatype *result_node = new struct result_node_datatype;
					result_node->result = pCvMat;
					result_push(result_link, result_node);
				}
			}
			av_free_packet(&packet);
		}
	}

	//free(buffer);
	av_free(buffer);
	av_free(pFrameRGB);
	av_free(pFrame);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
	system("Pause");
	return 0;
}

    队列函数:

#include "queue.h"
#include <iostream>

using namespace std;

void result_push(result_link_type* result_link, result_node_datatype * result_node) //入队操作
{
	if (result_link->head == NULL)
	{
		result_link->head = result_node;
		result_link->end = result_link->head;
		result_link->result_num++;
	}
	else
	{
		result_link->end->next = result_node;
		result_link->end = result_node;
		result_link->result_num++;
	}
}

struct result_node_datatype* result_pop(result_link_type* result_link) //出队操作
{
	struct result_node_datatype* tmp_node;
	if (result_link->head == NULL)
		return NULL;
	else if (result_link->head == result_link->end)
	{
		return NULL;
	}
	else
	{
		tmp_node = result_link->head;
		result_link->head = result_link->head->next;
		result_link->result_num--;
		return tmp_node;
	}
}

    队列函数的头文件:

#ifndef QUEUE_H
#define QUEUE_H
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;

typedef struct result_link_datatype
{
	struct result_node_datatype *head;
	struct result_node_datatype *end;
	int result_num;
}result_link_type;

struct result_node_datatype
{
	Mat result;
	struct result_node_datatype* next;
};

void result_push(result_link_type* result_link, result_node_datatype * result_node); //入队操作
struct result_node_datatype* result_pop(result_link_type* result_link);//出队操作

#endif


    解码后的数据进入队列,再从队列中取出,利用opencv将其显示(opencv显示是另外开的一个线程函数)。

    admin:jdh123456@10.170.6.187,这里是摄像头的名称和IP地址。


    测试代码下载:点击打开链接


全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
头像
11-07 01:12
重庆大学 Java
精致的小松鼠人狠话不多:签哪了哥
点赞 评论 收藏
分享
gcniz:一天写两千行你闹呢
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务