FFmpeg獲取網(wǎng)絡(luò)攝像頭數(shù)據(jù)解碼
對USB攝像頭實時編碼,在前面已經(jīng)探討過了。這次改變下思路,嘗試去截取網(wǎng)絡(luò)攝像頭的H264碼流,將其解碼播放。
這里的測試代碼,是在海康攝像頭的基礎(chǔ)上進行的。
解碼的大致流程和以前的保持一致,只不過增加了部分函數(shù)。
FFmpeg打開媒體文件并查看媒體文件的信息,有三個步驟:
avformat_open_input;
avformat_find_stream_info;
av_dump_format;
依次調(diào)用三個函數(shù)后,我們可以很清楚的知道碼流的各種信息。
完整的代碼:
#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;//結(jié)構(gòu)體AVFormatContext:包含碼流參數(shù)較多
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庫以及網(wǎng)絡(luò)加密協(xié)議相關(guān)的庫
if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)//打開多媒體數(shù)據(jù)并且獲得信息
{
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)//讀取視音頻數(shù)據(jù)并且獲得信息
{
return -1;
}
av_dump_format(pFormatCtx, 0, argv[1], false);//手工調(diào)試函數(shù),看到pFormatCtx->streams的內(nèi)容
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();//分配內(nèi)存
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;
}
//處理圖像數(shù)據(jù)
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;
}
隊列函數(shù):
#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;
}
}
隊列函數(shù)的頭文件:
#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
解碼后的數(shù)據(jù)進入隊列,再從隊列中取出,利用opencv將其顯示(opencv顯示是另外開的一個線程函數(shù))。
admin:jdh123456@10.170.6.187,這里是攝像頭的名稱和IP地址。
測試代碼下載:點擊打開鏈接
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C語言 數(shù)據(jù)結(jié)構(gòu)之中序二叉樹實例詳解
這篇文章主要介紹了C語言 數(shù)據(jù)結(jié)構(gòu)之中序二叉樹實例詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01
c++中的消息框messagebox()詳細(xì)介紹及使用方法
本文將介紹下c++中的messagebox()的使用方法:常用屬性/按鈕的形式/返回值等等,感興趣的朋友可以了解下,希望本文可以幫助到你2013-02-02
VSCode 使用 Code Runner 插件無法編譯運行文件名帶空格的文件問題
這篇文章主要介紹了VSCode 使用 Code Runner 插件無法編譯運行文件名帶空格的文件問題,本文通過圖文實例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-07-07

