FFmpeg中AVIOContext的使用方法詳解
通過FFmpeg對視頻進(jìn)行編解碼時,如果輸入文件存在本機(jī)或通過USB攝像頭、筆記本內(nèi)置攝像頭獲取數(shù)據(jù)時,可通過avformat_open_input接口中的第二個參數(shù)直接指定即可。但如果待處理的視頻數(shù)據(jù)存在于內(nèi)存塊中時,該如何指定,可通過FFmpeg中的結(jié)構(gòu)體AVIOContext實(shí)現(xiàn),此時avformat_open_input中的第二個參數(shù)傳nullptr。
涉及到FFmpeg中的主要函數(shù)是avio_alloc_context,聲明如下:
AVIOContext *avio_alloc_context( unsigned char *buffer, int buffer_size, int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence))
(1).buffer:通過AVIOContext進(jìn)行輸入/輸出操作的內(nèi)存塊,由av_malloc分配,av_free釋放。av_read_frame會持續(xù)從此處取數(shù)據(jù)。
(2).buffer_size: 內(nèi)存塊大小。
(3).write_flag: 如果buffer作為輸出即寫入則為1(FFmpeg將處理后的數(shù)據(jù)寫入buffer),如果buffer作為輸入則設(shè)置為0(FFmpeg從buffer獲取數(shù)據(jù)).
(4).opaque: 指向用戶特定數(shù)據(jù)的不透明指針。
(5).read_packet: 回調(diào)函數(shù),當(dāng)buffer作為輸入時必須指定,否則可為nullptr。此回調(diào)函數(shù)的參數(shù)依次為avio_alloc_context中的opaque、buffer、buffer_size。
(6).write_packet:回調(diào)函數(shù),當(dāng)buffer作為輸出時必須指定,否則可為nullptr。此回調(diào)函數(shù)的參數(shù)依次為avio_alloc_context中的opaque、buffer、buffer_size。
(7).seek:回調(diào)函數(shù),用于查找指定字節(jié)位置的函數(shù),可為nullptr。
調(diào)用完此接口后,需要將此接口返回的指針賦值給AVFormatContext的pb即I/O context。
以下為測試代碼段:
(1).主線程用于實(shí)時顯示內(nèi)存塊內(nèi)容。另有一個單獨(dú)線程用于創(chuàng)建數(shù)據(jù)。這里使用隊(duì)列:線程set_packet持續(xù)向隊(duì)列中push數(shù)據(jù);回調(diào)函數(shù)read_packet持續(xù)從隊(duì)列中pop數(shù)據(jù)
typedef struct Buffer { unsigned char* data; unsigned int length; } Buffer; class BufferQueue { public: BufferQueue() = default; ~BufferQueue() {} void push(Buffer& buffer) { std::unique_lock<std::mutex> lck(mtx); queue.push(buffer); cv.notify_all(); } void pop(Buffer& buffer) { std::unique_lock<std::mutex> lck(mtx); while (queue.empty()) { cv.wait(lck); } buffer = queue.front(); queue.pop(); } unsigned int size() { return queue.size(); } private: std::queue<Buffer> queue; std::mutex mtx; std::condition_variable cv; }; class PacketScaleQueue { public: PacketScaleQueue() = default; ~PacketScaleQueue() { Buffer buffer; while (getPacketSize() > 0) { popPacket(buffer); delete[] buffer.data; } while (getScaleSize() > 0) { popScale(buffer); delete[] buffer.data; } } void init(unsigned int buffer_num = 16, unsigned int buffer_size = 1024 * 1024 * 4) { for (unsigned int i = 0; i < buffer_num; ++i) { Buffer buffer = { new unsigned char[buffer_size], buffer_num}; pushPacket(buffer); } } void pushPacket(Buffer& buffer) { packet_queue.push(buffer); } void popPacket(Buffer& buffer) { packet_queue.pop(buffer); } unsigned int getPacketSize() { return packet_queue.size(); } void pushScale(Buffer& buffer) { scale_queue.push(buffer); } void popScale(Buffer& buffer) { scale_queue.pop(buffer); } unsigned int getScaleSize() { return scale_queue.size(); } private: BufferQueue packet_queue, scale_queue; };
(2).線程函數(shù)set_packet內(nèi)容如下:類PacketScaleQueue中有兩個BufferQueue: packet_queue:未被使用的;scale_queue:已被使用的
void set_packet(PacketScaleQueue& packet_encode) { while (packet_encode_flag) { static unsigned char v1 = 0, v2 = 0, v3 = 255; static const size_t size = height * width; Buffer buffer; packet_encode.popPacket(buffer); memset(buffer.data, v1, size); memset(buffer.data + size, v2, size); memset(buffer.data + size * 2, v3, size); packet_encode.pushScale(buffer); ++v1; ++v2; --v3; if (v1 == 255) v1 = 0; if (v2 == 255) v2 = 0; if (v3 == 0) v3 = 255; std::this_thread::sleep_for(std::chrono::milliseconds(40)); } }
(3).回調(diào)函數(shù)read_packet內(nèi)容如下:
int read_packet(void* opaque, uint8_t* buf, int buf_size) { PacketScaleQueue* packet_encode = static_cast<PacketScaleQueue*>(opaque); Buffer buffer; packet_encode->popScale(buffer); memcpy(buf, buffer.data, buf_size); packet_encode->pushPacket(buffer); return buf_size; }
(4).主函數(shù)test_ffmpeg_avio_show內(nèi)容如下:
int test_ffmpeg_avio_show() { PacketScaleQueue packet_encode; packet_encode.init(30, block_size); std::thread thread_packet(set_packet, std::ref(packet_encode)); uint8_t* avio_ctx_buffer = static_cast<uint8_t*>(av_malloc(block_size)); if (!avio_ctx_buffer) { print_error_string(AVERROR(ENOMEM)); return -1; } AVIOContext* avio_ctx = avio_alloc_context(avio_ctx_buffer, block_size, 0, &packet_encode, &read_packet, nullptr, nullptr); if (!avio_ctx) { print_error_string(AVERROR(ENOMEM)); return -1; } AVFormatContext* ifmt_ctx = avformat_alloc_context(); if (!ifmt_ctx) { print_error_string(AVERROR(ENOMEM)); return -1; } ifmt_ctx->pb = avio_ctx; AVDictionary* dict = nullptr; av_dict_set(&dict, "video_size", "640x480", 0); av_dict_set(&dict, "pixel_format", "bgr24", 0); auto ret = avformat_open_input(&ifmt_ctx, nullptr, av_find_input_format("rawvideo"), &dict); if (ret < 0) { fprintf(stderr, "Could not open input\n"); print_error_string(ret); return ret; } ret = avformat_find_stream_info(ifmt_ctx, nullptr); if (ret < 0) { fprintf(stderr, "Could not find stream information\n"); print_error_string(ret); return ret; } av_dump_format(ifmt_ctx, 0, "nothing", 0); int video_stream_index = -1; for (unsigned int i = 0; i < ifmt_ctx->nb_streams; ++i) { const AVStream* stream = ifmt_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, "error: no video stream\n"); return -1; } AVCodecParameters* codecpar = ifmt_ctx->streams[video_stream_index]->codecpar; if (codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) { fprintf(stderr, "error: this test code only support rawvideo encode: %d\n", codecpar->codec_id); return -1; } AVPacket* packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))); if (!packet) { fprintf(stderr, "fail to av_malloc\n"); return -1; } cv::Mat mat(height, width, CV_8UC3); const char* winname = "show video"; cv::namedWindow(winname); while (1) { ret = av_read_frame(ifmt_ctx, packet); if (ret >= 0 && packet->stream_index == video_stream_index && packet->size > 0) { mat.data = packet->data; cv::imshow(winname, mat); av_packet_unref(packet); int key = cv::waitKey(30); if (key == 27) { packet_encode_flag = false; break; } } } av_freep(packet); cv::destroyWindow(winname); avformat_close_input(&ifmt_ctx); // note: the internal buffer could have changed, and be != avio_ctx_buffer if (avio_ctx) { av_freep(&avio_ctx->buffer); av_freep(&avio_ctx); } //avio_context_free(&avio_ctx); ==> av_freep(&avio_ctx); av_dict_free(&dict); thread_packet.join(); fprintf(stdout, "test finish\n"); return 0; }
執(zhí)行結(jié)果如下圖所示:
GitHub:https://github.com/fengbingchun/OpenCV_Test
到此這篇關(guān)于FFmpeg中AVIOContext的使用方法詳解的文章就介紹到這了,更多相關(guān)FFmpeg AVIOContext內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ string和wstring相互轉(zhuǎn)換方式
這篇文章主要介紹了C++ string和wstring相互轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02C++超詳細(xì)講解強(qiáng)制類型轉(zhuǎn)換的用法
在C++語言中新增了四個關(guān)鍵字static_cast、const_cast、reinterpret_cast和dynamic_cast。這四個關(guān)鍵字都是用于類型轉(zhuǎn)換的,類型轉(zhuǎn)換(type?cast),是高級語言的一個基本語法。它被實(shí)現(xiàn)為一個特殊的運(yùn)算符,以小括號內(nèi)加上類型名來表示,接下來讓我們一起來詳細(xì)了解2022-06-06詳解C++ 動態(tài)庫導(dǎo)出函數(shù)名亂碼及解決
這篇文章主要介紹了C++ 動態(tài)庫導(dǎo)出函數(shù)名亂碼及解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03C++使用map實(shí)現(xiàn)多進(jìn)程拷貝文件的程序思路
這篇文章主要介紹了C++使用mmap實(shí)現(xiàn)多進(jìn)程拷貝文件,通過本文給大家分享程序思路及完整代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12C++類與對象的重點(diǎn)知識點(diǎn)詳細(xì)分析
類和對象是兩種以計(jì)算機(jī)為載體的計(jì)算機(jī)語言的合稱。對象是對客觀事物的抽象,類是對對象的抽象。類是一種抽象的數(shù)據(jù)類型;變量就是可以變化的量,存儲在內(nèi)存中—個可以擁有在某個范圍內(nèi)的可變存儲區(qū)域2023-02-02