最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

FFmpeg實(shí)現(xiàn)多線程編碼并保存mp4文件

 更新時(shí)間:2023年08月28日 10:58:53   作者:fengbingchun  
這篇文章主要為大家介紹了FFmpeg如何持續(xù)的從指定內(nèi)存中讀取原始數(shù)據(jù),再將解碼數(shù)據(jù)存入隊(duì)列中,并通過單獨(dú)的線程進(jìn)行編碼,最后保存為mp4文件,感興趣的可以了解下

之前介紹的示例:

(1).FFmpeg實(shí)現(xiàn)將編碼后數(shù)據(jù)保存成mp4中對(duì)編碼后數(shù)據(jù)保存成mp4

(2).FFmpeg中AVIOContext的使用方法詳解中通過AVIOContext實(shí)現(xiàn)從內(nèi)存讀取數(shù)據(jù)

(3).FFmpeg中avfilter模塊的介紹與使用中將圖像加載到視頻中

這里將三部分整合到類中,便于后面增加測(cè)試代碼,下面的示例是兩個(gè)線程:從內(nèi)存中讀取數(shù)據(jù),并將指定的圖像加載到視頻,將結(jié)果保存成mp4。

示例代碼如下:

1. 類PacketScaleQueue:用于持續(xù)的從指定內(nèi)存中讀取原始數(shù)據(jù),上面的示例中已包含此代碼

2.類CodecQueue:用于將解碼數(shù)據(jù)存入隊(duì)列中,并通過單獨(dú)的線程進(jìn)行編碼

class AVFrameQueue {
public:
	AVFrameQueue() = default;
	~AVFrameQueue() {}
	void push(AVFrame** frame) {
		std::unique_lock<std::mutex> lck(mtx);
		queue.push(*frame);
		cv.notify_all();
	}
	void pop(AVFrame** frame) {
		std::unique_lock<std::mutex> lck(mtx);
		while (queue.empty()) {
			//cv.wait(lck);
			if (cv.wait_for(lck, std::chrono::milliseconds(150)) == std::cv_status::timeout) {
				fprintf(stderr, "#### Warning: wait timeout\n");
				*frame = nullptr;
				return;
			}
		}
		*frame = queue.front();
		queue.pop();
	}
	size_t size() const {
		return queue.size();
	}
private:
	std::queue<AVFrame*> queue;
	std::mutex mtx;
	std::condition_variable cv;
};
class CodecQueue {
public:
	CodecQueue() = default;
	void init(unsigned int frame_num) {
		for (auto i = 0; i < frame_num; ++i) {
			AVFrame* frame = nullptr;
			pushDecode(&frame);
		}
	}
	~CodecQueue() { release(); }
	void release() {
		AVFrame* frame = nullptr;
		while (getDecodeSize() > 0) {
			popDecode(&frame);
			av_frame_free(&frame);
		}
		while (getEncodeSize() > 0) {
			popEncode(&frame);
			av_frame_free(&frame);
		}
	}
	void pushDecode(AVFrame** frame) { decode_queue.push(frame); }
	void popDecode(AVFrame** frame) { decode_queue.pop(frame); }
	size_t getDecodeSize() const { return decode_queue.size(); }
	void pushEncode(AVFrame** frame) { encode_queue.push(frame); }
	void popEncode(AVFrame** frame) { encode_queue.pop(frame); }
	size_t getEncodeSize() const { return encode_queue.size(); }
private:
	AVFrameQueue decode_queue, encode_queue;
};

3.類VideoCodec:供外面的接口調(diào)用,封裝了視頻的解碼和編碼過程

聲明如下:

typedef struct CodecCtx {
	char outfile_name[VIDEO_CODEC_MAX_STRING_SIZE];
	char video_size[VIDEO_CODEC_MAX_STRING_SIZE];
	char bitrate_str[VIDEO_CODEC_MAX_STRING_SIZE];
	char pixel_format[VIDEO_CODEC_MAX_STRING_SIZE];
	char filter_descr[VIDEO_CODEC_MAX_STRING_SIZE];
	AVFormatContext* ifmt_ctx;
	AVFormatContext* ofmt_ctx;
	AVCodecContext* dec_ctx;
	AVCodecContext* enc_ctx;
	AVFrame* dec_frame;
	AVFilterContext* buffersink_ctx;
	AVFilterContext* buffersrc_ctx;
	AVFilterGraph* filter_graph;
	AVPacket* enc_pkt;
	AVRational frame_rate;
	int term_status;
	int stream_index;
	int frame_count;
	bool encode_thread_end;
} CodecCtx;
class VideoCodec {
public:
	VideoCodec() = default;
	~VideoCodec() {  }
	void setOutfileName(const std::string& name) { outfile_name_ = name; }
	void setVideoSize(const std::string& size) { video_size_ = size; }
	void setPixelFormat(const std::string& format) { pixel_format_ = format; }
	void setFilterDescr(const std::string& filter_descr) { filter_descr_ = filter_descr; }
	void stopEncode() {
		while (raw_packet_queue_.getScaleSize() != 0);
		codec_ctx_->term_status = 1;
		Buffer buffer;
		raw_packet_queue_.popPacket(buffer);
		memset(buffer.data, 0, block_size_);
		raw_packet_queue_.pushScale(buffer); // for av_read_frame to exit normally
	}
	PacketScaleQueue& get_raw_packet_queue(unsigned int buffer_num, size_t buffer_size) {
		raw_packet_queue_.init(buffer_num, buffer_size);
		block_size_ = buffer_size;
		return raw_packet_queue_;
	}
	int openEncode();
	int processEncode();
	int closeEncode();
private:
	std::string outfile_name_ = "";
	std::string video_size_ = "";
	std::string pixel_format_ = "";
	std::string filter_descr_ = "";
	PacketScaleQueue raw_packet_queue_;
	int block_size_ = 0;
	CodecCtx* codec_ctx_ = nullptr;
	AVIOContext* avio_ctx_ = nullptr;
	CodecQueue codec_queue_;
	std::thread encode_thread_;
	int get_decode_context();
	int get_encode_context();
	int init_filters();
	int filter_encode_write_frame(AVFrame* frame);
	int get_output_format_context();
	int flush_encode_write_frame();
	int flush_decoder();
	int flush_encoder();
	void flush_codec();
};

類VideoCodec實(shí)現(xiàn)部分:是之前示例的整理,參考之前示例

4.測(cè)試代碼,即調(diào)用VideoCodec接口,以下是同時(shí)兩個(gè)線程進(jìn)行編碼寫

namespace {
const int total_push_count = 121;
bool flag1 = true;
const size_t block_size_1 = 640 * 480 * 3;
size_t total_push_count_1 = 0;
void fill_raw_data_1(PacketScaleQueue& raw_packet)
{
    unsigned char value = 0;
    while (total_push_count_1 < total_push_count) {
        value += 10;
        if (value >= 255) value = 0;
        Buffer buffer;
        raw_packet.popPacket(buffer);
        memset(buffer.data, value, block_size_1);
        raw_packet.pushScale(buffer);
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
        ++total_push_count_1;
    }
    flag1 = false;
}
void sleep_seconds_1(VideoCodec& video_codec)
{
    while (flag1) {
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
    }
    video_codec.stopEncode();
}
void encode_1()
{
    VideoCodec video_codec;
    video_codec.setOutfileName("out1.mp4");
    video_codec.setVideoSize("640x480");
    video_codec.setPixelFormat("bgr24");
    video_codec.setFilterDescr("movie=1.jpg[logo];[in][logo]overlay=10:20[out]");
    auto& raw_queue = video_codec.get_raw_packet_queue(16, block_size_1);
    std::thread thread_fill(fill_raw_data_1, std::ref(raw_queue));
    auto ret = video_codec.openEncode();
    if (ret != 0) {
        std::cout << "fail to openEncode: " << ret << std::endl;
        //return -1;
    }
    std::thread thread_sleep(sleep_seconds_1, std::ref(video_codec));
    ret = video_codec.processEncode();
    if (ret != 0) {
        std::cout << "fail to processEncode: " << ret << std::endl;
        //return -1;
    }
    thread_fill.join();
    thread_sleep.join();
    video_codec.closeEncode();
    std::cout << "1 total push count: " << total_push_count_1 << std::endl;
}
bool flag2 = true;
const size_t block_size_2 = 640 * 480 * 3;
size_t total_push_count_2 = 0;
void fill_raw_data_2(PacketScaleQueue& raw_packet)
{
    unsigned char value = 0;
    while (total_push_count_2 < total_push_count) {
        value += 10;
        if (value >= 255) value = 0;
        Buffer buffer;
        raw_packet.popPacket(buffer);
        memset(buffer.data, value, block_size_2);
        raw_packet.pushScale(buffer);
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
        ++total_push_count_2;
    }
    flag2 = false;
}
void sleep_seconds_2(VideoCodec& video_codec)
{
    while (flag2) {
        std::this_thread::sleep_for(std::chrono::milliseconds(33));
    }
    video_codec.stopEncode();
}
void encode_2()
{
    VideoCodec video_codec;
    video_codec.setOutfileName("out2.mp4");
    video_codec.setVideoSize("640x480");
    video_codec.setPixelFormat("bgr24");
    video_codec.setFilterDescr("movie=1.jpg[logo];[in][logo]overlay=10:20[out]");
    auto& raw_queue = video_codec.get_raw_packet_queue(16, block_size_2);
    std::thread thread_fill(fill_raw_data_2, std::ref(raw_queue));
    auto ret = video_codec.openEncode();
    if (ret != 0) {
        std::cout << "fail to openEncode: " << ret << std::endl;
        //return -1;
    }
    std::thread thread_sleep(sleep_seconds_2, std::ref(video_codec));
    ret = video_codec.processEncode();
    if (ret != 0) {
        std::cout << "fail to processEncode: " << ret << std::endl;
        //return -1;
    }
    thread_fill.join();
    thread_sleep.join();
    std::cout << "2 total push count: " << total_push_count_2 << std::endl;
}
} // namespce
int test_ffmpeg_libavfilter_movie_multi_thread()
{
    std::thread thread_1(encode_1);
    std::thread thread_2(encode_2);
    thread_1.join();
    thread_2.join();
    std::cout << "test finish" << std::endl;
    return 0;
}

生成的mp4文件結(jié)果如下:在release下生成的兩個(gè)視頻文件完全一致;在debug下編碼過程中有時(shí)會(huì)timeout

GitHubhttps://github.com/fengbingchun/OpenCV_Test

到此這篇關(guān)于FFmpeg實(shí)現(xiàn)多線程編碼并保存mp4文件的文章就介紹到這了,更多相關(guān)FFmpeg多線程編碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何基于C++解決RTSP取流報(bào)錯(cuò)問題

    如何基于C++解決RTSP取流報(bào)錯(cuò)問題

    這篇文章主要介紹了如何基于C++解決RTSP取流報(bào)錯(cuò)問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • OpenCV+Qt實(shí)現(xiàn)圖像處理操作工具的示例代碼

    OpenCV+Qt實(shí)現(xiàn)圖像處理操作工具的示例代碼

    這篇文章主要介紹了利用OpenCV+Qt實(shí)現(xiàn)圖像處理操作工具,可以實(shí)現(xiàn)雪花屏、高斯模糊、中值濾波、毛玻璃等操作,感興趣的可以了解一下
    2022-08-08
  • 詳解VisualS tudio Code開發(fā)Arm嵌入式Linux應(yīng)用

    詳解VisualS tudio Code開發(fā)Arm嵌入式Linux應(yīng)用

    本文介紹如何在 Visual Studio Code 中使用 Yocto Project 生成的 Linux SDK,并針對(duì) Arm 處理器進(jìn)行 C/C++ 應(yīng)用交叉編譯和調(diào)試,感興趣的朋友跟隨小編一起看看吧
    2021-04-04
  • 使用MySQL編程實(shí)現(xiàn)C語(yǔ)言功能強(qiáng)大化步驟示例

    使用MySQL編程實(shí)現(xiàn)C語(yǔ)言功能強(qiáng)大化步驟示例

    這篇文章主要為大家介紹了使用MySQL編程實(shí)現(xiàn)C語(yǔ)言功能強(qiáng)大化步驟示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 聊聊c++數(shù)組名稱和sizeof的問題

    聊聊c++數(shù)組名稱和sizeof的問題

    這篇文章主要介紹了c++數(shù)組名稱和sizeof,介紹了一維數(shù)組名稱的用途及二維數(shù)組數(shù)組名,通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01
  • 詳解Linux的SOCKET編程

    詳解Linux的SOCKET編程

    這篇文章主要介紹了Linux的SOCKET編程,并且進(jìn)行了實(shí)例講解,需要的朋友可以參考下
    2015-08-08
  • C語(yǔ)言程序的編譯與預(yù)處理詳解

    C語(yǔ)言程序的編譯與預(yù)處理詳解

    這篇文章主要介紹了C語(yǔ)言程序的編譯與預(yù)處理,包括介紹了C和C++混合編程的情況,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-10-10
  • opencv實(shí)現(xiàn)角點(diǎn)檢測(cè)

    opencv實(shí)現(xiàn)角點(diǎn)檢測(cè)

    這篇文章主要為大家詳細(xì)介紹了opencv實(shí)現(xiàn)角點(diǎn)檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C語(yǔ)言游戲之猜數(shù)字

    C語(yǔ)言游戲之猜數(shù)字

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言游戲之猜數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • C++實(shí)現(xiàn)LeetCode(40.組合之和之二)

    C++實(shí)現(xiàn)LeetCode(40.組合之和之二)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(40.組合之和之二),本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07

最新評(píng)論

新乡市| 毕节市| 马尔康县| 昌邑市| 紫金县| 江达县| 大城县| 阿尔山市| 繁峙县| 九江市| 改则县| 宁陕县| 怀来县| 双江| 日土县| 泗阳县| 印江| 虹口区| 无极县| 台东市| 尚义县| 万荣县| 南部县| 威宁| 剑川县| 拉萨市| 仙游县| 榆社县| 崇义县| 凤阳县| 拉萨市| 龙胜| 南丹县| 车致| 获嘉县| 泽州县| 柏乡县| 荔波县| 合阳县| 安图县| 长岭县|