【音视频 | AAC】AAC解码库faad2介绍、使用步骤、例子代码

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你------泰戈尔🍭
⏰发布时间⏰:

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、下载、编译、交叉编译
    • [✨2.1 faad2 下载](#✨2.1 faad2 下载)
    • [✨2.2 faad2 在Ubuntu下编译](#✨2.2 faad2 在Ubuntu下编译)
    • [✨2.3 faad2 交叉编译](#✨2.3 faad2 交叉编译)
  • [🎄三、faad2 解码库使用流程](#🎄三、faad2 解码库使用流程)
  • [🎄四、faad2 解码的例子代码](#🎄四、faad2 解码的例子代码)
    • [4.1 ✨例子 1](#4.1 ✨例子 1)
    • [4.2 ✨例子 2](#4.2 ✨例子 2)
  • 🎄五、总结

🎄一、概述

faad 的全称是 Freeware Advanced Audio (AAC) Decoder,faad2 是一个开源的、免费的 AAC 音频解码器库。它能够将 AAC 编码的音频文件或数据流解码为未压缩的音频数据,如脉冲编码调制(PCM)格式,以便后续的音频播放、编辑等处理。

faad2 支持多种 AAC 音频格式,包括 LC - AAC(Low Complexity AAC)、HE - AAC(High Efficiency AAC)、HE - AAC v2 、MAIN 、 LTP等。这使得它能够处理各种来源的 AAC 音频文件,无论是移动设备上的音频文件,还是在线音频流,都能进行有效的解码。


🎄二、下载、编译、交叉编译

✨2.1 faad2 下载

faad2 的源码在Github托管,下载地址:https://github.com/knik0/faad2/tags。本文下载的是倒数第二个版本:faad2-2.11.1.tar.gz,见下图。如果GitHub访问不了可以点击这个链接下载:https://download.csdn.net/download/wkd_007/90453816


✨2.2 faad2 在Ubuntu下编译

这个小节介绍 faad2 的源码在 Ubuntu 18.04 系统下编译的步骤:

  • 将源码解压缩

    sh 复制代码
    tar zxf faad2-2.11.1.tar.gz
  • 进入源码使用cmake编译,这份源码对cmake的版本是有要求的,如果版本太低,需要先升级cmake的版本,不懂的可以参考这篇文章:Ubuntu 18.04 更新 cmake 到最新版本 3.31.2。下面是cmake 编译 faad2 的配置步骤:

    sh 复制代码
    cd faad2-2.11.1/
    mkdir build_gcc
    cd build_gcc/
    cmake ..  -DCMAKE_C_COMPILER=gcc -DCMAKE_INSTALL_PREFIX::PATH=`pwd`/../../result_gcc -DBUILD_SHARED_LIBS=OFF

    DBUILD_SHARED_LIBS=OFF是为了生成静态库而不是动态库,如果想要动态库,则去掉这个配置。

  • 执行make 、make install

    sh 复制代码
    make
    make install

编译成功后,会在源码目录的上一级目录生成一个result_gcc目录,里面就是我们的编译结果。


✨2.3 faad2 交叉编译

这个小节介绍 faad2 的源码在 Ubuntu 18.04 系统下使用 aarch64-mix210-linux-gcc 交叉编译的步骤:

  • 将源码解压缩

    sh 复制代码
    tar zxf faad2-2.11.1.tar.gz
  • 进入源码使用cmake编译,这份源码对cmake的版本是有要求的,如果版本太低,需要先升级cmake的版本,不懂的可以参考这篇文章:Ubuntu 18.04 更新 cmake 到最新版本 3.31.2。下面是cmake 编译 faad2 的配置步骤:

    sh 复制代码
    cd faad2-2.11.1/
    mkdir build_mix210
    cd build_mix210/
    cmake ..  -DCMAKE_C_COMPILER=aarch64-mix210-linux-gcc -DCMAKE_INSTALL_PREFIX::PATH=`pwd`/../../result_mix210 -DBUILD_SHARED_LIBS=OFF

    DBUILD_SHARED_LIBS=OFF是为了生成静态库而不是动态库,如果想要动态库,则去掉这个配置。

  • 执行make 、make install

    sh 复制代码
    make
    make install

编译成功后,会在源码目录的上一级目录生成一个result_mix210目录,里面就是我们的编译结果。


🎄三、faad2 解码库使用流程

在 faad2 源码里有一个pdf文件,介绍了 faad2 库的使用,文件路径:faad2-2.11.1_gcc\docs\Ahead AAC Decoder library documentation.pdf,文件介绍了faad2常用的的API函数和简单使用流程,感兴趣的可以自己去看,下面是我总结的使用流程:

  • 1、打开 FAAD 解码器句柄

    cpp 复制代码
    NeAACDecHandle decoder = NeAACDecOpen();;
  • 2、配置解码器属性,一般可以省略

    cpp 复制代码
    NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
    // 设置输出格式为 16 位
    conf->outputFormat = FAAD_FMT_16BIT;
    // 启用下混矩阵
    conf->downMatrix = 1;
    // 不使用旧的 ADTS 格式
    conf->useOldADTSFormat = 0;
    // 将配置应用到解码器
    if (!NeAACDecSetConfiguration(decoder, conf)) {
        std::cerr << "Failed to set decoder configuration." << std::endl;
        return false;
    }
  • 3、初始化解码器,必须先读取一帧ADTS帧作为参数传入,不然通道数、位宽会出错

    cpp 复制代码
    int initRet = NeAACDecInit(decoder, aacframe, size, &samplerate, &channels); 
  • 4、解码,解码时传入的是一帧完整的ADTS帧

    cpp 复制代码
    pcm_data = (unsigned char*)NeAACDecDecode(decoder, &frame_info, aacframe, size);  
    if(frame_info.error > 0)  
    {  
    	printf("%s\n",NeAACDecGetErrorMessage(frame_info.error));              
    }  
    else if(pcm_data && frame_info.samples > 0)  
    {
    }
  • 5、关闭 FAAD 解码器句柄

    cpp 复制代码
    NeAACDecClose(decoder);

🎄四、faad2 解码的例子代码

4.1 ✨例子 1

下面是根据流程写的例子,保存后,执行 gcc aacDecoder.c -I result_gcc/include/ result_gcc/lib/libfaad.a -lm 编译。

cpp 复制代码
// gcc aacDecoder.c -I result_gcc/include/ result_gcc/lib/libfaad.a -lm

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "faad.h"  

#define FRAME_MAX_LEN 1024*5   
#define BUFFER_MAX_LEN 1024*1024  


int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){
	int size = 0;

	if(!buffer || !data || !data_size ){
		return -1;
	}

	while(1){
		if(buf_size  < 7 ){
			return -1;
		}
		//Sync words
		if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){
			size |= ((buffer[3] & 0x03) <<11);     //high 2 bit
			size |= buffer[4]<<3;                //middle 8 bit
			size |= ((buffer[5] & 0xe0)>>5);        //low 3bit
			break;
		}
		--buf_size;
		++buffer;
	}

	if(buf_size < size){
		return 1;
	}

	memcpy(data, buffer, size);
	*data_size = size;

	return 0;
}

int main(int argc, char* argv[])
{
	int data_size = 0;
	int size = 0;
	int cnt=0;
	int offset=0;
	
	unsigned long samplerate=0;  
    unsigned char channels=0;  
    NeAACDecHandle decoder = 0;
	NeAACDecFrameInfo frame_info;  
	unsigned char* pcm_data = NULL; 
	
	// 判断输入参数 2025-03-03 15:04:01
	if(argc < 3)  
    {  
        printf("usage\nfaaddec src_file dst_file\n");  
        return -1;  
    }
	
	// 获取输入、输出文件 2025-03-03 15:04:22
	char src_file[128] = {0};  
    char dst_file[128] = {0}; 
	sscanf(argv[1], "%s", src_file);  
    sscanf(argv[2], "%s", dst_file);
	FILE *ifile = fopen(src_file, "rb");
	FILE *ofile = fopen(dst_file, "wb");  
    if(!ifile || !ofile)  
    {  
        printf("source or destination file");  
        return -1;  
    }

	//FILE *myout=fopen("output_log.txt","wb+");
	FILE *myout=stdout;

	unsigned char *aacframe=(unsigned char *)malloc(FRAME_MAX_LEN);
	unsigned char *aacbuffer=(unsigned char *)malloc(BUFFER_MAX_LEN);

	
	// 1、打开AAC解码器
    decoder = NeAACDecOpen();  
	
	// 2、配置解码器属性,一般可以省略 2025-03-03 15:16:23
	
	
	printf("-----+- ADTS Frame Table -+------+\n");
	printf(" NUM | Profile | Frequency| Size |\n");
	printf("-----+---------+----------+------+\n");

	while(!feof(ifile)){
		data_size = fread(aacbuffer+offset, 1, BUFFER_MAX_LEN-offset, ifile);
		unsigned char* input_data = aacbuffer;

		while(1)
		{
			int ret=getADTSframe(input_data, data_size, aacframe, &size);
			if(ret==-1){
				break;
			}else if(ret==1){
				memcpy(aacbuffer,input_data,data_size);
				offset=data_size;
				break;
			}

			char profile_str[10]={0};
			char frequence_str[10]={0};

			unsigned char profile=aacframe[2]&0xC0;
			profile=profile>>6;
			switch(profile){
			case 0: sprintf(profile_str,"Main");break;
			case 1: sprintf(profile_str,"LC");break;
			case 2: sprintf(profile_str,"SSR");break;
			default:sprintf(profile_str,"unknown");break;
			}

			unsigned char sampling_frequency_index=aacframe[2]&0x3C;
			sampling_frequency_index=sampling_frequency_index>>2;
			switch(sampling_frequency_index){
			case 0: sprintf(frequence_str,"96000Hz");break;
			case 1: sprintf(frequence_str,"88200Hz");break;
			case 2: sprintf(frequence_str,"64000Hz");break;
			case 3: sprintf(frequence_str,"48000Hz");break;
			case 4: sprintf(frequence_str,"44100Hz");break;
			case 5: sprintf(frequence_str,"32000Hz");break;
			case 6: sprintf(frequence_str,"24000Hz");break;
			case 7: sprintf(frequence_str,"22050Hz");break;
			case 8: sprintf(frequence_str,"16000Hz");break;
			case 9: sprintf(frequence_str,"12000Hz");break;
			case 10: sprintf(frequence_str,"11025Hz");break;
			case 11: sprintf(frequence_str,"8000Hz");break;
			default:sprintf(frequence_str,"unknown");break;
			}

			fprintf(myout,"%5d| %8s|  %8s| %5d|",cnt,profile_str ,frequence_str,size);
			
			// 3、初始化AAC解码器
			if(cnt == 0)	// 第一个ADTS帧用来初始化解码器 2025-03-03 15:40:53
			{
				int initRet = NeAACDecInit(decoder, aacframe, size, &samplerate, &channels); 
				printf("samplerate %ld, channels %d, initRet=%d\n", samplerate, channels, initRet);  
			}
			
			// 4、解码AAC的 ADTS 帧
			pcm_data = (unsigned char*)NeAACDecDecode(decoder, &frame_info, aacframe, size);  
			if(frame_info.error > 0)  
			{  
				printf("%s\n",NeAACDecGetErrorMessage(frame_info.error));              
			}  
			else if(pcm_data && frame_info.samples > 0)  
			{
			#if 1
				printf("frame info: bytesconsumed %ld, channels %d, header_type %d object_type %d, samples %ld, samplerate %ld\n",   
					frame_info.bytesconsumed,   
					frame_info.channels, frame_info.header_type,   
					frame_info.object_type, frame_info.samples,   
					frame_info.samplerate);  
			#endif
				fwrite(pcm_data, 1, frame_info.samples * frame_info.channels, ofile);      //2个通道  
				fflush(ofile);  
			}  
			
			
			data_size -= size;
			input_data += size;
			cnt++;
		}   

	}
	NeAACDecClose(decoder);	// 5、关闭解码器
	fclose(ifile);
	fclose(ofile);
	free(aacbuffer);
	free(aacframe);

	return 0;
}

4.2 ✨例子 2

下面是让 AI 写的代码:使用 C++ 和 FAAD 库将使用 ADTS 帧的 AAC 编码文件分别解码为 WAV 编码文件和 PCM 文件的示例代码,将调用 FAAD 的部分封装成了一个类,并且添加了详细的注释。

cpp 复制代码
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <faad.h>

// 定义 AAC 解码器类
class AACDecoder {
private:
    NeAACDecHandle decoder;  // FAAD 解码器句柄
    unsigned long samplerate; // 音频采样率
    unsigned char channels;   // 音频通道数

    // 从文件中读取一个 ADTS 帧
    bool readADTSFrame(std::ifstream& inputFile, unsigned char* buffer, size_t bufferSize, size_t& frameSize) {
        if (inputFile.eof()) return false;

        // 查找 ADTS 帧头
        while (inputFile.read(reinterpret_cast<char*>(buffer), 2)) {
            if ((buffer[0] == 0xFF) && ((buffer[1] & 0xF0) == 0xF0)) {
                // 找到帧头,继续读取剩余的帧头部分
                inputFile.read(reinterpret_cast<char*>(buffer + 2), 5);
                // 从帧头中提取帧大小
                frameSize = ((buffer[3] & 0x03) << 11) | (buffer[4] << 3) | ((buffer[5] & 0xE0) >> 5);
                if (frameSize > bufferSize) {
                    std::cerr << "ADTS frame size exceeds buffer size." << std::endl;
                    return false;
                }
                // 读取剩余的帧数据
                inputFile.read(reinterpret_cast<char*>(buffer + 7), frameSize - 7);
                return true;
            }
        }
        return false;
    }

public:
    // 构造函数,初始化解码器
    AACDecoder() : decoder(nullptr), samplerate(0), channels(0) {
        decoder = NeAACDecOpen();
        if (decoder == nullptr) {
            std::cerr << "Failed to open FAAD decoder." << std::endl;
        }
    }

    // 析构函数,关闭解码器
    ~AACDecoder() {
        if (decoder != nullptr) {
            NeAACDecClose(decoder);
        }
    }

    // 配置解码器
    bool configureDecoder() {
        // 获取当前解码器配置
        NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
        if (conf == nullptr) {
            std::cerr << "Failed to get decoder configuration." << std::endl;
            return false;
        }

        // 设置输出格式为 16 位
        conf->outputFormat = FAAD_FMT_16BIT;
        // 启用下混矩阵
        conf->downMatrix = 1;
        // 不使用旧的 ADTS 格式
        conf->useOldADTSFormat = 0;

        // 将配置应用到解码器
        if (!NeAACDecSetConfiguration(decoder, conf)) {
            std::cerr << "Failed to set decoder configuration." << std::endl;
            return false;
        }

        return true;
    }

    // 解码 AAC 文件并保存为 WAV 文件
    bool decodeAACToWAV(const std::string& inputFileName, const std::string& outputFileName) {
        // 打开输入的 AAC 文件
        std::ifstream inputFile(inputFileName, std::ios::binary);
        if (!inputFile) {
            std::cerr << "Failed to open input file." << std::endl;
            return false;
        }

        // 配置解码器
        if (!configureDecoder()) {
            inputFile.close();
            return false;
        }

        // 打开输出的 WAV 文件
        std::ofstream outputFile(outputFileName, std::ios::binary);
        if (!outputFile) {
            std::cerr << "Failed to open output file." << std::endl;
            inputFile.close();
            return false;
        }

        unsigned char buffer[8192];
        size_t frameSize;
        bool isFirstFrame = true;
        std::streamsize totalDecodedSamples = 0;

        // 读取第一个 ADTS 帧用于初始化解码器
        if (!readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
            std::cerr << "Failed to read the first ADTS frame for initialization." << std::endl;
            inputFile.close();
            outputFile.close();
            return false;
        }

        // 初始化解码器
        if (NeAACDecInit(decoder, buffer, static_cast<unsigned long>(frameSize), &samplerate, &channels) < 0) {
            std::cerr << "Failed to initialize FAAD decoder." << std::endl;
            inputFile.close();
            outputFile.close();
            return false;
        }

        // 写入 WAV 文件头
        writeWAVHeader(outputFile, samplerate, channels);

        // 处理第一个帧的解码
        NeAACDecFrameInfo frameInfo;
        void* decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
        if (frameInfo.error > 0) {
            std::cerr << "Decoding error in the first frame: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
        } else if (frameInfo.samples > 0) {
            outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
            totalDecodedSamples += frameInfo.samples;
        }

        // 循环读取并解码剩余的 ADTS 帧
        while (readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
            decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));

            if (frameInfo.error > 0) {
                // 输出解码错误信息
                std::cerr << "Decoding error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
                continue;
            }

            if (frameInfo.samples > 0) {
                // 将解码后的数据写入 WAV 文件
                outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
                totalDecodedSamples += frameInfo.samples;
            }
        }

        // 更新 WAV 文件头中的文件大小和数据大小
        updateWAVHeader(outputFile, totalDecodedSamples, samplerate, channels);

        // 关闭输入和输出文件
        inputFile.close();
        outputFile.close();

        return true;
    }

    // 解码 AAC 文件并保存为 PCM 文件
    bool decodeAACToPCM(const std::string& inputFileName, const std::string& outputFileName) {
        // 打开输入的 AAC 文件
        std::ifstream inputFile(inputFileName, std::ios::binary);
        if (!inputFile) {
            std::cerr << "Failed to open input file." << std::endl;
            return false;
        }

        // 配置解码器
        if (!configureDecoder()) {
            inputFile.close();
            return false;
        }

        // 打开输出的 PCM 文件
        std::ofstream outputFile(outputFileName, std::ios::binary);
        if (!outputFile) {
            std::cerr << "Failed to open output file." << std::endl;
            inputFile.close();
            return false;
        }

        unsigned char buffer[8192];
        size_t frameSize;

        // 读取第一个 ADTS 帧用于初始化解码器
        if (!readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
            std::cerr << "Failed to read the first ADTS frame for initialization." << std::endl;
            inputFile.close();
            outputFile.close();
            return false;
        }

        // 初始化解码器
        if (NeAACDecInit(decoder, buffer, static_cast<unsigned long>(frameSize), &samplerate, &channels) < 0) {
            std::cerr << "Failed to initialize FAAD decoder." << std::endl;
            inputFile.close();
            outputFile.close();
            return false;
        }

        // 处理第一个帧的解码
        NeAACDecFrameInfo frameInfo;
        void* decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
        if (frameInfo.error > 0) {
            std::cerr << "Decoding error in the first frame: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
        } else if (frameInfo.samples > 0) {
            outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
        }

        // 循环读取并解码剩余的 ADTS 帧
        while (readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
            decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));

            if (frameInfo.error > 0) {
                // 输出解码错误信息
                std::cerr << "Decoding error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
                continue;
            }

            if (frameInfo.samples > 0) {
                // 将解码后的数据写入 PCM 文件
                outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
            }
        }

        // 关闭输入和输出文件
        inputFile.close();
        outputFile.close();

        return true;
    }

    // 写入 WAV 文件头
    void writeWAVHeader(std::ofstream& outputFile, unsigned long samplerate, unsigned char channels) {
        // 采样位数
        const int bitsPerSample = 16;
        // 块对齐
        const int blockAlign = channels * (bitsPerSample / 8);
        // 每秒数据字节数
        const int byteRate = samplerate * blockAlign;

        // 写入 RIFF 头
        outputFile.write("RIFF", 4);
        // 这里先占位,后续再更新文件大小
        unsigned int fileSizePlaceholder = 0;
        outputFile.write(reinterpret_cast<const char*>(&fileSizePlaceholder), 4);
        outputFile.write("WAVE", 4);

        // 写入 fmt 块
        outputFile.write("fmt ", 4);
        unsigned int fmtChunkSize = 16;
        outputFile.write(reinterpret_cast<const char*>(&fmtChunkSize), 4);
        unsigned short audioFormat = 1; // PCM 格式
        outputFile.write(reinterpret_cast<const char*>(&audioFormat), 2);
        outputFile.write(reinterpret_cast<const char*>(&channels), 2);
        outputFile.write(reinterpret_cast<const char*>(&samplerate), 4);
        outputFile.write(reinterpret_cast<const char*>(&byteRate), 4);
        outputFile.write(reinterpret_cast<const char*>(&blockAlign), 2);
        outputFile.write(reinterpret_cast<const char*>(&bitsPerSample), 2);

        // 写入 data 块
        outputFile.write("data", 4);
        // 这里先占位,后续再更新数据大小
        unsigned int dataSizePlaceholder = 0;
        outputFile.write(reinterpret_cast<const char*>(&dataSizePlaceholder), 4);
    }

    // 更新 WAV 文件头中的文件大小和数据大小
    void updateWAVHeader(std::ofstream& outputFile, std::streamsize totalDecodedSamples, unsigned long samplerate, unsigned char channels) {
        const int bitsPerSample = 16;
        const int blockAlign = channels * (bitsPerSample / 8);
        unsigned int dataSize = static_cast<unsigned int>(totalDecodedSamples * sizeof(short));
        unsigned int fileSize = dataSize + 36;

        // 回到文件开头更新文件大小
        outputFile.seekp(4, std::ios::beg);
        outputFile.write(reinterpret_cast<const char*>(&fileSize), 4);

        // 回到 data 块更新数据大小
        outputFile.seekp(40, std::ios::beg);
        outputFile.write(reinterpret_cast<const char*>(&dataSize), 4);
    }
};

int main() {
    AACDecoder decoder;
    std::string inputFileName = "input.aac";
    std::string wavOutputFileName = "output.wav";
    std::string pcmOutputFileName = "output.pcm";

    if (decoder.decodeAACToWAV(inputFileName, wavOutputFileName)) {
        std::cout << "Decoded AAC to WAV successfully." << std::endl;
    } else {
        std::cout << "Failed to decode AAC to WAV." << std::endl;
    }

    if (decoder.decodeAACToPCM(inputFileName, pcmOutputFileName)) {
        std::cout << "Decoded AAC to PCM successfully." << std::endl;
    } else {
        std::cout << "Failed to decode AAC to PCM." << std::endl;
    }

    return 0;
}

🎄五、总结

本文介绍了AAC音频解码库 faad2 ,并介绍了其使用流程,最后给出了使用例子源码

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考:

https://blog.csdn.net/qq_43603125/article/details/138583930

https://blog.csdn.net/weixin_45266730/article/details/140501073

相关推荐
EasyCVR7 分钟前
安防监控/视频集中存储EasyCVR视频汇聚平台如何配置AI智能分析平台的接入?
人工智能·音视频·webrtc·rtsp·gb28181
weixin_519311749 分钟前
3.多线程获取音频AI的PCM数据
人工智能·音视频·pcm
轶软工作室6 小时前
全自动数据强制备份程序,无视占用直接硬复制各种数据文件、文档、音视频、软件、数据库等的VSS卷拷贝批处理脚本程序,解放双手,一劳永逸
数据库·音视频
摸鱼 特供版6 小时前
一键无损放大视频,让老旧画面重焕新生!
windows·学习·音视频·软件需求
敢嗣先锋6 小时前
鸿蒙5.0实战案例:基于OpenGL渲染视频画面帧
移动开发·音视频·harmonyos·arkts·opengl·arkui·鸿蒙开发
gma9996 小时前
【音视频】RTP封包H265信息
音视频
tinghai_2168 小时前
vivo手机怎么剪辑视频?从零开始的视频编辑之旅
智能手机·音视频
正在走向自律19 小时前
通义万相2.1:开启视频生成新时代
人工智能·文生图·音视频·ai绘画·文生视频·ai视频·通义万相 2.1
Antonio91519 小时前
【音视频】FFmpeg如何查询命令帮助文档
ffmpeg·音视频