FFmpeg通过摄像头实现对视频流进行解码并显示测试代码(旧接口)
這里通過USB攝像頭(注:windows7/10下使用內(nèi)置攝像頭,linux下接普通的usb攝像頭(Logitech))獲取視頻流,然后解碼,最后再用opencv顯示。用到的模塊包括avformat、avcodec和avdevice。libavdevice庫是libavformat的一個(gè)補(bǔ)充庫(complementary library)。主要涉及到的接口函數(shù)包括:注:其中一些接口是被廢棄的舊接口
1. avdevice_register_all:初始化libavdevice庫并注冊(cè)所有輸入輸出設(shè)備;
2. av_find_input_format:根據(jù)輸入格式的名字查找AVInputFormat,在測(cè)試代碼中,windows平臺(tái)使用”vfwcap”(video for windows capture),linux平臺(tái)使用”v4l2”(Video4Linux2);
3. avformat_alloc_context:分配AVFormatContext;
4. avformat_open_input:打開輸入流并讀取header;
5. avformat_find_stream_info:讀取媒體文件的數(shù)據(jù)包以獲取流信息;
6. 通過AVFormatContext中AVStream查找視頻/音頻流索引,這里在windows10下獲取到的編碼類型為mjpeg即AV_CODEC_ID_MJPEG,像素格式為yuv422p即AV_PIX_FMT_YUVJ422P;在linux或windows7下獲取到的編碼類型為rawvideo即AV_CODEC_ID_RAWVIDEO,像素格式y(tǒng)uyv422即AV_PIX_FMT_YUYV422;可見攝像頭類型不同獲取到的編碼類型和像素格式可能也不同;
7. avcodec_find_decoder:由codec ID查找已注冊(cè)的解碼器;
8. avcodec_open2:初始化AVCodecContext;
9. av_frame_alloc:分配一個(gè)AVFrame并設(shè)置它的字段為默認(rèn)值;
10. av_malloc:為一個(gè)AVPacket分配內(nèi)存塊;
11. sws_getContext:分配一個(gè)SwsContext;
12. av_read_frame:獲取流即packet(AVPacket);
13. avcodec_decode_video2:解碼視頻幀,從流數(shù)據(jù)到圖像數(shù)據(jù);
14. sws_scale:轉(zhuǎn)換圖像格式;
15. av_free_packet:釋放AVPacket;
16. av_frame_free:釋放由av_frame_alloc分配的AVFrame;
17. sws_freeContext:釋放由sws_getContext分配的SwsContext;
18. av_free:釋放由av_malloc分配的AVPacket;
19. avformat_close_input:關(guān)閉打開的AVFormatContext并釋放。
測(cè)試代碼(test_ffmpeg_decode_show.cpp):
#include "funset.hpp"
#include <stdio.h>
#include <iostream>
#include <memory>
#include <fstream>#ifdef __cplusplus
extern "C" {
#endif#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/mem.h>
#include <libavutil/imgutils.h>#ifdef __cplusplus
}
#endif#include <opencv2/opencv.hpp>int test_ffmpeg_decode_show_old()
{avdevice_register_all();#ifdef _MSC_VERconst char* input_format_name = "vfwcap";const char* url = "";
#elseconst char* input_format_name = "video4linux2";const char* url = "/dev/video0";
#endifAVInputFormat* input_fmt = av_find_input_format(input_format_name);AVFormatContext* format_ctx = avformat_alloc_context();int ret = avformat_open_input(&format_ctx, url, input_fmt, nullptr);if (ret != 0) {fprintf(stderr, "fail to open url: %s, return value: %d\n", url, ret);return -1;}ret = avformat_find_stream_info(format_ctx, nullptr);if (ret < 0) {fprintf(stderr, "fail to get stream information: %d\n", ret);return -1;}int video_stream_index = -1;for (int i = 0; i < format_ctx->nb_streams; ++i) {const AVStream* stream = format_ctx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_index = i;fprintf(stdout, "type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);}}if (video_stream_index == -1) {fprintf(stderr, "no video stream\n");return -1;}AVCodecContext* codec_ctx = format_ctx->streams[video_stream_index]->codec;AVCodec* codec = avcodec_find_decoder(codec_ctx->codec_id);if (!codec) {fprintf(stderr, "no decoder was found\n");return -1;}ret = avcodec_open2(codec_ctx, codec, nullptr);if (ret != 0) {fprintf(stderr, "fail to init AVCodecContext: %d\n", ret);return -1;}AVFrame* frame = av_frame_alloc();AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));SwsContext* sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, 0, nullptr, nullptr, nullptr);if (!frame || !packet || !sws_ctx) {fprintf(stderr, "fail to alloc\n");return -1;}int got_picture = -1;std::unique_ptr<uint8_t[]> data(new uint8_t[codec_ctx->width * codec_ctx->height * 3]);cv::Mat mat(codec_ctx->height, codec_ctx->width, CV_8UC3);int width_new = 320, height_new = 240;cv::Mat dst(height_new, width_new, CV_8UC3);const char* winname = "usb video1";cv::namedWindow(winname);while (1) {ret = av_read_frame(format_ctx, packet);if (ret < 0) {fprintf(stderr, "fail to av_read_frame: %d\n", ret);continue;}if (packet->stream_index == video_stream_index) {ret = avcodec_decode_video2(codec_ctx, frame, &got_picture, packet);if (ret < 0) {fprintf(stderr, "fail to avcodec_decode_video2: %d\n", ret);av_free_packet(packet);continue;}if (got_picture) {uint8_t* p[1] = { data.get() };int dst_stride[1] = { frame->width * 3 };sws_scale(sws_ctx, frame->data, frame->linesize, 0, codec_ctx->height, p, dst_stride);mat.data = data.get();cv::resize(mat, dst, cv::Size(width_new, height_new));cv::imshow(winname, dst);}}av_free_packet(packet);int key = cv::waitKey(25);if (key == 27) break;}cv::destroyWindow(winname);av_frame_free(&frame);sws_freeContext(sws_ctx);av_free(packet);avformat_close_input(&format_ctx);fprintf(stdout, "test finish\n");return 0;
}執(zhí)行結(jié)果如下:
GitHub:https://github.com//fengbingchun/OpenCV_Test
總結(jié)
以上是生活随笔為你收集整理的FFmpeg通过摄像头实现对视频流进行解码并显示测试代码(旧接口)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: FFmpeg实现获取USB摄像头视频流测
- 下一篇: FFmpeg通过摄像头实现对视频流进行解
