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

本文尝试截取网络摄像头的H264码流并解码播放,测试代码基于海康摄像头。介绍了FFmpeg打开媒体文件查看信息的步骤,给出完整代码及队列函数和头文件。解码后的数据入队再出队,利用opencv显示,还提供了测试代码下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://blog.youkuaiyun.com/qq_41051855/article/details/79729259

对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地址。

    测试代码下载:点击打开链接
--------------------- 
作者:为取经而来 
来源:优快云 
原文:https://blog.youkuaiyun.com/qq_41051855/article/details/79729259 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值