FFmpeg通过摄像头实现对视频流进行解码并显示测试代码(新接口)
在https://blog.csdn.net/fengbingchun/article/details/93975325?中給出了通過舊接口即FFmpeg中已廢棄的接口實(shí)現(xiàn)通過攝像頭獲取視頻流然后解碼并顯示的測(cè)試代碼,這里通過使用FFmpeg中的新接口再次實(shí)現(xiàn)通過的功能,主要涉及到的接口函數(shù)包括:
1. avdevice_register_all:初始化libavdevice庫(kù)并注冊(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. av_dict_set:設(shè)置或重寫一個(gè)AVDictionary項(xiàng),測(cè)試代碼中設(shè)置video_size為640x480,設(shè)置input_format為mjpeg,這兩個(gè)設(shè)置僅對(duì)usb攝像頭有效,對(duì)windows內(nèi)置攝像頭會(huì)crash;
5. avformat_open_input:打開輸入流并讀取header;
6. avformat_find_stream_info:讀取媒體文件的數(shù)據(jù)包以獲取流信息;
7. 通過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;由于通過av_dict_set進(jìn)行了設(shè)置,因此編碼類型由rawvideo調(diào)整成了mjpeg,可見可以通過av_dict_set對(duì)usb攝像頭的原有配置進(jìn)行調(diào)整;
8. avcodec_find_decoder:由codec ID查找已注冊(cè)的解碼器;
9. avcodec_alloc_context3:分配一個(gè)AVCodecContext并設(shè)置它的字段為默認(rèn)值;
10. avcodec_open2:初始化AVCodecContext,由于前面使用avcodec_alloc_context3,因此在調(diào)用avcodec_open2之前,需要對(duì)AVCodecContext的某些字段進(jìn)行指定值,如寬、高、像素格式等,thread_count用于指定幾個(gè)線程來進(jìn)行解碼;
11. av_frame_alloc:分配一個(gè)AVFrame并設(shè)置它的字段為默認(rèn)值;
12. av_malloc:為一個(gè)AVPacket分配內(nèi)存塊;
13. sws_getContext:分配一個(gè)SwsContext;
14. av_image_alloc:根據(jù)指定的寬、高、像素格式為圖像分配buffer;
15. av_read_frame:獲取流即packet(AVPacket);
16. avcodec_send_packet:提供原始packet數(shù)據(jù)作為解碼器的輸入;
17. avcodec_receive_frame:從解碼器中獲取解碼后的數(shù)據(jù);
18. sws_scale:轉(zhuǎn)換圖像格式;
19. av_packet_unref:釋放AVPacket;
20. av_frame_free:釋放由av_frame_alloc分配的AVFrame;
21. sws_freeContext:釋放由sws_getContext分配的SwsContext;
22. av_freep:釋放由av_malloc分配的AVPacket;
23. avformat_close_input:關(guān)閉打開的AVFormatContext并釋放;
24. av_dict_free:釋放由av_dist_set分配的AVDictionary;
25. av_freep:釋放由av_image_alloc分配的buffer。
測(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_new()
{avdevice_register_all();AVDictionary* options = nullptr;
#ifdef _MSC_VERconst char* input_format_name = "vfwcap";const char* url = "";
#elseconst char* input_format_name = "video4linux2";const char* url = "/dev/video0";av_dict_set(&options, "video_size", "640x480", 0);av_dict_set(&options, "input_format", "mjpeg", 0);
#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, &options);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 (unsigned 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;}AVCodecParameters* codecpar = format_ctx->streams[video_stream_index]->codecpar;const AVCodec* codec = avcodec_find_decoder(codecpar->codec_id);if (!codec) {fprintf(stderr, "fail to avcodec_find_decoder\n");return -1;}AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return -1;}codec_ctx->pix_fmt = AVPixelFormat(codecpar->format);codec_ctx->height = codecpar->height;codec_ctx->width = codecpar->width;codec_ctx->thread_count = 4;ret = avcodec_open2(codec_ctx, codec, nullptr);if (ret != 0) {fprintf(stderr, "fail to avcodec_open2: %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;}uint8_t* bgr_data[4];int bgr_linesize[4];av_image_alloc(bgr_data, bgr_linesize, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, 1);cv::Mat mat(codec_ctx->height, codec_ctx->width, CV_8UC3);const char* winname = "usb video2";cv::namedWindow(winname);while (1) {ret = av_read_frame(format_ctx, packet);if (ret >= 0 && packet->stream_index == video_stream_index) {ret = avcodec_send_packet(codec_ctx, packet);if (ret < 0) {fprintf(stderr, "fail to avcodec_send_packet: %d\n", ret);av_packet_unref(packet);continue;}ret = avcodec_receive_frame(codec_ctx, frame);if (ret < 0) {fprintf(stderr, "fail to avcodec_receive_frame\n");av_packet_unref(packet);continue;}sws_scale(sws_ctx, frame->data, frame->linesize, 0, codec_ctx->height, bgr_data, bgr_linesize);mat.data = bgr_data[0];cv::imshow(winname, mat);}av_packet_unref(packet);int key = cv::waitKey(25);if (key == 27) break;}cv::destroyWindow(winname);av_frame_free(&frame);sws_freeContext(sws_ctx);av_dict_free(&options);avformat_close_input(&format_ctx);av_freep(packet);av_freep(&bgr_data[0]);fprintf(stdout, "test finish\n");return 0;
}執(zhí)行結(jié)果如下:
GitHub:https://github.com//fengbingchun/OpenCV_Test
總結(jié)
以上是生活随笔為你收集整理的FFmpeg通过摄像头实现对视频流进行解码并显示测试代码(新接口)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: FFmpeg通过摄像头实现对视频流进行解
- 下一篇: FFmpeg中AVDictionary介
