Affected files: .obsidian/workspace 03. 專注Study/Android/ADB 取得 APK 的 icon.md 03. 專注Study/Android/ADB.md 03. 專注Study/Android/AOSP.md 03. 專注Study/Android/Android programming.md 03. 專注Study/Android/AudioTrack.md 03. 專注Study/Android/Ktor.md 03. 專注Study/Android/Service.md 03. 專注Study/Android/Tools.md 03. 專注Study/Android/UI.md 03. 專注Study/C++/C++17.md 03. 專注Study/C++/Class template.md 03. 專注Study/C++/GCC.md 03. 專注Study/C++/Structured binding declaration.md 03. 專注Study/C++/for_each.md 03. 專注Study/C++/lambda.md 03. 專注Study/C++/lvalue.md 03. 專注Study/C++/move operator.md 03. 專注Study/C++/rvalue.md 03. 專注Study/C++/智慧指標.md 03. 專注Study/RxKotlin/20200207 - Study RxKotlin.md 04. Programming/COM/20210726 - COM Interface.md 04. Programming/DB/MySQL.md 04. Programming/DB/sqlite.md 04. Programming/Design Pattern.md 04. Programming/FFMPEG/00. Introduction.md 04. Programming/FFMPEG/01. Setup.md 04. Programming/FFMPEG/FFMpeg.md 04. Programming/Flask.md 04. Programming/Kotlin/class.md 04. Programming/Kotlin/run, let, with, also 和 apply.md 04. Programming/Media Foundation/20210604 - Windows media foundation.md 04. Programming/OpenCV.md 04. Programming/OpenGL.md 04. Programming/Python/argparse.ArgumentParser.md 04. Programming/Python/decorator.md 04. Programming/Python/logging.md 04. Programming/Python/opencv.md 04. Programming/Python/subprocess.md 04. Programming/Python/threading.md 04. Programming/Python/tkinter.md 04. Programming/Python/檢測工具.md 04. Programming/QT/Dropdown button.md 04. Programming/QT/QVariant.md 04. Programming/QT/Qt.md 04. Programming/UML.md 04. Programming/演算法.md 05. 資料收集/99. templates/blogHeader.md 05. 資料收集/99. templates/date.md 05. 資料收集/99. templates/front matter.md 05. 資料收集/99. templates/note.md 05. 資料收集/99. templates/table.md 05. 資料收集/99. templates/thisWeek.md 05. 資料收集/99. templates/日記.md 05. 資料收集/99. templates/讀書筆記.md 05. 資料收集/Linux/CLI/cut.md 05. 資料收集/Linux/CLI/scp.md 05. 資料收集/Linux/CLI/timedatectl.md 05. 資料收集/Linux/Programming.md 05. 資料收集/Linux/Ubuntu.md 05. 資料收集/Tool Setup/Hardware/RaspberryPi.md 05. 資料收集/Tool Setup/Software/Chrome.md 05. 資料收集/Tool Setup/Software/Obisidian.md 05. 資料收集/Tool Setup/Software/SublimeText.md 05. 資料收集/Tool Setup/Software/VirtualBox.md 05. 資料收集/Tool Setup/Software/Visual Studio Code.md 05. 資料收集/Tool Setup/Software/Windows Setup.md 05. 資料收集/Tool Setup/Software/Windows Terminal.md 05. 資料收集/Tool Setup/Software/freefilesync.md 05. 資料收集/Tool Setup/Software/vim.md 05. 資料收集/名言佳句.md 05. 資料收集/架站/Gitea.md 05. 資料收集/架站/HTTP Server/Apache.md 05. 資料收集/架站/HTTP Server/Nginx/Reverse Proxy(Layer4).md 05. 資料收集/架站/Pelican blog.md 05. 資料收集/架站/Proxmox VE.md 05. 資料收集/架站/SWAG Reverse proxy.md 05. 資料收集/架站/Storj.md 05. 資料收集/架站/Trojan.md 05. 資料收集/每週外食.md 05. 資料收集/科技/802.11.md 05. 資料收集/科技/HDR Sensor.md 05. 資料收集/科技/量子電腦.md 05. 資料收集/科技/鋰電池.md 05. 資料收集/興趣嗜好/RC/Traxxas Sledge.md 05. 資料收集/興趣嗜好/RC/好盈電變調整中立點.md 05. 資料收集/興趣嗜好/RC/差速器調教教學.md 05. 資料收集/興趣嗜好/模型/舊化作例.md 05. 資料收集/興趣嗜好/軍武/虎式.md 05. 資料收集/讀書筆記/20201201 - 學習如何學習.md 05. 資料收集/讀書筆記/20201218 - Kotlin權威2.0.md 05. 資料收集/讀書筆記/20201224 - 寫作是最好的自我投資.md 05. 資料收集/讀書筆記/20210119 - 中產悲歌.md 05. 資料收集/讀書筆記/20210220 - 最高學習法.md 05. 資料收集/讀書筆記/20210320 - 最高學以致用法.md 05. 資料收集/讀書筆記/20210406 - 精準購買.md 05. 資料收集/讀書筆記/20210723 - 高手學習.md 05. 資料收集/讀書筆記/20220526 - 深入淺出設計模式.md 05. 資料收集/讀書筆記/20220619 - 精確的力量.md 05. 資料收集/軟體工具/IPFS.md 05. 資料收集/軟體工具/MkDocs.md 05. 資料收集/軟體工具/Obsidian.md 05. 資料收集/軟體工具/docker.md 05. 資料收集/軟體工具/git/apply.md 05. 資料收集/軟體工具/git/submodule.md 05. 資料收集/軟體工具/youtube-dl.md 05. 資料收集/面試準備/技术面试最后反问面试官的话.md
10 KiB
10 KiB
Initialization
av_register_all();
avformat_network_init();
Command line
利用FFMpeg來轉檔
ffmpeg -i <SOURCE.mp4> -t 10 -s <W>x<H> -pix_fmt <OUTPUT_FORMAT> <OUTPUT_FILENAME>
-i:來源檔名-t:影片長度(秒)-s:影片大小,長x寬-pix_fmt:輸出的影像格式
example:
ffmpeg -i test.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv
Open file
使用avformat_open_input()來打開檔案。
AVFormatContext* pFormatContext = NULL;
char* path = "E:\\download\\Incredible Patagonia_1920x1080.mp4";
AVDictionary* pDict = NULL;
int result = avformat_open_input(&pFormatContext, path, NULL, &pDict);
若是開啟失敗result會是一個非0的值。可以用av_strerror()來印出詳細的錯誤訊息。例:
char errorMsg[1024] = { 0 };
av_strerror(result, errorMsg, sizeof(errorMsg) - 1);
printf("Open file %s error, code = %d, message = %s\n", path, result, errorMsg);
Get file information
用avformat_find_stream_info()來把檔案的video/audio相關訊息放到AVFormatContext裡面。例:
result = avformat_find_stream_info(pFormatContext, NULL);
也可以用
av_dump_format(pFormatContext, 0, path, 0);
來直接印出相關訊息,不過這方法是Ffmpeg內定的,如果要印出自己想要的內容,就必須將AVFormatContext裡面的AVStream一一撈出來:
for (int i = 0; i < pFormatContext->nb_streams; i++) {
AVStream* avStream = pFormatContext->streams[i];
if (avStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
printf("Stream[%d] is a audio stream.\n", i);
printf(" Sample rate: %d\n", avStream->codecpar->sample_rate);
printf(" Format : %d\n", avStream->codecpar->format);
printf(" Channels : %d\n", avStream->codecpar->channels);
printf(" FPS : %d/%d\n", avStream->avg_frame_rate.num, avStream->avg_frame_rate.den);
printf(" Frame Size : %d\n", avStream->codecpar->frame_size);
printf(" Codec ID : %d\n", avStream->codecpar->codec_id);
} else if (avStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
printf("Stream[%d] is a video stream.\n", i);
printf(" Width : %d\n", avStream->codecpar->width);
printf(" Height: %d\n", avStream->codecpar->height);
printf(" FPS : %d/%d\n", avStream->avg_frame_rate.num, avStream->avg_frame_rate.den);
printf(" Codec ID: %d\n", avStream->codecpar->codec_id);
}
}
這個for-loop可以找出檔案裡面的video stream index與audio stream index。
另一個找出video stream index與audio stream index的方法:
int videoStream = av_find_best_stream(pFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
int audioStream = av_find_best_stream(pFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
printf("Found best video stream index: %d\n", videoStream);
printf("Found best audio stream index: %d\n", audioStream);
Demux
重要結構
AVPackt
重要的member:
AVBufferRef *buf:buffer的reference count,基本上不會用到。int64_t pts:顯示順序int64_t dts:decode順序uint8_t *data:實際存data的bufferint size:上面那個buffer的大小
操作function
AVPacket* av_packet_alloc():產生一個兂的AVPacketAVPacket* av_packet_clone(const AVPacket*):複製一個AVPacketint av_packet_ref(AVPacket*, const AVPacket*):增加reference countav_packet_unref(AVPacket*):減少reference countvoid av_packet_free(AVPacket**):釋放AVPacketvoid av_init_packet(AVPacket*):初始化AVPacketint av_packet_from_data(AVPacket*, uint8_t*, int size):從自己的dataav_seek_frame:AVFormatContext*int stream_index:要seek的streamint64_t timestamp:timestampint flag:seek的方法#define AVSEEK_FLAG_BACKWARD 1:往後找#define AVSEEK_FLAG_BYTE 2:以byte為單位,跳到timestamp相對應的byte#define AVSEEK_FLAG_ANY 4:跳到任意frame,不一定要key frame#define AVSEEK_FLAG_FRAME 8:依照frame number來找
Decode
重要結構
AVCodecContext
重要的member
int thread_count:執行續的數量AVRational time_base
操作function
avcodec_register_all():註冊所有的decoder。AVCodec* avcodec_find_decoder(enum AVCodecID):用codec ID來找到相對應的codec instanceAVCodec* avcodec_find_decoder_by_name(const char* name):用codec的名子來找到相對應的codec instanceAVCodecContext* avcodec_alloc_context3(const AVCodec*):用codec來建立AVCodecContext,codec只是用來解碼,解碼之後的相關資料要放在AVCodecContext裡面。void avcodec_free_context(AVCodecContext**):釋放AVCodecContext。int avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary*)- 開始解碼
AVDictionary的選項請看libavcodec/options_table.h
AVFrame
重要的member
uint8_t* data[AV_NUM_DATA_POINTERS]:存放這個frame的資料int linesize[AV_NUM_DATA_POINTERS]:存放指向各個plane的pointer- Plane mode
- For video
- 0:Y
- 1:U
- 2:V
- 3:NULL
- For audio
- 0:Left channel
- 1:Right channel
- 2:NULL
- For video
- Non plane mode
- For video
- 0:RGB RGB RGB
- 1:NULL
- For video
- Plane mode
int width:For video, frame widthint height:For video, frame heightint nb_samples:For audioint64_t pts:這個frame的ptsint64_t pkt_dts:Packet的dtsint sample_rate:For audio,sampling rateuint64_t channel_layout:For audioint channels:For audioint format:For video it's AVPixelFormat, for audio it's AVSampleFormat
操作function
AVFrame* av_frame_alloc():void av_frame_free(AVFrame*):AVFrame* av_frame_clone(const AVFrame*):int av_frame_ref(AVFrame* dst, const AVFrame* src):void av_frame_unref(AVFrame*):int avcodec_send_packet(AVCodecContext*, const AVPacket*)int avcodec_receive_frame(AVCodecContext*, AVFrame*)
Frame scaling
操作function
sws_getContextsws_getCachedContextSwsContext* sws_getCachedContext( SwsContext*, int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter srcFilter, SwsFilter dstFilter, const double* param)sws_scaleint sws_scale( SwsContext*, const uint8_t* const srcSlice[], // 來源buffer的pointer, 如果是plane mode,要丟srcSlice[3] const int srcStride[], // linesize int srcSliceY, int srcSliceH, // image height uint8_t* const dst[], // 目標buffer的pointer, 如果是plane mode,要丟dst[3] const int dstStride[]) // linesizevoid sws_freeContext(SwsContext*)
Audio resampling
SwrContext* swr_alloc()swr_alloc_set_optsSwrContext* swr_alloc_set_opts( SwrContext*, int64_t out_ch_layout, // Output AVSampleFormat outSampleFormat, // Output int outSampleRate, // Output int64_t in_ch_layout, // Input AVSampleFormat inSampleFormat, // Input int inSampleRate, // Input int log_offset, // 一般丟0 void* log_ctx) // 一般丟0int swr_init(SwrContext*)void swr_free(SwrContext**)swr_convertint swt_convert( SwrContext*, uint8_t** outData, int outCount, // Sample count for one channel uint8_t** inData, int inCount) // Sample count for one channel
Audio 播放
會用到的class
QAudioFormat
setSampleRatesetSampleSizesetChannelCountsetCodec("audio/pcm")setByteOrder(QAudioFormat::LittleEndian)setSampleType(QAudioFormat::UnSignedInt)
QAudioOutput
- Constuctor參數就是一個
QAudioFormat QIODevice* start()suspend()resume()bufferSize()byteFree()periodSize()
QIODevice
qint64 write(const char* data, qint64 len)
QBuffer
QBuffer buffer;
if (buffer.open(QIODevice::ReadWrite)) {
qInfo() << "Buffer Opened!";
QByteArray data("Hello World");
for (int i = 0; i < 5; i++) {
buffer.write(data);
buffer.write("\r\n");
}
//File and device access you may need to flush the data to the device
//buffer.flush()
//Move to the first position
buffer.seek(0);
qInfo() << buffer.readAll(); // retuen a QVariant
qInfo() << "closing the buffer";
//ALWAYS close your device!
buffer.close();
}
Example
QAudioFormat audioFormat;
audioFormat.setSampleRate(44100);
audioFormat.setSampleSize(16);
audioFormat.setChannelCount(2);
audioFormat.setCodec("audio/pcm");
audioFormat.setByteOrder(QAudioFormat::LittleEndian);
audioFormat.setSampleType(QAudioFormat::UnSignedInt);
QAudioOutput* audioOutput = new QAudioOutput(audioFormat);
QIODevice* ioDevice = audioOutput->start();
int size = audioOutput->periodSize();
char* buffer = new char[size];
FILE *pFile = fopen("E:\\download\\out.pcm", "rb");
while (!feof(pFile)) {
if (audioOutput->bytesFree() < size) {
QThread::msleep(1);
}
qint64 len = fread(buffer, 1, size, pFile);
if (len <= 0) {
break;
}
ioDevice->write(buffer, len);
}
fclose(pFile);
delete buffer;
buffer = NULL;